From 4f54124b77198d3ffc40ca3e22217a0ce0996791 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Wed, 30 Mar 2022 09:40:17 +0200 Subject: [PATCH] [Discover] Implement search source migration (#128609) --- src/plugins/discover/server/plugin.ts | 15 +++- .../discover/server/saved_objects/index.ts | 2 +- .../discover/server/saved_objects/search.ts | 85 ++++++++++--------- .../saved_objects/search_migrations.test.ts | 33 ++++++- .../server/saved_objects/search_migrations.ts | 54 +++++++++++- 5 files changed, 140 insertions(+), 49 deletions(-) diff --git a/src/plugins/discover/server/plugin.ts b/src/plugins/discover/server/plugin.ts index 879b75986365..00888d31f8d9 100644 --- a/src/plugins/discover/server/plugin.ts +++ b/src/plugins/discover/server/plugin.ts @@ -9,13 +9,22 @@ import { CoreSetup, CoreStart, Plugin } from 'kibana/server'; import { getUiSettings } from './ui_settings'; import { capabilitiesProvider } from './capabilities_provider'; -import { searchSavedObjectType } from './saved_objects'; +import { getSavedSearchObjectType } from './saved_objects'; +import type { PluginSetup as DataPluginSetup } from '../../data/server'; export class DiscoverServerPlugin implements Plugin { - public setup(core: CoreSetup) { + public setup( + core: CoreSetup, + plugins: { + data: DataPluginSetup; + } + ) { + const getSearchSourceMigrations = plugins.data.search.searchSource.getAllMigrations.bind( + plugins.data.search.searchSource + ); core.capabilities.registerProvider(capabilitiesProvider); core.uiSettings.register(getUiSettings(core.docLinks)); - core.savedObjects.registerType(searchSavedObjectType); + core.savedObjects.registerType(getSavedSearchObjectType(getSearchSourceMigrations)); return {}; } diff --git a/src/plugins/discover/server/saved_objects/index.ts b/src/plugins/discover/server/saved_objects/index.ts index 27bb6eead7f6..fdb078850cf7 100644 --- a/src/plugins/discover/server/saved_objects/index.ts +++ b/src/plugins/discover/server/saved_objects/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { searchSavedObjectType } from './search'; +export { getSavedSearchObjectType } from './search'; diff --git a/src/plugins/discover/server/saved_objects/search.ts b/src/plugins/discover/server/saved_objects/search.ts index d5b0a3e09bc6..796ab164cf0a 100644 --- a/src/plugins/discover/server/saved_objects/search.ts +++ b/src/plugins/discover/server/saved_objects/search.ts @@ -7,46 +7,51 @@ */ import { SavedObjectsType } from 'kibana/server'; -import { searchMigrations } from './search_migrations'; +import { MigrateFunctionsObject } from 'src/plugins/kibana_utils/common'; +import { getAllMigrations } from './search_migrations'; -export const searchSavedObjectType: SavedObjectsType = { - name: 'search', - hidden: false, - namespaceType: 'multiple-isolated', - convertToMultiNamespaceTypeVersion: '8.0.0', - management: { - icon: 'discoverApp', - defaultSearchField: 'title', - importableAndExportable: true, - getTitle(obj) { - return obj.attributes.title; - }, - getInAppUrl(obj) { - return { - path: `/app/discover#/view/${encodeURIComponent(obj.id)}`, - uiCapabilitiesPath: 'discover.show', - }; - }, - }, - mappings: { - properties: { - columns: { type: 'keyword', index: false, doc_values: false }, - description: { type: 'text' }, - viewMode: { type: 'keyword', index: false, doc_values: false }, - hideChart: { type: 'boolean', index: false, doc_values: false }, - hideAggregatedPreview: { type: 'boolean', index: false, doc_values: false }, - hits: { type: 'integer', index: false, doc_values: false }, - kibanaSavedObjectMeta: { - properties: { - searchSourceJSON: { type: 'text', index: false }, - }, +export function getSavedSearchObjectType( + getSearchSourceMigrations: () => MigrateFunctionsObject +): SavedObjectsType { + return { + name: 'search', + hidden: false, + namespaceType: 'multiple-isolated', + convertToMultiNamespaceTypeVersion: '8.0.0', + management: { + icon: 'discoverApp', + defaultSearchField: 'title', + importableAndExportable: true, + getTitle(obj) { + return obj.attributes.title; + }, + getInAppUrl(obj) { + return { + path: `/app/discover#/view/${encodeURIComponent(obj.id)}`, + uiCapabilitiesPath: 'discover.show', + }; }, - sort: { type: 'keyword', index: false, doc_values: false }, - title: { type: 'text' }, - grid: { type: 'object', enabled: false }, - version: { type: 'integer' }, - rowHeight: { type: 'text' }, }, - }, - migrations: searchMigrations, -}; + mappings: { + properties: { + columns: { type: 'keyword', index: false, doc_values: false }, + description: { type: 'text' }, + viewMode: { type: 'keyword', index: false, doc_values: false }, + hideChart: { type: 'boolean', index: false, doc_values: false }, + hideAggregatedPreview: { type: 'boolean', index: false, doc_values: false }, + hits: { type: 'integer', index: false, doc_values: false }, + kibanaSavedObjectMeta: { + properties: { + searchSourceJSON: { type: 'text', index: false }, + }, + }, + sort: { type: 'keyword', index: false, doc_values: false }, + title: { type: 'text' }, + grid: { type: 'object', enabled: false }, + version: { type: 'integer' }, + rowHeight: { type: 'text' }, + }, + }, + migrations: () => getAllMigrations(getSearchSourceMigrations()), + }; +} diff --git a/src/plugins/discover/server/saved_objects/search_migrations.test.ts b/src/plugins/discover/server/saved_objects/search_migrations.test.ts index 122371642fab..9e4c23c91976 100644 --- a/src/plugins/discover/server/saved_objects/search_migrations.test.ts +++ b/src/plugins/discover/server/saved_objects/search_migrations.test.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { SavedObjectMigrationContext } from 'kibana/server'; -import { searchMigrations } from './search_migrations'; +import { SavedObjectMigrationContext, SavedObjectUnsanitizedDoc } from 'kibana/server'; +import { getAllMigrations, searchMigrations } from './search_migrations'; const savedObjectMigrationContext = null as unknown as SavedObjectMigrationContext; @@ -350,4 +350,33 @@ Object { testMigrateMatchAllQuery(migrationFn); }); }); + it('should apply search source migrations within saved search', () => { + const savedSearch = { + attributes: { + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + some: 'prop', + migrated: false, + }), + }, + }, + } as SavedObjectUnsanitizedDoc; + + const versionToTest = '9.1.1'; + const migrations = getAllMigrations({ + // providing a function for search source migration that's just setting `migrated` to true + [versionToTest]: (state) => ({ ...state, migrated: true }), + }); + + expect(migrations[versionToTest](savedSearch, {} as SavedObjectMigrationContext)).toEqual({ + attributes: { + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + some: 'prop', + migrated: true, + }), + }, + }, + }); + }); }); diff --git a/src/plugins/discover/server/saved_objects/search_migrations.ts b/src/plugins/discover/server/saved_objects/search_migrations.ts index 5d630f782fb7..cb8ced07387a 100644 --- a/src/plugins/discover/server/saved_objects/search_migrations.ts +++ b/src/plugins/discover/server/saved_objects/search_migrations.ts @@ -8,10 +8,22 @@ // TODO: This needs to be removed and properly typed /* eslint-disable @typescript-eslint/no-explicit-any */ - -import { flow, get } from 'lodash'; -import { SavedObjectMigrationFn } from 'kibana/server'; +import { flow, get, mapValues } from 'lodash'; +import type { + SavedObjectAttributes, + SavedObjectMigrationFn, + SavedObjectMigrationMap, +} from 'kibana/server'; +import { mergeSavedObjectMigrationMaps } from '../../../../core/server'; import { DEFAULT_QUERY_LANGUAGE } from '../../../data/server'; +import { MigrateFunctionsObject, MigrateFunction } from '../../../kibana_utils/common'; +import type { SerializedSearchSourceFields } from '../../../data/common'; + +export interface SavedSearchMigrationAttributes extends SavedObjectAttributes { + kibanaSavedObjectMeta: { + searchSourceJSON: string; + }; +} /** * This migration script is related to: @@ -120,9 +132,45 @@ const migrateSearchSortToNestedArray: SavedObjectMigrationFn = (doc) = }; }; +/** + * This creates a migration map that applies search source migrations + */ +const getSearchSourceMigrations = (searchSourceMigrations: MigrateFunctionsObject) => + mapValues( + searchSourceMigrations, + (migrate: MigrateFunction): MigrateFunction => + (state) => { + const _state = state as unknown as { attributes: SavedSearchMigrationAttributes }; + + const parsedSearchSourceJSON = _state.attributes.kibanaSavedObjectMeta.searchSourceJSON; + + if (!parsedSearchSourceJSON) return _state; + + return { + ..._state, + attributes: { + ..._state.attributes, + kibanaSavedObjectMeta: { + ..._state.attributes.kibanaSavedObjectMeta, + searchSourceJSON: JSON.stringify(migrate(JSON.parse(parsedSearchSourceJSON))), + }, + }, + }; + } + ); + export const searchMigrations = { '6.7.2': flow(migrateMatchAllQuery), '7.0.0': flow(setNewReferences), '7.4.0': flow(migrateSearchSortToNestedArray), '7.9.3': flow(migrateMatchAllQuery), }; + +export const getAllMigrations = ( + searchSourceMigrations: MigrateFunctionsObject +): SavedObjectMigrationMap => { + return mergeSavedObjectMigrationMaps( + searchMigrations, + getSearchSourceMigrations(searchSourceMigrations) as unknown as SavedObjectMigrationMap + ); +};