diff --git a/package.json b/package.json index d6fa3701ceff3..8ede5c453ada5 100644 --- a/package.json +++ b/package.json @@ -664,6 +664,7 @@ "@kbn/saved-objects-settings": "link:packages/kbn-saved-objects-settings", "@kbn/saved-objects-tagging-oss-plugin": "link:src/plugins/saved_objects_tagging_oss", "@kbn/saved-objects-tagging-plugin": "link:x-pack/plugins/saved_objects_tagging", + "@kbn/saved-search": "link:packages/kbn-saved-search", "@kbn/saved-search-plugin": "link:src/plugins/saved_search", "@kbn/saved-search-so-plugin": "link:src/plugins/saved_search_so", "@kbn/screenshot-mode-example-plugin": "link:examples/screenshot_mode_example", diff --git a/packages/kbn-saved-search/README.md b/packages/kbn-saved-search/README.md new file mode 100644 index 0000000000000..c553dfda89ef8 --- /dev/null +++ b/packages/kbn-saved-search/README.md @@ -0,0 +1,3 @@ +# @kbn/saved-search + +Package for extracted saved search plugin logic diff --git a/src/plugins/saved_search_so/common/index.ts b/packages/kbn-saved-search/index.ts similarity index 76% rename from src/plugins/saved_search_so/common/index.ts rename to packages/kbn-saved-search/index.ts index 7faa35e48b305..e32d44a15edcd 100644 --- a/src/plugins/saved_search_so/common/index.ts +++ b/packages/kbn-saved-search/index.ts @@ -6,4 +6,8 @@ * Side Public License, v 1. */ -export { MIN_SAVED_SEARCH_SAMPLE_SIZE, MAX_SAVED_SEARCH_SAMPLE_SIZE, VIEW_MODE } from './constants'; +export { + MIN_SAVED_SEARCH_SAMPLE_SIZE, + MAX_SAVED_SEARCH_SAMPLE_SIZE, + VIEW_MODE, +} from './src/constants'; diff --git a/packages/kbn-saved-search/jest.config.js b/packages/kbn-saved-search/jest.config.js new file mode 100644 index 0000000000000..f67e94e6c5bdd --- /dev/null +++ b/packages/kbn-saved-search/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-saved-search'], +}; diff --git a/packages/kbn-saved-search/kibana.jsonc b/packages/kbn-saved-search/kibana.jsonc new file mode 100644 index 0000000000000..0e3270472ffb0 --- /dev/null +++ b/packages/kbn-saved-search/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/saved-search", + "owner": "@elastic/kibana-data-discovery" +} diff --git a/packages/kbn-saved-search/package.json b/packages/kbn-saved-search/package.json new file mode 100644 index 0000000000000..0e4ce3dcab3f4 --- /dev/null +++ b/packages/kbn-saved-search/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/saved-search", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": false +} \ No newline at end of file diff --git a/src/plugins/saved_search_so/common/constants.ts b/packages/kbn-saved-search/src/constants.ts similarity index 100% rename from src/plugins/saved_search_so/common/constants.ts rename to packages/kbn-saved-search/src/constants.ts diff --git a/packages/kbn-saved-search/tsconfig.json b/packages/kbn-saved-search/tsconfig.json new file mode 100644 index 0000000000000..3a4af07499b60 --- /dev/null +++ b/packages/kbn-saved-search/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["*.ts", "src/**/*", "__mocks__/**/*.ts"], + "compilerOptions": { + "outDir": "target/types" + }, + "exclude": [ + "target/**/*" + ], + "kbn_references": [] +} diff --git a/packages/kbn-saved-search/types.ts b/packages/kbn-saved-search/types.ts new file mode 100644 index 0000000000000..5a39b6cd8a030 --- /dev/null +++ b/packages/kbn-saved-search/types.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { RefreshInterval, TimeRange } from '@kbn/data-plugin/common'; +import type { SerializableRecord } from '@kbn/utility-types'; +import { VIEW_MODE } from './src/constants'; + +export interface DiscoverGridSettings extends SerializableRecord { + columns?: Record; +} + +export interface DiscoverGridSettingsColumn extends SerializableRecord { + width?: number; +} + +/** @internal **/ +export interface SavedSearchAttributes { + title: string; + sort: Array<[string, string]>; + columns: string[]; + description: string; + grid: DiscoverGridSettings; + hideChart: boolean; + isTextBasedQuery: boolean; + usesAdHocDataView?: boolean; + kibanaSavedObjectMeta: { + searchSourceJSON: string; + }; + viewMode?: VIEW_MODE; + hideAggregatedPreview?: boolean; + rowHeight?: number; + + timeRestore?: boolean; + timeRange?: Pick; + refreshInterval?: RefreshInterval; + + rowsPerPage?: number; + sampleSize?: number; + breakdownField?: string; + visContextJSON?: string; +} diff --git a/src/plugins/saved_search/common/content_management/v1/cm_services.ts b/src/plugins/saved_search/common/content_management/v1/cm_services.ts index 9eca6ff85c9fa..988d548812e62 100644 --- a/src/plugins/saved_search/common/content_management/v1/cm_services.ts +++ b/src/plugins/saved_search/common/content_management/v1/cm_services.ts @@ -15,10 +15,7 @@ import { updateOptionsSchema, createResultSchema, } from '@kbn/content-management-utils'; -import { - MIN_SAVED_SEARCH_SAMPLE_SIZE, - MAX_SAVED_SEARCH_SAMPLE_SIZE, -} from '@kbn/saved-search-so-plugin/common'; +import { MIN_SAVED_SEARCH_SAMPLE_SIZE, MAX_SAVED_SEARCH_SAMPLE_SIZE } from '@kbn/saved-search'; const sortSchema = schema.arrayOf(schema.string(), { maxSize: 2 }); diff --git a/src/plugins/saved_search/common/index.ts b/src/plugins/saved_search/common/index.ts index 47ed071e7b085..96067f3054c41 100644 --- a/src/plugins/saved_search/common/index.ts +++ b/src/plugins/saved_search/common/index.ts @@ -10,16 +10,16 @@ export { VIEW_MODE, MIN_SAVED_SEARCH_SAMPLE_SIZE, MAX_SAVED_SEARCH_SAMPLE_SIZE, -} from '@kbn/saved-search-so-plugin/common'; -export { getSavedSearchUrl, getSavedSearchFullPathUrl } from './saved_searches_url'; -export { fromSavedSearchAttributes } from './saved_searches_utils'; - +} from '@kbn/saved-search'; export type { DiscoverGridSettings, DiscoverGridSettingsColumn, - SavedSearch, SavedSearchAttributes, -} from './types'; +} from '@kbn/saved-search/types'; +export { getSavedSearchUrl, getSavedSearchFullPathUrl } from './saved_searches_url'; +export { fromSavedSearchAttributes } from './saved_searches_utils'; + +export type { SavedSearch } from './types'; export { SavedSearchType, LATEST_VERSION } from './constants'; export { getKibanaContextFn } from './expressions/kibana_context'; diff --git a/src/plugins/saved_search/common/types.ts b/src/plugins/saved_search/common/types.ts index 7ea9ca5d5eade..cd67177a80ba9 100644 --- a/src/plugins/saved_search/common/types.ts +++ b/src/plugins/saved_search/common/types.ts @@ -9,43 +9,8 @@ import type { ISearchSource, RefreshInterval, TimeRange } from '@kbn/data-plugin/common'; import type { SavedObjectReference } from '@kbn/core-saved-objects-server'; import type { SavedObjectsResolveResponse } from '@kbn/core/server'; -import type { SerializableRecord } from '@kbn/utility-types'; -import { VIEW_MODE } from '.'; - -export interface DiscoverGridSettings extends SerializableRecord { - columns?: Record; -} - -export interface DiscoverGridSettingsColumn extends SerializableRecord { - width?: number; -} - -/** @internal **/ -export interface SavedSearchAttributes { - title: string; - sort: Array<[string, string]>; - columns: string[]; - description: string; - grid: DiscoverGridSettings; - hideChart: boolean; - isTextBasedQuery: boolean; - usesAdHocDataView?: boolean; - kibanaSavedObjectMeta: { - searchSourceJSON: string; - }; - viewMode?: VIEW_MODE; - hideAggregatedPreview?: boolean; - rowHeight?: number; - - timeRestore?: boolean; - timeRange?: Pick; - refreshInterval?: RefreshInterval; - - rowsPerPage?: number; - sampleSize?: number; - breakdownField?: string; - visContextJSON?: string; -} +import { VIEW_MODE } from '@kbn/saved-search'; +import { DiscoverGridSettings } from '@kbn/saved-search/types'; /** @internal **/ export type SortOrder = [string, string]; diff --git a/src/plugins/saved_search_so/server/plugin.ts b/src/plugins/saved_search_so/server/plugin.ts index 51ef3566d8530..faa00601bb5f4 100644 --- a/src/plugins/saved_search_so/server/plugin.ts +++ b/src/plugins/saved_search_so/server/plugin.ts @@ -8,6 +8,7 @@ import { CoreSetup, Plugin } from '@kbn/core/server'; import type { PluginSetup as DataPluginSetup } from '@kbn/data-plugin/server'; +import type { LensServerPluginSetup } from '@kbn/lens-plugin/server'; import { getSavedSearchObjectType } from './saved_objects'; /** @@ -15,16 +16,22 @@ import { getSavedSearchObjectType } from './saved_objects'; */ export interface SavedSearchSOPublicSetupDependencies { data: DataPluginSetup; + lens: LensServerPluginSetup; } export class SavedSearchSOServerPlugin implements Plugin { constructor() {} - public setup(core: CoreSetup, { data }: SavedSearchSOPublicSetupDependencies) { + public setup(core: CoreSetup, { data, lens }: SavedSearchSOPublicSetupDependencies) { const searchSource = data.search.searchSource; const getSearchSourceMigrations = searchSource.getAllMigrations.bind(searchSource); - core.savedObjects.registerType(getSavedSearchObjectType(getSearchSourceMigrations)); + core.savedObjects.registerType( + getSavedSearchObjectType({ + getSearchSourceMigrations, + lensEmbeddableFactory: lens.lensEmbeddableFactory, + }) + ); return {}; } diff --git a/src/plugins/saved_search_so/server/saved_objects/schema.ts b/src/plugins/saved_search_so/server/saved_objects/schema.ts index 13efbe102d2bf..d6d3fb0739b36 100644 --- a/src/plugins/saved_search_so/server/saved_objects/schema.ts +++ b/src/plugins/saved_search_so/server/saved_objects/schema.ts @@ -11,7 +11,7 @@ import { MIN_SAVED_SEARCH_SAMPLE_SIZE, MAX_SAVED_SEARCH_SAMPLE_SIZE, VIEW_MODE, -} from '../../common'; +} from '@kbn/saved-search'; const SCHEMA_SEARCH_BASE = { // General diff --git a/src/plugins/saved_search_so/server/saved_objects/search.ts b/src/plugins/saved_search_so/server/saved_objects/search.ts index d5a5793d2ff0a..b2521b283b4cf 100644 --- a/src/plugins/saved_search_so/server/saved_objects/search.ts +++ b/src/plugins/saved_search_so/server/saved_objects/search.ts @@ -9,12 +9,17 @@ import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { SavedObjectsType } from '@kbn/core/server'; import { MigrateFunctionsObject } from '@kbn/kibana-utils-plugin/common'; +import type { LensServerPluginSetup } from '@kbn/lens-plugin/server'; import { getAllMigrations } from './search_migrations'; import { SCHEMA_SEARCH_V8_8_0, SCHEMA_SEARCH_V8_12_0, SCHEMA_SEARCH_V8_13_0 } from './schema'; -export function getSavedSearchObjectType( - getSearchSourceMigrations: () => MigrateFunctionsObject -): SavedObjectsType { +export function getSavedSearchObjectType({ + getSearchSourceMigrations, + lensEmbeddableFactory, +}: { + getSearchSourceMigrations: () => MigrateFunctionsObject; + lensEmbeddableFactory: LensServerPluginSetup['lensEmbeddableFactory']; +}): SavedObjectsType { return { name: 'search', indexPattern: ANALYTICS_SAVED_OBJECT_INDEX, @@ -47,6 +52,10 @@ export function getSavedSearchObjectType( '8.12.0': SCHEMA_SEARCH_V8_12_0, '8.13.0': SCHEMA_SEARCH_V8_13_0, }, - migrations: () => getAllMigrations(getSearchSourceMigrations()), // TODO: add lens embeddable migrations for `visContextJSON` + migrations: () => + getAllMigrations({ + searchSourceMigrations: getSearchSourceMigrations(), + lensEmbeddableFactory, + }), }; } diff --git a/src/plugins/saved_search_so/server/saved_objects/search_migrations.ts b/src/plugins/saved_search_so/server/saved_objects/search_migrations.ts index 6d327db994451..1a1787fd46aa3 100644 --- a/src/plugins/saved_search_so/server/saved_objects/search_migrations.ts +++ b/src/plugins/saved_search_so/server/saved_objects/search_migrations.ts @@ -16,6 +16,8 @@ import { mergeSavedObjectMigrationMaps } from '@kbn/core/server'; import { DEFAULT_QUERY_LANGUAGE } from '@kbn/data-plugin/server'; import { MigrateFunctionsObject, MigrateFunction } from '@kbn/kibana-utils-plugin/common'; import { isSerializedSearchSource, SerializedSearchSourceFields } from '@kbn/data-plugin/common'; +import type { LensServerPluginSetup } from '@kbn/lens-plugin/server'; +import { getLensVisContextMigrations } from './search_vis_migrations'; export interface SavedSearchMigrationAttributes extends SavedObjectAttributes { kibanaSavedObjectMeta: { @@ -168,11 +170,18 @@ export const searchMigrations = { '7.9.3': flow(migrateMatchAllQuery), }; -export const getAllMigrations = ( - searchSourceMigrations: MigrateFunctionsObject -): SavedObjectMigrationMap => { +export const getAllMigrations = ({ + searchSourceMigrations, + lensEmbeddableFactory, +}: { + searchSourceMigrations: MigrateFunctionsObject; + lensEmbeddableFactory: LensServerPluginSetup['lensEmbeddableFactory']; +}): SavedObjectMigrationMap => { return mergeSavedObjectMigrationMaps( - searchMigrations, - getSearchSourceMigrations(searchSourceMigrations) as SavedObjectMigrationMap + mergeSavedObjectMigrationMaps( + searchMigrations, + getSearchSourceMigrations(searchSourceMigrations) as SavedObjectMigrationMap + ), + getLensVisContextMigrations(lensEmbeddableFactory) ); }; diff --git a/src/plugins/saved_search_so/server/saved_objects/search_vis_migrations.ts b/src/plugins/saved_search_so/server/saved_objects/search_vis_migrations.ts new file mode 100644 index 0000000000000..8e9d9690d8c4c --- /dev/null +++ b/src/plugins/saved_search_so/server/saved_objects/search_vis_migrations.ts @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { isFunction, mapValues } from 'lodash'; +import type { SavedObjectMigrationMap } from '@kbn/core-saved-objects-server'; +import type { LensServerPluginSetup } from '@kbn/lens-plugin/server'; +import { LogMeta } from '@kbn/logging'; +import { MigrateFunction, MigrateFunctionsObject } from '@kbn/kibana-utils-plugin/common'; +import { + SavedObjectMigrationContext, + SavedObjectMigrationParams, + SavedObjectSanitizedDoc, + SavedObjectUnsanitizedDoc, +} from '@kbn/core-saved-objects-server'; +import type { SavedSearchAttributes } from '@kbn/saved-search/types'; + +export const getLensVisContextMigrations = ( + lensEmbeddableFactory: LensServerPluginSetup['lensEmbeddableFactory'] +): SavedObjectMigrationMap => { + return getLensMigrations({ + lensEmbeddableFactory, + migratorFactory: migrateByValueLensVisualizations, + }); +}; + +function migrateByValueLensVisualizations( + migrate: MigrateFunction +): SavedObjectMigrationParams { + return { + deferred: false, + transform: ( + doc: SavedObjectUnsanitizedDoc, + context: SavedObjectMigrationContext + ): SavedObjectSanitizedDoc => { + if (doc.attributes.visContextJSON) { + return migrateLensVisContext({ migrate, doc, context }); + } + + return Object.assign(doc, { references: doc.references ?? [] }); + }, + }; +} + +interface MigrationLogMeta extends LogMeta { + migrations: { + [x: string]: { + id: string; + }; + }; +} + +function migrateLensVisContext({ + migrate, + doc, + context, +}: { + migrate: MigrateFunction; + doc: SavedObjectUnsanitizedDoc; + context: SavedObjectMigrationContext; +}): SavedObjectSanitizedDoc { + try { + const { visContextJSON } = doc.attributes; + const parsedVisContext = visContextJSON ? JSON.parse(visContextJSON) : undefined; + + return { + ...doc, + attributes: { + ...doc.attributes, + visContextJSON: + parsedVisContext && parsedVisContext.attributes + ? JSON.stringify({ + ...parsedVisContext, + attributes: migrate(parsedVisContext.attributes), + }) + : undefined, + }, + references: doc.references ?? [], + }; + } catch (error) { + context.log.error( + `Failed to migrate "search" with doc id: ${doc.id} version: ${context.migrationVersion} error: ${error.message}`, + { + migrations: { + visContextJSON: { + id: doc.id, + }, + }, + } + ); + + return Object.assign(doc, { references: doc.references ?? [] }); + } +} + +interface GetLensMigrationsArgs { + lensEmbeddableFactory: LensServerPluginSetup['lensEmbeddableFactory']; + migratorFactory: ( + migrate: MigrateFunction, + migrationVersion: string + ) => SavedObjectMigrationParams; +} + +function getLensMigrations({ + lensEmbeddableFactory, + migratorFactory, +}: GetLensMigrationsArgs) { + const lensMigrations = lensEmbeddableFactory().migrations; + const lensMigrationObject = isFunction(lensMigrations) ? lensMigrations() : lensMigrations || {}; + + const embeddableMigrations = mapValues>( + lensMigrationObject, + migratorFactory + ); + + return embeddableMigrations; +} diff --git a/tsconfig.base.json b/tsconfig.base.json index 2a92d90d8e94d..70cdb91a3b6f9 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1316,6 +1316,8 @@ "@kbn/saved-objects-tagging-oss-plugin/*": ["src/plugins/saved_objects_tagging_oss/*"], "@kbn/saved-objects-tagging-plugin": ["x-pack/plugins/saved_objects_tagging"], "@kbn/saved-objects-tagging-plugin/*": ["x-pack/plugins/saved_objects_tagging/*"], + "@kbn/saved-search": ["packages/kbn-saved-search"], + "@kbn/saved-search/*": ["packages/kbn-saved-search/*"], "@kbn/saved-search-plugin": ["src/plugins/saved_search"], "@kbn/saved-search-plugin/*": ["src/plugins/saved_search/*"], "@kbn/saved-search-so-plugin": ["src/plugins/saved_search_so"], diff --git a/yarn.lock b/yarn.lock index c79a82b54eaa9..eb164ac7438df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5704,6 +5704,10 @@ version "0.0.0" uid "" +"@kbn/saved-search@link:packages/kbn-saved-search": + version "0.0.0" + uid "" + "@kbn/screenshot-mode-example-plugin@link:examples/screenshot_mode_example": version "0.0.0" uid ""