From d1827562bbe8d06a0975782f177b236be63ba1a9 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Tue, 9 Jun 2020 08:22:31 +0200 Subject: [PATCH 01/12] create server-side skeleton --- .../global_search_providers/kibana.json | 10 +++++ .../global_search_providers/server/index.ts | 22 ++++++++++ .../global_search_providers/server/plugin.ts | 40 +++++++++++++++++++ .../server/providers/index.ts | 7 ++++ .../server/providers/saved_objects.ts | 30 ++++++++++++++ .../global_search_providers/server/types.ts | 11 +++++ 6 files changed, 120 insertions(+) create mode 100644 x-pack/plugins/global_search_providers/kibana.json create mode 100644 x-pack/plugins/global_search_providers/server/index.ts create mode 100644 x-pack/plugins/global_search_providers/server/plugin.ts create mode 100644 x-pack/plugins/global_search_providers/server/providers/index.ts create mode 100644 x-pack/plugins/global_search_providers/server/providers/saved_objects.ts create mode 100644 x-pack/plugins/global_search_providers/server/types.ts diff --git a/x-pack/plugins/global_search_providers/kibana.json b/x-pack/plugins/global_search_providers/kibana.json new file mode 100644 index 00000000000000..20a2724c86bd39 --- /dev/null +++ b/x-pack/plugins/global_search_providers/kibana.json @@ -0,0 +1,10 @@ +{ + "id": "globalSearchProviders", + "version": "8.0.0", + "kibanaVersion": "kibana", + "server": true, + "ui": false, + "requiredPlugins": ["globalSearch"], + "optionalPlugins": [], + "configPath": ["xpack", "global_search_providers"] +} diff --git a/x-pack/plugins/global_search_providers/server/index.ts b/x-pack/plugins/global_search_providers/server/index.ts new file mode 100644 index 00000000000000..44115a230231db --- /dev/null +++ b/x-pack/plugins/global_search_providers/server/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PluginInitializer } from 'src/core/server'; +import { + GlobalSearchProvidersPlugin, + GlobalSearchProvidersPluginSetupDeps, + GlobalSearchProvidersPluginStartDeps, +} from './plugin'; +import { GlobalSearchProvidersPluginSetup, GlobalSearchProvidersPluginStart } from './types'; + +export const plugin: PluginInitializer< + GlobalSearchProvidersPluginSetup, + GlobalSearchProvidersPluginStart, + GlobalSearchProvidersPluginSetupDeps, + GlobalSearchProvidersPluginStartDeps +> = (context) => new GlobalSearchProvidersPlugin(); + +export { GlobalSearchProvidersPluginSetup, GlobalSearchProvidersPluginStart }; diff --git a/x-pack/plugins/global_search_providers/server/plugin.ts b/x-pack/plugins/global_search_providers/server/plugin.ts new file mode 100644 index 00000000000000..f37824e3a2d8a3 --- /dev/null +++ b/x-pack/plugins/global_search_providers/server/plugin.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CoreSetup, Plugin } from 'src/core/server'; +import { GlobalSearchPluginSetup } from '../../global_search/server'; +import { GlobalSearchProvidersPluginSetup, GlobalSearchProvidersPluginStart } from './types'; +import { createSavedObjectsResultProvider } from './providers'; + +export interface GlobalSearchProvidersPluginSetupDeps { + globalSearch: GlobalSearchPluginSetup; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface GlobalSearchProvidersPluginStartDeps {} + +export class GlobalSearchProvidersPlugin + implements + Plugin< + GlobalSearchProvidersPluginSetup, + GlobalSearchProvidersPluginStart, + GlobalSearchProvidersPluginSetupDeps, + GlobalSearchProvidersPluginStartDeps + > { + setup( + { + getStartServices, + }: CoreSetup, + { globalSearch }: GlobalSearchProvidersPluginSetupDeps + ): GlobalSearchProvidersPluginSetup { + globalSearch.registerResultProvider(createSavedObjectsResultProvider()); + return {}; + } + + start(): GlobalSearchProvidersPluginStart { + return {}; + } +} diff --git a/x-pack/plugins/global_search_providers/server/providers/index.ts b/x-pack/plugins/global_search_providers/server/providers/index.ts new file mode 100644 index 00000000000000..1670871f305d96 --- /dev/null +++ b/x-pack/plugins/global_search_providers/server/providers/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { createSavedObjectsResultProvider } from './saved_objects'; diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects.ts new file mode 100644 index 00000000000000..e8879abe325575 --- /dev/null +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { from, of } from 'rxjs'; +import { take, map, takeUntil, mergeMap } from 'rxjs/operators'; +import { ApplicationStart } from 'src/core/public'; +import { GlobalSearchResultProvider } from '../../../global_search/server'; + +export const createSavedObjectsResultProvider = (): GlobalSearchResultProvider => { + return { + id: 'application', + find: (term, { aborted$, maxResults }, { core }) => { + return of(); + /* + return from(applicationPromise).pipe( + mergeMap((application) => application.applications$), + takeUntil(aborted$), + take(1), + map((apps) => { + const results = getAppResults(term, [...apps.values()]); + return results.sort((a, b) => b.score - a.score).slice(0, maxResults); + }) + ); + */ + }, + }; +}; diff --git a/x-pack/plugins/global_search_providers/server/types.ts b/x-pack/plugins/global_search_providers/server/types.ts new file mode 100644 index 00000000000000..f3aac002606f5a --- /dev/null +++ b/x-pack/plugins/global_search_providers/server/types.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface GlobalSearchProvidersPluginSetup {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface GlobalSearchProvidersPluginStart {} From ec51f7f95d2fd6fb2695a94ce01df1dc548b2c33 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Tue, 9 Jun 2020 13:50:25 +0200 Subject: [PATCH 02/12] add base implementation & tests --- x-pack/plugins/global_search/server/mocks.ts | 8 +- .../server/services/context.mock.ts | 39 +++++ .../server/plugin.test.ts | 33 ++++ .../server/providers/saved_objects.ts | 30 ---- .../server/providers/saved_objects/index.ts | 7 + .../map_object_to_result.test.ts | 158 ++++++++++++++++++ .../saved_objects/map_object_to_result.ts | 30 ++++ .../providers/saved_objects/provider.ts | 45 +++++ 8 files changed, 317 insertions(+), 33 deletions(-) create mode 100644 x-pack/plugins/global_search/server/services/context.mock.ts create mode 100644 x-pack/plugins/global_search_providers/server/plugin.test.ts delete mode 100644 x-pack/plugins/global_search_providers/server/providers/saved_objects.ts create mode 100644 x-pack/plugins/global_search_providers/server/providers/saved_objects/index.ts create mode 100644 x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts create mode 100644 x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts create mode 100644 x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts diff --git a/x-pack/plugins/global_search/server/mocks.ts b/x-pack/plugins/global_search/server/mocks.ts index 8a189a57017088..e7c133edf95c84 100644 --- a/x-pack/plugins/global_search/server/mocks.ts +++ b/x-pack/plugins/global_search/server/mocks.ts @@ -11,6 +11,7 @@ import { RouteHandlerGlobalSearchContext, } from './types'; import { searchServiceMock } from './services/search_service.mock'; +import { contextMock } from './services/context.mock'; const createSetupMock = (): jest.Mocked => { const searchMock = searchServiceMock.createSetupContract(); @@ -29,17 +30,18 @@ const createStartMock = (): jest.Mocked => { }; const createRouteHandlerContextMock = (): jest.Mocked => { - const contextMock = { + const handlerContextMock = { find: jest.fn(), }; - contextMock.find.mockReturnValue(of([])); + handlerContextMock.find.mockReturnValue(of([])); - return contextMock; + return handlerContextMock; }; export const globalSearchPluginMock = { createSetupContract: createSetupMock, createStartContract: createStartMock, createRouteHandlerContext: createRouteHandlerContextMock, + createProviderContext: contextMock.create, }; diff --git a/x-pack/plugins/global_search/server/services/context.mock.ts b/x-pack/plugins/global_search/server/services/context.mock.ts new file mode 100644 index 00000000000000..0fe379008f41e4 --- /dev/null +++ b/x-pack/plugins/global_search/server/services/context.mock.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + savedObjectsTypeRegistryMock, + savedObjectsClientMock, + elasticsearchServiceMock, + uiSettingsServiceMock, +} from '../../../../../src/core/server/mocks'; +import { GlobalSearchProviderContext } from '../types'; + +const createContextMock = (): jest.Mocked => { + return { + core: { + savedObjects: { + client: savedObjectsClientMock.create(), + typeRegistry: savedObjectsTypeRegistryMock.create(), + }, + elasticsearch: { + legacy: { + client: elasticsearchServiceMock.createScopedClusterClient(), + }, + }, + uiSettings: { + client: uiSettingsServiceMock.createClient(), + }, + }, + }; +}; + +const createFactoryMock = () => () => () => createContextMock(); + +export const contextMock = { + create: createContextMock, + createFactory: createFactoryMock, +}; diff --git a/x-pack/plugins/global_search_providers/server/plugin.test.ts b/x-pack/plugins/global_search_providers/server/plugin.test.ts new file mode 100644 index 00000000000000..c9b51619d17899 --- /dev/null +++ b/x-pack/plugins/global_search_providers/server/plugin.test.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { coreMock } from '../../../../src/core/server/mocks'; +import { globalSearchPluginMock } from '../../global_search/server/mocks'; +import { GlobalSearchProvidersPlugin } from './plugin'; + +describe('GlobalSearchProvidersPlugin', () => { + let plugin: GlobalSearchProvidersPlugin; + let globalSearchSetup: ReturnType; + + beforeEach(() => { + plugin = new GlobalSearchProvidersPlugin(); + globalSearchSetup = globalSearchPluginMock.createSetupContract(); + }); + + describe('#setup', () => { + it('registers the `savedObjects` result provider', () => { + const coreSetup = coreMock.createSetup(); + plugin.setup(coreSetup, { globalSearch: globalSearchSetup }); + + expect(globalSearchSetup.registerResultProvider).toHaveBeenCalledTimes(1); + expect(globalSearchSetup.registerResultProvider).toHaveBeenCalledWith( + expect.objectContaining({ + id: 'savedObjects', + }) + ); + }); + }); +}); diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects.ts deleted file mode 100644 index e8879abe325575..00000000000000 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { from, of } from 'rxjs'; -import { take, map, takeUntil, mergeMap } from 'rxjs/operators'; -import { ApplicationStart } from 'src/core/public'; -import { GlobalSearchResultProvider } from '../../../global_search/server'; - -export const createSavedObjectsResultProvider = (): GlobalSearchResultProvider => { - return { - id: 'application', - find: (term, { aborted$, maxResults }, { core }) => { - return of(); - /* - return from(applicationPromise).pipe( - mergeMap((application) => application.applications$), - takeUntil(aborted$), - take(1), - map((apps) => { - const results = getAppResults(term, [...apps.values()]); - return results.sort((a, b) => b.score - a.score).slice(0, maxResults); - }) - ); - */ - }, - }; -}; diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/index.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/index.ts new file mode 100644 index 00000000000000..4a67fd8b3df180 --- /dev/null +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { createSavedObjectsResultProvider } from './provider'; diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts new file mode 100644 index 00000000000000..255150b805be03 --- /dev/null +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts @@ -0,0 +1,158 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObject, SavedObjectsType, SavedObjectTypeRegistry } from 'src/core/server'; +import { mapToResult, mapToResults } from './map_object_to_result'; + +const createType = (props: Partial): SavedObjectsType => { + return { + name: 'type', + hidden: false, + namespaceType: 'single', + mappings: { properties: {} }, + ...props, + management: { + defaultSearchField: 'field', + getInAppUrl: (obj) => ({ path: `/object/${obj.id}`, uiCapabilitiesPath: '' }), + ...props.management, + }, + }; +}; + +const createObject = (props: Partial, attributes: T): SavedObject => { + return { + id: 'id', + type: 'dashboard', + references: [], + ...props, + attributes, + }; +}; + +describe('mapToResult', () => { + it('converts a savedObject to a result', () => { + const type = createType({ + name: 'dashboard', + management: { + defaultSearchField: 'title', + getInAppUrl: (obj) => ({ path: `/dashboard/${obj.id}`, uiCapabilitiesPath: '' }), + }, + }); + + const obj = createObject( + { + id: 'dash1', + type: 'dashboard', + }, + { + title: 'My dashboard', + } + ); + + expect(mapToResult(obj, type)).toEqual({ + id: 'dash1', + title: 'My dashboard', + type: 'dashboard', + url: '/dashboard/dash1', + score: 100, + }); + }); +}); + +describe('mapToResults', () => { + let typeRegistry: SavedObjectTypeRegistry; + + beforeEach(() => { + typeRegistry = new SavedObjectTypeRegistry(); + }); + + it('converts savedObjects to results', () => { + typeRegistry.registerType( + createType({ + name: 'typeA', + management: { + defaultSearchField: 'title', + getInAppUrl: (obj) => ({ path: `/type-a/${obj.id}`, uiCapabilitiesPath: '' }), + }, + }) + ); + typeRegistry.registerType( + createType({ + name: 'typeB', + management: { + defaultSearchField: 'description', + getInAppUrl: (obj) => ({ path: `/type-b/${obj.id}`, uiCapabilitiesPath: 'foo' }), + }, + }) + ); + typeRegistry.registerType( + createType({ + name: 'typeC', + management: { + defaultSearchField: 'excerpt', + getInAppUrl: (obj) => ({ path: `/type-c/${obj.id}`, uiCapabilitiesPath: 'bar' }), + }, + }) + ); + + const results = [ + createObject( + { + id: 'resultA', + type: 'typeA', + }, + { + title: 'titleA', + field: 'noise', + } + ), + createObject( + { + id: 'resultC', + type: 'typeC', + }, + { + excerpt: 'titleC', + title: 'foo', + } + ), + createObject( + { + id: 'resultB', + type: 'typeB', + }, + { + description: 'titleB', + bar: 'baz', + } + ), + ]; + + expect(mapToResults(results, typeRegistry)).toEqual([ + { + id: 'resultA', + title: 'titleA', + type: 'typeA', + url: '/type-a/resultA', + score: 100, + }, + { + id: 'resultC', + title: 'titleC', + type: 'typeC', + url: '/type-c/resultC', + score: 100, + }, + { + id: 'resultB', + title: 'titleB', + type: 'typeB', + url: '/type-b/resultB', + score: 100, + }, + ]); + }); +}); diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts new file mode 100644 index 00000000000000..0c64a991ff2249 --- /dev/null +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObject, SavedObjectsType, ISavedObjectTypeRegistry } from 'src/core/server'; +import { GlobalSearchProviderResult } from '../../../../global_search/server'; + +export const mapToResults = ( + objects: Array>, + registry: ISavedObjectTypeRegistry +): GlobalSearchProviderResult[] => { + return objects.map((obj) => mapToResult(obj, registry.getType(obj.type)!)); +}; + +export const mapToResult = ( + object: SavedObject, + type: SavedObjectsType +): GlobalSearchProviderResult => { + const { defaultSearchField, getInAppUrl } = type.management!; + + return { + id: object.id, + title: (object.attributes as any)[defaultSearchField!], + type: object.type, + url: getInAppUrl!(object).path, + score: 100, + }; +}; diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts new file mode 100644 index 00000000000000..2dca2a9cc59059 --- /dev/null +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { from } from 'rxjs'; +import { map, takeUntil } from 'rxjs/operators'; +import { GlobalSearchResultProvider } from '../../../../global_search/server'; +import { mapToResults } from './map_object_to_result'; + +export const createSavedObjectsResultProvider = (): GlobalSearchResultProvider => { + return { + id: 'savedObjects', + find: (term, { aborted$, maxResults }, { core }) => { + const { typeRegistry, client } = core.savedObjects; + + const searchableTypes = typeRegistry + .getAllTypes() + .filter((type) => !typeRegistry.isHidden(type.name)) + .filter((type) => type.management?.defaultSearchField && type.management?.getInAppUrl); + const typeToField = searchableTypes.reduce((memo, type) => { + return { + ...memo, + [type.name]: type.management!.defaultSearchField!, + }; + }, {} as Record); + const searchFields = [...new Set(Object.values(typeToField))]; + + // TODO: add preference once https://github.com/elastic/kibana/pull/68620 lands + const responsePromise = client.find({ + page: 1, + perPage: maxResults, + search: term, + searchFields, + type: Object.keys(typeToField), + }); + + return from(responsePromise).pipe( + takeUntil(aborted$), + map((res) => mapToResults(res.saved_objects, typeRegistry)) + ); + }, + }; +}; From 9417744df5157284fcd2dba4fa8367c6154c616f Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Tue, 9 Jun 2020 15:09:19 +0200 Subject: [PATCH 03/12] add unit test for provider --- .../server/services/context.mock.ts | 3 +- .../providers/saved_objects/provider.test.ts | 159 ++++++++++++++++++ .../providers/saved_objects/provider.ts | 12 +- 3 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts diff --git a/x-pack/plugins/global_search/server/services/context.mock.ts b/x-pack/plugins/global_search/server/services/context.mock.ts index 0fe379008f41e4..50c6da109f8dde 100644 --- a/x-pack/plugins/global_search/server/services/context.mock.ts +++ b/x-pack/plugins/global_search/server/services/context.mock.ts @@ -10,9 +10,8 @@ import { elasticsearchServiceMock, uiSettingsServiceMock, } from '../../../../../src/core/server/mocks'; -import { GlobalSearchProviderContext } from '../types'; -const createContextMock = (): jest.Mocked => { +const createContextMock = () => { return { core: { savedObjects: { diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts new file mode 100644 index 00000000000000..34663d0bbac352 --- /dev/null +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts @@ -0,0 +1,159 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EMPTY } from 'rxjs'; +import { TestScheduler } from 'rxjs/testing'; +import { + SavedObjectsFindResponse, + SavedObject, + SavedObjectsType, + SavedObjectTypeRegistry, +} from 'src/core/server'; +import { globalSearchPluginMock } from '../../../../global_search/server/mocks'; +import { + GlobalSearchResultProvider, + GlobalSearchProviderFindOptions, +} from '../../../../global_search/server'; +import { createSavedObjectsResultProvider } from './provider'; + +const getTestScheduler = () => + new TestScheduler((actual, expected) => { + expect(actual).toEqual(expected); + }); + +const createFindResponse = (results: SavedObject[]): SavedObjectsFindResponse => ({ + saved_objects: results, + page: 1, + per_page: 20, + total: results.length, +}); + +const createType = (props: Partial): SavedObjectsType => { + return { + name: 'type', + hidden: false, + namespaceType: 'single', + mappings: { properties: {} }, + ...props, + management: { + defaultSearchField: 'field', + getInAppUrl: (obj) => ({ path: `/object/${obj.id}`, uiCapabilitiesPath: '' }), + ...props.management, + }, + }; +}; + +const createObject = (props: Partial, attributes: T): SavedObject => { + return { + id: 'id', + type: 'dashboard', + references: [], + ...props, + attributes, + }; +}; + +const defaultOption: GlobalSearchProviderFindOptions = { + preference: 'pref', + maxResults: 20, + aborted$: EMPTY, +}; + +describe('savedObjectsResultProvider', () => { + let provider: GlobalSearchResultProvider; + let registry: SavedObjectTypeRegistry; + let context: ReturnType; + + beforeEach(() => { + provider = createSavedObjectsResultProvider(); + registry = new SavedObjectTypeRegistry(); + + registry.registerType( + createType({ + name: 'typeA', + management: { + defaultSearchField: 'title', + getInAppUrl: (obj) => ({ path: `/type-a/${obj.id}`, uiCapabilitiesPath: '' }), + }, + }) + ); + registry.registerType( + createType({ + name: 'typeB', + management: { + defaultSearchField: 'description', + getInAppUrl: (obj) => ({ path: `/type-b/${obj.id}`, uiCapabilitiesPath: 'foo' }), + }, + }) + ); + + context = globalSearchPluginMock.createProviderContext(); + context.core.savedObjects.client.find.mockResolvedValue(createFindResponse([])); + context.core.savedObjects.typeRegistry = registry as any; + }); + + it('has the correct id', () => { + expect(provider.id).toBe('savedObjects'); + }); + + it('calls `savedObjectClient.find` with the correct parameters', () => { + provider.find('term', defaultOption, context); + + expect(context.core.savedObjects.client.find).toHaveBeenCalledTimes(1); + expect(context.core.savedObjects.client.find).toHaveBeenCalledWith({ + page: 1, + perPage: defaultOption.maxResults, + search: 'term', + searchFields: ['title', 'description'], + type: ['typeA', 'typeB'], + }); + }); + + it('converts the saved objects to results', async () => { + context.core.savedObjects.client.find.mockResolvedValue( + createFindResponse([ + createObject({ id: 'resultA', type: 'typeA' }, { title: 'titleA' }), + createObject({ id: 'resultB', type: 'typeB' }, { description: 'titleB' }), + ]) + ); + + const results = await provider.find('term', defaultOption, context).toPromise(); + expect(results).toEqual([ + { + id: 'resultA', + title: 'titleA', + type: 'typeA', + url: '/type-a/resultA', + score: 100, + }, + { + id: 'resultB', + title: 'titleB', + type: 'typeB', + url: '/type-b/resultB', + score: 100, + }, + ]); + }); + + it('only emits results until `aborted$` emits', () => { + getTestScheduler().run(({ hot, expectObservable }) => { + // test scheduler doesnt play well with promises. need to workaround by passing + // an observable instead. Behavior with promise is asserted in previous tests of the suite + context.core.savedObjects.client.find.mockReturnValue( + hot('---a', { a: createFindResponse([]) }) as any + ); + + const resultObs = provider.find( + 'term', + { ...defaultOption, aborted$: hot('-(a|)', { a: undefined }) }, + context + ); + + expectObservable(resultObs).toBe('-|'); + }); + }); +}); diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts index 2dca2a9cc59059..c31fdc813ba7c3 100644 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts @@ -19,13 +19,9 @@ export const createSavedObjectsResultProvider = (): GlobalSearchResultProvider = .getAllTypes() .filter((type) => !typeRegistry.isHidden(type.name)) .filter((type) => type.management?.defaultSearchField && type.management?.getInAppUrl); - const typeToField = searchableTypes.reduce((memo, type) => { - return { - ...memo, - [type.name]: type.management!.defaultSearchField!, - }; - }, {} as Record); - const searchFields = [...new Set(Object.values(typeToField))]; + const searchFields = [ + ...new Set(searchableTypes.map((type) => type.management!.defaultSearchField!)), + ]; // TODO: add preference once https://github.com/elastic/kibana/pull/68620 lands const responsePromise = client.find({ @@ -33,7 +29,7 @@ export const createSavedObjectsResultProvider = (): GlobalSearchResultProvider = perPage: maxResults, search: term, searchFields, - type: Object.keys(typeToField), + type: searchableTypes.map((type) => type.name), }); return from(responsePromise).pipe( From a2aecbe00738b1d340e45c6e256b18f049b2d874 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Tue, 23 Jun 2020 11:22:54 +0200 Subject: [PATCH 04/12] remove useless contracts --- .../global_search_providers/server/index.ts | 17 +++-------------- .../global_search_providers/server/plugin.ts | 11 +---------- .../global_search_providers/server/types.ts | 11 ----------- 3 files changed, 4 insertions(+), 35 deletions(-) delete mode 100644 x-pack/plugins/global_search_providers/server/types.ts diff --git a/x-pack/plugins/global_search_providers/server/index.ts b/x-pack/plugins/global_search_providers/server/index.ts index 44115a230231db..26e4142d4865aa 100644 --- a/x-pack/plugins/global_search_providers/server/index.ts +++ b/x-pack/plugins/global_search_providers/server/index.ts @@ -5,18 +5,7 @@ */ import { PluginInitializer } from 'src/core/server'; -import { - GlobalSearchProvidersPlugin, - GlobalSearchProvidersPluginSetupDeps, - GlobalSearchProvidersPluginStartDeps, -} from './plugin'; -import { GlobalSearchProvidersPluginSetup, GlobalSearchProvidersPluginStart } from './types'; +import { GlobalSearchProvidersPlugin, GlobalSearchProvidersPluginSetupDeps } from './plugin'; -export const plugin: PluginInitializer< - GlobalSearchProvidersPluginSetup, - GlobalSearchProvidersPluginStart, - GlobalSearchProvidersPluginSetupDeps, - GlobalSearchProvidersPluginStartDeps -> = (context) => new GlobalSearchProvidersPlugin(); - -export { GlobalSearchProvidersPluginSetup, GlobalSearchProvidersPluginStart }; +export const plugin: PluginInitializer<{}, {}, GlobalSearchProvidersPluginSetupDeps, {}> = () => + new GlobalSearchProvidersPlugin(); diff --git a/x-pack/plugins/global_search_providers/server/plugin.ts b/x-pack/plugins/global_search_providers/server/plugin.ts index f37824e3a2d8a3..efe704cdb9f29c 100644 --- a/x-pack/plugins/global_search_providers/server/plugin.ts +++ b/x-pack/plugins/global_search_providers/server/plugin.ts @@ -13,17 +13,8 @@ export interface GlobalSearchProvidersPluginSetupDeps { globalSearch: GlobalSearchPluginSetup; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface GlobalSearchProvidersPluginStartDeps {} - export class GlobalSearchProvidersPlugin - implements - Plugin< - GlobalSearchProvidersPluginSetup, - GlobalSearchProvidersPluginStart, - GlobalSearchProvidersPluginSetupDeps, - GlobalSearchProvidersPluginStartDeps - > { + implements Plugin<{}, {}, GlobalSearchProvidersPluginSetupDeps, {}> { setup( { getStartServices, diff --git a/x-pack/plugins/global_search_providers/server/types.ts b/x-pack/plugins/global_search_providers/server/types.ts deleted file mode 100644 index f3aac002606f5a..00000000000000 --- a/x-pack/plugins/global_search_providers/server/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface GlobalSearchProvidersPluginSetup {} - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface GlobalSearchProvidersPluginStart {} From 872e8ab18643a7d8ae48403b0c8db757ad1b5846 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Tue, 23 Jun 2020 11:23:19 +0200 Subject: [PATCH 05/12] add preference search option --- .../server/providers/saved_objects/provider.test.ts | 1 + .../server/providers/saved_objects/provider.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts index 34663d0bbac352..4cf449387d0f9a 100644 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts @@ -107,6 +107,7 @@ describe('savedObjectsResultProvider', () => { page: 1, perPage: defaultOption.maxResults, search: 'term', + preference: 'pref', searchFields: ['title', 'description'], type: ['typeA', 'typeB'], }); diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts index c31fdc813ba7c3..dd61be99d827cd 100644 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts @@ -12,7 +12,7 @@ import { mapToResults } from './map_object_to_result'; export const createSavedObjectsResultProvider = (): GlobalSearchResultProvider => { return { id: 'savedObjects', - find: (term, { aborted$, maxResults }, { core }) => { + find: (term, { aborted$, maxResults, preference }, { core }) => { const { typeRegistry, client } = core.savedObjects; const searchableTypes = typeRegistry @@ -23,11 +23,11 @@ export const createSavedObjectsResultProvider = (): GlobalSearchResultProvider = ...new Set(searchableTypes.map((type) => type.management!.defaultSearchField!)), ]; - // TODO: add preference once https://github.com/elastic/kibana/pull/68620 lands const responsePromise = client.find({ page: 1, perPage: maxResults, search: term, + preference, searchFields, type: searchableTypes.map((type) => type.name), }); From adb832c6bfab30979d8f5024f0ff43f5697e1386 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Tue, 23 Jun 2020 11:34:09 +0200 Subject: [PATCH 06/12] implement score from find results --- .../map_object_to_result.test.ts | 18 ++++++++++++----- .../saved_objects/map_object_to_result.ts | 12 +++++++---- .../providers/saved_objects/provider.test.ts | 20 ++++++++++++------- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts index 255150b805be03..3ed1eef2201afc 100644 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObject, SavedObjectsType, SavedObjectTypeRegistry } from 'src/core/server'; +import { SavedObjectsFindResult, SavedObjectsType, SavedObjectTypeRegistry } from 'src/core/server'; import { mapToResult, mapToResults } from './map_object_to_result'; const createType = (props: Partial): SavedObjectsType => { @@ -22,11 +22,15 @@ const createType = (props: Partial): SavedObjectsType => { }; }; -const createObject = (props: Partial, attributes: T): SavedObject => { +const createObject = ( + props: Partial, + attributes: T +): SavedObjectsFindResult => { return { id: 'id', type: 'dashboard', references: [], + score: 100, ...props, attributes, }; @@ -46,6 +50,7 @@ describe('mapToResult', () => { { id: 'dash1', type: 'dashboard', + score: 42, }, { title: 'My dashboard', @@ -57,7 +62,7 @@ describe('mapToResult', () => { title: 'My dashboard', type: 'dashboard', url: '/dashboard/dash1', - score: 100, + score: 42, }); }); }); @@ -103,6 +108,7 @@ describe('mapToResults', () => { { id: 'resultA', type: 'typeA', + score: 100, }, { title: 'titleA', @@ -113,6 +119,7 @@ describe('mapToResults', () => { { id: 'resultC', type: 'typeC', + score: 42, }, { excerpt: 'titleC', @@ -123,6 +130,7 @@ describe('mapToResults', () => { { id: 'resultB', type: 'typeB', + score: 69, }, { description: 'titleB', @@ -144,14 +152,14 @@ describe('mapToResults', () => { title: 'titleC', type: 'typeC', url: '/type-c/resultC', - score: 100, + score: 42, }, { id: 'resultB', title: 'titleB', type: 'typeB', url: '/type-b/resultB', - score: 100, + score: 69, }, ]); }); diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts index 0c64a991ff2249..a8a9fb69c2a981 100644 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts @@ -4,18 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObject, SavedObjectsType, ISavedObjectTypeRegistry } from 'src/core/server'; +import { + SavedObjectsType, + ISavedObjectTypeRegistry, + SavedObjectsFindResult, +} from 'src/core/server'; import { GlobalSearchProviderResult } from '../../../../global_search/server'; export const mapToResults = ( - objects: Array>, + objects: Array>, registry: ISavedObjectTypeRegistry ): GlobalSearchProviderResult[] => { return objects.map((obj) => mapToResult(obj, registry.getType(obj.type)!)); }; export const mapToResult = ( - object: SavedObject, + object: SavedObjectsFindResult, type: SavedObjectsType ): GlobalSearchProviderResult => { const { defaultSearchField, getInAppUrl } = type.management!; @@ -25,6 +29,6 @@ export const mapToResult = ( title: (object.attributes as any)[defaultSearchField!], type: object.type, url: getInAppUrl!(object).path, - score: 100, + score: object.score, }; }; diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts index 4cf449387d0f9a..84e05c67c5f668 100644 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts @@ -8,7 +8,7 @@ import { EMPTY } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; import { SavedObjectsFindResponse, - SavedObject, + SavedObjectsFindResult, SavedObjectsType, SavedObjectTypeRegistry, } from 'src/core/server'; @@ -24,7 +24,9 @@ const getTestScheduler = () => expect(actual).toEqual(expected); }); -const createFindResponse = (results: SavedObject[]): SavedObjectsFindResponse => ({ +const createFindResponse = ( + results: SavedObjectsFindResult[] +): SavedObjectsFindResponse => ({ saved_objects: results, page: 1, per_page: 20, @@ -46,10 +48,14 @@ const createType = (props: Partial): SavedObjectsType => { }; }; -const createObject = (props: Partial, attributes: T): SavedObject => { +const createObject = ( + props: Partial, + attributes: T +): SavedObjectsFindResult => { return { id: 'id', type: 'dashboard', + score: 100, references: [], ...props, attributes, @@ -116,8 +122,8 @@ describe('savedObjectsResultProvider', () => { it('converts the saved objects to results', async () => { context.core.savedObjects.client.find.mockResolvedValue( createFindResponse([ - createObject({ id: 'resultA', type: 'typeA' }, { title: 'titleA' }), - createObject({ id: 'resultB', type: 'typeB' }, { description: 'titleB' }), + createObject({ id: 'resultA', type: 'typeA', score: 50 }, { title: 'titleA' }), + createObject({ id: 'resultB', type: 'typeB', score: 78 }, { description: 'titleB' }), ]) ); @@ -128,14 +134,14 @@ describe('savedObjectsResultProvider', () => { title: 'titleA', type: 'typeA', url: '/type-a/resultA', - score: 100, + score: 50, }, { id: 'resultB', title: 'titleB', type: 'typeB', url: '/type-b/resultB', - score: 100, + score: 78, }, ]); }); From eb7916f45ae495f4b60fc01a9ec9b74e89ddcf94 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Tue, 23 Jun 2020 12:06:38 +0200 Subject: [PATCH 07/12] fix types --- x-pack/plugins/global_search_providers/server/plugin.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/global_search_providers/server/plugin.ts b/x-pack/plugins/global_search_providers/server/plugin.ts index efe704cdb9f29c..64e7802937d804 100644 --- a/x-pack/plugins/global_search_providers/server/plugin.ts +++ b/x-pack/plugins/global_search_providers/server/plugin.ts @@ -6,7 +6,6 @@ import { CoreSetup, Plugin } from 'src/core/server'; import { GlobalSearchPluginSetup } from '../../global_search/server'; -import { GlobalSearchProvidersPluginSetup, GlobalSearchProvidersPluginStart } from './types'; import { createSavedObjectsResultProvider } from './providers'; export interface GlobalSearchProvidersPluginSetupDeps { @@ -16,16 +15,14 @@ export interface GlobalSearchProvidersPluginSetupDeps { export class GlobalSearchProvidersPlugin implements Plugin<{}, {}, GlobalSearchProvidersPluginSetupDeps, {}> { setup( - { - getStartServices, - }: CoreSetup, + { getStartServices }: CoreSetup<{}, {}>, { globalSearch }: GlobalSearchProvidersPluginSetupDeps - ): GlobalSearchProvidersPluginSetup { + ) { globalSearch.registerResultProvider(createSavedObjectsResultProvider()); return {}; } - start(): GlobalSearchProvidersPluginStart { + start() { return {}; } } From 929eb8940623e23e6edbe10b9dbf6477769bf115 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Tue, 23 Jun 2020 13:35:51 +0200 Subject: [PATCH 08/12] add FTR test --- .../es_archives/global_search/basic/data.json | 193 +++++++ .../global_search/basic/mappings.json | 516 ++++++++++++++++++ .../global_search_test/public/plugin.ts | 12 +- .../global_search/global_search_api.ts | 2 +- .../global_search/global_search_providers.ts | 64 +++ .../test_suites/global_search/index.ts | 1 + 6 files changed, 786 insertions(+), 2 deletions(-) create mode 100644 x-pack/test/plugin_functional/es_archives/global_search/basic/data.json create mode 100644 x-pack/test/plugin_functional/es_archives/global_search/basic/mappings.json create mode 100644 x-pack/test/plugin_functional/test_suites/global_search/global_search_providers.ts diff --git a/x-pack/test/plugin_functional/es_archives/global_search/basic/data.json b/x-pack/test/plugin_functional/es_archives/global_search/basic/data.json new file mode 100644 index 00000000000000..dcedc7f40b2a8c --- /dev/null +++ b/x-pack/test/plugin_functional/es_archives/global_search/basic/data.json @@ -0,0 +1,193 @@ +{ + "type": "doc", + "value": { + "index": ".kibana", + "type": "doc", + "id": "index-pattern:logstash-*", + "source": { + "index-pattern": { + "title": "logstash-*", + "timeFieldName": "@timestamp", + "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]" + }, + "type": "index-pattern", + "migrationVersion": { + "index-pattern": "6.5.0" + }, + "updated_at": "2018-12-21T00:43:07.096Z" + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "type": "doc", + "id": "visualization:75c3e060-1e7c-11e9-8488-65449e65d0ed", + "source": { + "visualization": { + "title": "A Pie", + "visState": "{\"title\":\"A Pie\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"dimensions\":{\"metric\":{\"accessor\":0,\"format\":{\"id\":\"number\"},\"params\":{},\"aggType\":\"count\"}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.src\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}", + "uiStateJSON": "{}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" + } + }, + "type": "visualization", + "updated_at": "2019-01-22T19:32:31.206Z" + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "type": "doc", + "id": "dashboard:i-exist", + "source": { + "dashboard": { + "title": "Amazing Dashboard", + "hits": 0, + "description": "", + "panelsJSON": "[{\"gridData\":{\"w\":24,\"h\":15,\"x\":0,\"y\":0,\"i\":\"1\"},\"version\":\"7.0.0\",\"panelIndex\":\"1\",\"type\":\"visualization\",\"id\":\"75c3e060-1e7c-11e9-8488-65449e65d0ed\",\"embeddableConfig\":{}}]", + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "version": 1, + "timeRestore": false, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" + } + }, + "type": "dashboard", + "updated_at": "2019-01-22T19:32:47.232Z" + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "type": "doc", + "id": "config:6.0.0", + "source": { + "config": { + "buildNum": 9007199254740991, + "defaultIndex": "logstash-*" + }, + "type": "config", + "updated_at": "2019-01-22T19:32:02.235Z" + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "type": "doc", + "id": "map:0b849ed0-70f5-11e9-8625-9580c4904684", + "source": { + "map": { + "title" : "just a map", + "description" : "", + "mapStateJSON" : "{\"zoom\":0.8,\"center\":{\"lon\":0,\"lat\":0},\"timeFilters\":{\"from\":\"now-15m\",\"to\":\"now\"},\"refreshConfig\":{\"isPaused\":false,\"interval\":0},\"query\":{\"query\":\"\",\"language\":\"kuery\"}}", + "layerListJSON" : "[{\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"id\":\"c7bdee60-5267-459e-83d6-b53acf1b9e67\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"applyGlobalQuery\":true,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"TILE\"}]", + "uiStateJSON" : "{\"isLayerTOCOpen\":true}", + "bounds" : { + "type" : "polygon", + "coordinates" : [ + [ + [ + -180, + 85.05113 + ], + [ + -180, + -85.05113 + ], + [ + 180, + -85.05113 + ], + [ + 180, + 85.05113 + ], + [ + -180, + 85.05113 + ] + ] + ] + } + }, + "type": "map", + "references" : [ ], + "migrationVersion" : { + "map" : "7.1.0" + }, + "updated_at" : "2019-05-07T18:22:17.405Z" + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "type": "doc", + "id": "dashboard:1c1a87f0-70f5-11e9-8625-9580c4904684", + "source": { + "dashboard": { + "title" : "dashboard with map", + "hits" : 0, + "description" : "", + "panelsJSON" : "[{\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1\"},\"version\":\"8.0.0\",\"panelIndex\":\"1\",\"embeddableConfig\":{\"mapCenter\":{\"lat\":0,\"lon\":0,\"zoom\":0.8},\"isLayerTOCOpen\":true},\"panelRefName\":\"panel_0\"}]", + "optionsJSON" : "{\"useMargins\":true,\"hidePanelTitles\":false}", + "version" : 1, + "timeRestore" : false, + "kibanaSavedObjectMeta" : { + "searchSourceJSON" : "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + } + }, + "type": "dashboard", + "references" : [ + { + "name" : "panel_0", + "type" : "map", + "id" : "0b849ed0-70f5-11e9-8625-9580c4904684" + } + ], + "migrationVersion" : { + "dashboard" : "7.0.0" + }, + "updated_at" : "2019-05-07T18:22:45.231Z" + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "type": "doc", + "id": "query:okjpgs", + "source": { + "query": { + "title": "OKJpgs", + "description": "Ok responses for jpg files", + "query": { + "query": "response:200", + "language": "kuery" + }, + "filters": [{"meta":{"index":"b15b1d40-a8bb-11e9-98cf-2bb06ef63e0b","alias":null,"negate":false,"type":"phrase","key":"extension.raw","value":"jpg","params":{"query":"jpg"},"disabled":false},"query":{"match":{"extension.raw":{"query":"jpg","type":"phrase"}}},"$state":{"store":"appState"}}] + }, + "type": "query", + "updated_at": "2019-07-17T17:54:26.378Z" + } + } +} diff --git a/x-pack/test/plugin_functional/es_archives/global_search/basic/mappings.json b/x-pack/test/plugin_functional/es_archives/global_search/basic/mappings.json new file mode 100644 index 00000000000000..ebb5b19387faf2 --- /dev/null +++ b/x-pack/test/plugin_functional/es_archives/global_search/basic/mappings.json @@ -0,0 +1,516 @@ +{ + "type": "index", + "value": { + "index": ".kibana", + "settings": { + "index": { + "number_of_shards": "1", + "auto_expand_replicas": "0-1", + "number_of_replicas": "0" + } + }, + "mappings": { + "dynamic": "strict", + "properties": { + "apm-telemetry": { + "properties": { + "has_any_services": { + "type": "boolean" + }, + "services_per_agent": { + "properties": { + "go": { + "type": "long", + "null_value": 0 + }, + "java": { + "type": "long", + "null_value": 0 + }, + "js-base": { + "type": "long", + "null_value": 0 + }, + "nodejs": { + "type": "long", + "null_value": 0 + }, + "python": { + "type": "long", + "null_value": 0 + }, + "ruby": { + "type": "long", + "null_value": 0 + } + } + } + } + }, + "canvas-workpad": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "id": { + "type": "text", + "index": false + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + } + } + } + }, + "config": { + "dynamic": "true", + "properties": { + "accessibility:disableAnimations": { + "type": "boolean" + }, + "buildNum": { + "type": "keyword" + }, + "dateFormat:tz": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + }, + "defaultIndex": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + }, + "telemetry:optIn": { + "type": "boolean" + } + } + }, + "dashboard": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "optionsJSON": { + "type": "text" + }, + "panelsJSON": { + "type": "text" + }, + "refreshInterval": { + "properties": { + "display": { + "type": "keyword" + }, + "pause": { + "type": "boolean" + }, + "section": { + "type": "integer" + }, + "value": { + "type": "integer" + } + } + }, + "timeFrom": { + "type": "keyword" + }, + "timeRestore": { + "type": "boolean" + }, + "timeTo": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "map" : { + "properties" : { + "bounds" : { + "type" : "geo_shape", + "tree" : "quadtree" + }, + "description" : { + "type" : "text" + }, + "layerListJSON" : { + "type" : "text" + }, + "mapStateJSON" : { + "type" : "text" + }, + "title" : { + "type" : "text" + }, + "uiStateJSON" : { + "type" : "text" + }, + "version" : { + "type" : "integer" + } + } + }, + "graph-workspace": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "numLinks": { + "type": "integer" + }, + "numVertices": { + "type": "integer" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "wsState": { + "type": "text" + } + } + }, + "index-pattern": { + "properties": { + "fieldFormatMap": { + "type": "text" + }, + "fields": { + "type": "text" + }, + "intervalName": { + "type": "keyword" + }, + "notExpandable": { + "type": "boolean" + }, + "sourceFilters": { + "type": "text" + }, + "timeFieldName": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "type": { + "type": "keyword" + }, + "typeMeta": { + "type": "keyword" + } + } + }, + "kql-telemetry": { + "properties": { + "optInCount": { + "type": "long" + }, + "optOutCount": { + "type": "long" + } + } + }, + "migrationVersion": { + "dynamic": "true", + "properties": { + "index-pattern": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + }, + "space": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + } + } + }, + "migrationVersion": { + "dynamic": "true", + "properties": { + "index-pattern": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "namespace": { + "type": "keyword" + }, + "references": { + "type": "nested", + "properties": { + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "id": { + "type": "keyword" + } + } + }, + "search": { + "properties": { + "columns": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "sort": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "server": { + "properties": { + "uuid": { + "type": "keyword" + } + } + }, + "space": { + "properties": { + "_reserved": { + "type": "boolean" + }, + "color": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "disabledFeatures": { + "type": "keyword" + }, + "initials": { + "type": "keyword" + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 2048 + } + } + } + } + }, + "spaceId": { + "type": "keyword" + }, + "telemetry": { + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "timelion-sheet": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "timelion_chart_height": { + "type": "integer" + }, + "timelion_columns": { + "type": "integer" + }, + "timelion_interval": { + "type": "keyword" + }, + "timelion_other_interval": { + "type": "keyword" + }, + "timelion_rows": { + "type": "integer" + }, + "timelion_sheet": { + "type": "text" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "url": { + "properties": { + "accessCount": { + "type": "long" + }, + "accessDate": { + "type": "date" + }, + "createDate": { + "type": "date" + }, + "url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 2048 + } + } + } + } + }, + "visualization": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "savedSearchId": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "visState": { + "type": "text" + } + } + }, + "query": { + "properties": { + "title": { + "type": "text" + }, + "description": { + "type": "text" + }, + "query": { + "properties": { + "language": { + "type": "keyword" + }, + "query": { + "type": "keyword", + "index": false + } + } + }, + "filters": { + "type": "object", + "enabled": false + }, + "timefilter": { + "type": "object", + "enabled": false + } + } + } + } + } + } +} diff --git a/x-pack/test/plugin_functional/plugins/global_search_test/public/plugin.ts b/x-pack/test/plugin_functional/plugins/global_search_test/public/plugin.ts index 27434202d77f1c..045af43e7d7c43 100644 --- a/x-pack/test/plugin_functional/plugins/global_search_test/public/plugin.ts +++ b/x-pack/test/plugin_functional/plugins/global_search_test/public/plugin.ts @@ -74,7 +74,7 @@ export class GlobalSearchTestPlugin { globalSearch }: GlobalSearchTestPluginStartDeps ): GlobalSearchTestPluginStart { return { - findAll: (term) => + findTest: (term) => globalSearch .find(term, {}) .pipe( @@ -84,6 +84,16 @@ export class GlobalSearchTestPlugin reduce((memo, results) => [...memo, ...results]) ) .toPromise(), + findReal: (term) => + globalSearch + .find(term, {}) + .pipe( + map((batch) => batch.results), + // remove test types + map((results) => results.filter((r) => !r.type.startsWith('test_'))), + reduce((memo, results) => [...memo, ...results]) + ) + .toPromise(), }; } } diff --git a/x-pack/test/plugin_functional/test_suites/global_search/global_search_api.ts b/x-pack/test/plugin_functional/test_suites/global_search/global_search_api.ts index ee1745436b7357..841c4d2967e21b 100644 --- a/x-pack/test/plugin_functional/test_suites/global_search/global_search_api.ts +++ b/x-pack/test/plugin_functional/test_suites/global_search/global_search_api.ts @@ -17,7 +17,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { return browser.executeAsync(async (term, cb) => { const { start } = window.__coreProvider; const globalSearchTestApi: GlobalSearchTestApi = start.plugins.globalSearchTest; - globalSearchTestApi.findAll(term).then(cb); + globalSearchTestApi.findTest(term).then(cb); }, t); }; diff --git a/x-pack/test/plugin_functional/test_suites/global_search/global_search_providers.ts b/x-pack/test/plugin_functional/test_suites/global_search/global_search_providers.ts new file mode 100644 index 00000000000000..aeed01d008bb5e --- /dev/null +++ b/x-pack/test/plugin_functional/test_suites/global_search/global_search_providers.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { GlobalSearchResult } from '../../../../plugins/global_search/common/types'; +import { GlobalSearchTestApi } from '../../plugins/global_search_test/public/types'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const pageObjects = getPageObjects(['common']); + const browser = getService('browser'); + const esArchiver = getService('esArchiver'); + + const findResultsWithAPI = async (t: string): Promise => { + return browser.executeAsync(async (term, cb) => { + const { start } = window.__coreProvider; + const globalSearchTestApi: GlobalSearchTestApi = start.plugins.globalSearchTest; + globalSearchTestApi.findReal(term).then(cb); + }, t); + }; + + describe('GlobalSearch - SavedObject provider', function () { + beforeEach(async () => { + await esArchiver.load('global_search/basic'); + await pageObjects.common.navigateToApp('globalSearchTestApp'); + }); + + afterEach(async () => { + await esArchiver.unload('global_search/basic'); + }); + + it('can search for index patterns', async () => { + const results = await findResultsWithAPI('logstash'); + expect(results.length).to.be(1); + expect(results[0].type).to.be('index-pattern'); + expect(results[0].title).to.be('logstash-*'); + expect(results[0].score).to.be.greaterThan(1); + }); + + it('can search for visualizations', async () => { + const results = await findResultsWithAPI('pie'); + expect(results.length).to.be(1); + expect(results[0].type).to.be('visualization'); + expect(results[0].title).to.be('A Pie'); + }); + + it('can search for maps', async () => { + const results = await findResultsWithAPI('just'); + expect(results.length).to.be(1); + expect(results[0].type).to.be('map'); + expect(results[0].title).to.be('just a map'); + }); + + it('can search for dashboards', async () => { + const results = await findResultsWithAPI('Amazing'); + expect(results.length).to.be(1); + expect(results[0].type).to.be('dashboard'); + expect(results[0].title).to.be('Amazing Dashboard'); + }); + }); +} diff --git a/x-pack/test/plugin_functional/test_suites/global_search/index.ts b/x-pack/test/plugin_functional/test_suites/global_search/index.ts index 1e5a765612f45d..d765e87add105b 100644 --- a/x-pack/test/plugin_functional/test_suites/global_search/index.ts +++ b/x-pack/test/plugin_functional/test_suites/global_search/index.ts @@ -10,5 +10,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { describe('GlobalSearch API', function () { this.tags('ciGroup7'); loadTestFile(require.resolve('./global_search_api')); + loadTestFile(require.resolve('./global_search_providers')); }); } From b3f62e309a65d598240eb1155e03537f10fc8412 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Tue, 23 Jun 2020 16:35:32 +0200 Subject: [PATCH 09/12] fix test plugin types --- .../plugins/global_search_test/public/plugin.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/plugin_functional/plugins/global_search_test/public/plugin.ts b/x-pack/test/plugin_functional/plugins/global_search_test/public/plugin.ts index 045af43e7d7c43..aba3512788f9c3 100644 --- a/x-pack/test/plugin_functional/plugins/global_search_test/public/plugin.ts +++ b/x-pack/test/plugin_functional/plugins/global_search_test/public/plugin.ts @@ -17,7 +17,8 @@ import { createResult } from '../common/utils'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface GlobalSearchTestPluginSetup {} export interface GlobalSearchTestPluginStart { - findAll: (term: string) => Promise; + findTest: (term: string) => Promise; + findReal: (term: string) => Promise; } export interface GlobalSearchTestPluginSetupDeps { From 4b86f0723cf5b287057424ca24d50d49c46f8c7a Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Thu, 2 Jul 2020 11:28:02 +0200 Subject: [PATCH 10/12] address ome review comments --- .../map_object_to_result.test.ts | 52 +++++++++++++++++-- .../saved_objects/map_object_to_result.ts | 12 +++-- .../providers/saved_objects/provider.ts | 8 +-- 3 files changed, 60 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts index 3ed1eef2201afc..0085331c5be5f2 100644 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts @@ -14,11 +14,6 @@ const createType = (props: Partial): SavedObjectsType => { namespaceType: 'single', mappings: { properties: {} }, ...props, - management: { - defaultSearchField: 'field', - getInAppUrl: (obj) => ({ path: `/object/${obj.id}`, uiCapabilitiesPath: '' }), - ...props.management, - }, }; }; @@ -65,6 +60,53 @@ describe('mapToResult', () => { score: 42, }); }); + + it('throws if the type do not have management information', () => { + const object = createObject( + { id: 'dash1', type: 'dashboard', score: 42 }, + { title: 'My dashboard' } + ); + + expect(() => { + mapToResult( + object, + createType({ + name: 'dashboard', + management: { + getInAppUrl: (obj) => ({ path: `/dashboard/${obj.id}`, uiCapabilitiesPath: '' }), + }, + }) + ); + }).toThrowErrorMatchingInlineSnapshot( + `"Trying to map an object from a type without management metadata"` + ); + + expect(() => { + mapToResult( + object, + createType({ + name: 'dashboard', + management: { + defaultSearchField: 'title', + }, + }) + ); + }).toThrowErrorMatchingInlineSnapshot( + `"Trying to map an object from a type without management metadata"` + ); + + expect(() => { + mapToResult( + object, + createType({ + name: 'dashboard', + management: undefined, + }) + ); + }).toThrowErrorMatchingInlineSnapshot( + `"Trying to map an object from a type without management metadata"` + ); + }); }); describe('mapToResults', () => { diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts index a8a9fb69c2a981..c93558b1a3cf4c 100644 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts @@ -22,13 +22,17 @@ export const mapToResult = ( object: SavedObjectsFindResult, type: SavedObjectsType ): GlobalSearchProviderResult => { - const { defaultSearchField, getInAppUrl } = type.management!; - + const { defaultSearchField, getInAppUrl } = type.management ?? {}; + if (defaultSearchField === undefined || getInAppUrl === undefined) { + throw new Error('Trying to map an object from a type without management metadata'); + } return { id: object.id, - title: (object.attributes as any)[defaultSearchField!], + // defaultSearchField is dynamic and not 'directly' bound to the generic type of the SavedObject + // so we are forced to cast the attributes to any to access the properties associated with it. + title: (object.attributes as any)[defaultSearchField], type: object.type, - url: getInAppUrl!(object).path, + url: getInAppUrl(object).path, score: object.score, }; }; diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts index dd61be99d827cd..a07199a3514b78 100644 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts @@ -19,9 +19,9 @@ export const createSavedObjectsResultProvider = (): GlobalSearchResultProvider = .getAllTypes() .filter((type) => !typeRegistry.isHidden(type.name)) .filter((type) => type.management?.defaultSearchField && type.management?.getInAppUrl); - const searchFields = [ - ...new Set(searchableTypes.map((type) => type.management!.defaultSearchField!)), - ]; + const searchFields = uniq( + searchableTypes.map((type) => type.management!.defaultSearchField!) + ); const responsePromise = client.find({ page: 1, @@ -39,3 +39,5 @@ export const createSavedObjectsResultProvider = (): GlobalSearchResultProvider = }, }; }; + +const uniq = (values: T[]): T[] => [...new Set(values)]; From 11caf687bd4ea0b10f73208cbfa98c78c2c00736 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Thu, 2 Jul 2020 13:17:21 +0200 Subject: [PATCH 11/12] add multi results test --- .../es_archives/global_search/basic/data.json | 2 +- .../global_search/global_search_providers.ts | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/x-pack/test/plugin_functional/es_archives/global_search/basic/data.json b/x-pack/test/plugin_functional/es_archives/global_search/basic/data.json index dcedc7f40b2a8c..f121f6859885b1 100644 --- a/x-pack/test/plugin_functional/es_archives/global_search/basic/data.json +++ b/x-pack/test/plugin_functional/es_archives/global_search/basic/data.json @@ -50,7 +50,7 @@ "id": "dashboard:i-exist", "source": { "dashboard": { - "title": "Amazing Dashboard", + "title": "Amazing Dashboard", "hits": 0, "description": "", "panelsJSON": "[{\"gridData\":{\"w\":24,\"h\":15,\"x\":0,\"y\":0,\"i\":\"1\"},\"version\":\"7.0.0\",\"panelIndex\":\"1\",\"type\":\"visualization\",\"id\":\"75c3e060-1e7c-11e9-8488-65449e65d0ed\",\"embeddableConfig\":{}}]", diff --git a/x-pack/test/plugin_functional/test_suites/global_search/global_search_providers.ts b/x-pack/test/plugin_functional/test_suites/global_search/global_search_providers.ts index aeed01d008bb5e..4e4f42578d11a1 100644 --- a/x-pack/test/plugin_functional/test_suites/global_search/global_search_providers.ts +++ b/x-pack/test/plugin_functional/test_suites/global_search/global_search_providers.ts @@ -23,15 +23,18 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }; describe('GlobalSearch - SavedObject provider', function () { - beforeEach(async () => { + before(async () => { await esArchiver.load('global_search/basic'); - await pageObjects.common.navigateToApp('globalSearchTestApp'); }); - afterEach(async () => { + after(async () => { await esArchiver.unload('global_search/basic'); }); + beforeEach(async () => { + await pageObjects.common.navigateToApp('globalSearchTestApp'); + }); + it('can search for index patterns', async () => { const results = await findResultsWithAPI('logstash'); expect(results.length).to.be(1); @@ -58,7 +61,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const results = await findResultsWithAPI('Amazing'); expect(results.length).to.be(1); expect(results[0].type).to.be('dashboard'); - expect(results[0].title).to.be('Amazing Dashboard'); + expect(results[0].title).to.be('Amazing Dashboard'); + }); + + it('returns all objects matching the search', async () => { + const results = await findResultsWithAPI('dashboard'); + expect(results.length).to.be.greaterThan(2); + expect(results.map((r) => r.title)).to.contain('dashboard with map'); + expect(results.map((r) => r.title)).to.contain('Amazing Dashboard'); }); }); } From 63e7c2d9580f10964108d4326f122406bc3cdbe2 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Thu, 2 Jul 2020 13:21:41 +0200 Subject: [PATCH 12/12] use `getVisibleTypes` --- .../server/providers/saved_objects/provider.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts index a07199a3514b78..b423b19ebc6723 100644 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts @@ -16,8 +16,7 @@ export const createSavedObjectsResultProvider = (): GlobalSearchResultProvider = const { typeRegistry, client } = core.savedObjects; const searchableTypes = typeRegistry - .getAllTypes() - .filter((type) => !typeRegistry.isHidden(type.name)) + .getVisibleTypes() .filter((type) => type.management?.defaultSearchField && type.management?.getInAppUrl); const searchFields = uniq( searchableTypes.map((type) => type.management!.defaultSearchField!)