From 7c2edab6ac19254f9ff4e61881e71deb48809d0c Mon Sep 17 00:00:00 2001 From: Rudolf Meijering Date: Fri, 4 Feb 2022 17:05:56 +0100 Subject: [PATCH 1/3] Move SavedObjectLoader into the dashboard plugin --- .../public/services/saved_objects.ts | 2 +- .../dashboard/public/url_generator.test.ts | 2 +- src/plugins/dashboard/public/url_generator.ts | 2 +- src/plugins/saved_objects/public/index.ts | 8 +- .../saved_object/helpers/string_utils.test.ts | 22 --- .../saved_object/helpers/string_utils.ts | 18 -- .../public/saved_object/index.ts | 2 - .../saved_object/saved_object_loader.ts | 168 ------------------ 8 files changed, 4 insertions(+), 220 deletions(-) delete mode 100644 src/plugins/saved_objects/public/saved_object/helpers/string_utils.test.ts delete mode 100644 src/plugins/saved_objects/public/saved_object/helpers/string_utils.ts delete mode 100644 src/plugins/saved_objects/public/saved_object/saved_object_loader.ts diff --git a/src/plugins/dashboard/public/services/saved_objects.ts b/src/plugins/dashboard/public/services/saved_objects.ts index 305ff3c2014f8..fa6b6256bcac1 100644 --- a/src/plugins/dashboard/public/services/saved_objects.ts +++ b/src/plugins/dashboard/public/services/saved_objects.ts @@ -15,7 +15,7 @@ export type { } from '../../../saved_objects/public'; export { showSaveModal, - SavedObjectLoader, SavedObjectSaveModal, getSavedObjectFinder, } from '../../../saved_objects/public'; +export { SavedObjectLoader } from './saved_object_loader'; diff --git a/src/plugins/dashboard/public/url_generator.test.ts b/src/plugins/dashboard/public/url_generator.test.ts index 9a1204f116c7f..9e5a0eb1795c8 100644 --- a/src/plugins/dashboard/public/url_generator.test.ts +++ b/src/plugins/dashboard/public/url_generator.test.ts @@ -10,7 +10,7 @@ import { createDashboardUrlGenerator } from './url_generator'; import { hashedItemStore } from '../../kibana_utils/public'; import { mockStorage } from '../../kibana_utils/public/storage/hashed_item_store/mock'; import { esFilters, Filter } from '../../data/public'; -import { SavedObjectLoader } from '../../saved_objects/public'; +import { SavedObjectLoader } from './services/saved_objects'; const APP_BASE_PATH: string = 'xyz/app/dashboards'; diff --git a/src/plugins/dashboard/public/url_generator.ts b/src/plugins/dashboard/public/url_generator.ts index 5c0cd32ee5a16..96b402fbffb66 100644 --- a/src/plugins/dashboard/public/url_generator.ts +++ b/src/plugins/dashboard/public/url_generator.ts @@ -16,7 +16,7 @@ import { } from '../../data/public'; import { setStateToKbnUrl } from '../../kibana_utils/public'; import { UrlGeneratorsDefinition } from '../../share/public'; -import { SavedObjectLoader } from '../../saved_objects/public'; +import { SavedObjectLoader } from './services/saved_object_loader'; import { ViewMode } from '../../embeddable/public'; import { DashboardConstants } from './dashboard_constants'; import { SavedDashboardPanel } from '../common/types'; diff --git a/src/plugins/saved_objects/public/index.ts b/src/plugins/saved_objects/public/index.ts index d63e20f5f5673..5e6e67f7e4acc 100644 --- a/src/plugins/saved_objects/public/index.ts +++ b/src/plugins/saved_objects/public/index.ts @@ -13,17 +13,11 @@ export { SavedObjectSaveModal, SavedObjectSaveModalOrigin, showSaveModal } from export type { SavedObjectFinderUiProps, SavedObjectMetaData } from './finder'; export { getSavedObjectFinder, SavedObjectFinderUi } from './finder'; export type { - SavedObjectLoaderFindOptions, SavedObjectDecorator, SavedObjectDecoratorFactory, SavedObjectDecoratorConfig, } from './saved_object'; -export { - SavedObjectLoader, - checkForDuplicateTitle, - saveWithConfirmation, - isErrorNonFatal, -} from './saved_object'; +export { checkForDuplicateTitle, saveWithConfirmation, isErrorNonFatal } from './saved_object'; export type { SavedObjectSaveOpts, SavedObject, SavedObjectConfig } from './types'; export { PER_PAGE_SETTING, LISTING_LIMIT_SETTING } from '../common'; export type { SavedObjectsStart, SavedObjectSetup } from './plugin'; diff --git a/src/plugins/saved_objects/public/saved_object/helpers/string_utils.test.ts b/src/plugins/saved_objects/public/saved_object/helpers/string_utils.test.ts deleted file mode 100644 index 4e7258f1575dc..0000000000000 --- a/src/plugins/saved_objects/public/saved_object/helpers/string_utils.test.ts +++ /dev/null @@ -1,22 +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 - * 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 { StringUtils } from './string_utils'; - -describe('StringUtils class', () => { - describe('static upperFirst', () => { - test('should converts the first character of string to upper case', () => { - expect(StringUtils.upperFirst()).toBe(''); - expect(StringUtils.upperFirst('')).toBe(''); - - expect(StringUtils.upperFirst('Fred')).toBe('Fred'); - expect(StringUtils.upperFirst('fred')).toBe('Fred'); - expect(StringUtils.upperFirst('FRED')).toBe('FRED'); - }); - }); -}); diff --git a/src/plugins/saved_objects/public/saved_object/helpers/string_utils.ts b/src/plugins/saved_objects/public/saved_object/helpers/string_utils.ts deleted file mode 100644 index 5d4551f0c200c..0000000000000 --- a/src/plugins/saved_objects/public/saved_object/helpers/string_utils.ts +++ /dev/null @@ -1,18 +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 - * 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. - */ - -export class StringUtils { - /** - * Returns a version of the string with the first letter capitalized. - * @param str {string} - * @returns {string} - */ - public static upperFirst(str: string = ''): string { - return str ? str.charAt(0).toUpperCase() + str.slice(1) : ''; - } -} diff --git a/src/plugins/saved_objects/public/saved_object/index.ts b/src/plugins/saved_objects/public/saved_object/index.ts index f30730a1c39ac..01770866b65ed 100644 --- a/src/plugins/saved_objects/public/saved_object/index.ts +++ b/src/plugins/saved_objects/public/saved_object/index.ts @@ -7,8 +7,6 @@ */ export { createSavedObjectClass } from './saved_object'; -export type { SavedObjectLoaderFindOptions } from './saved_object_loader'; -export { SavedObjectLoader } from './saved_object_loader'; export { checkForDuplicateTitle } from './helpers/check_for_duplicate_title'; export { saveWithConfirmation } from './helpers/save_with_confirmation'; export { isErrorNonFatal } from './helpers/save_saved_object'; diff --git a/src/plugins/saved_objects/public/saved_object/saved_object_loader.ts b/src/plugins/saved_objects/public/saved_object/saved_object_loader.ts deleted file mode 100644 index 10872b5d9cd1a..0000000000000 --- a/src/plugins/saved_objects/public/saved_object/saved_object_loader.ts +++ /dev/null @@ -1,168 +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 - * 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 { - SavedObjectsClientContract, - SavedObjectsFindOptions, - SavedObjectsFindOptionsReference, - SavedObjectReference, -} from 'kibana/public'; -import { SavedObject } from '../types'; -import { StringUtils } from './helpers/string_utils'; - -export interface SavedObjectLoaderFindOptions { - size?: number; - fields?: string[]; - hasReference?: SavedObjectsFindOptionsReference[]; -} - -/** - * @deprecated - * The SavedObjectLoader class provides some convenience functions - * to load and save one kind of saved objects (specified in the constructor). - * - * It is based on the SavedObjectClient which implements loading and saving - * in an abstract, type-agnostic way. If possible, use SavedObjectClient directly - * to avoid pulling in extra functionality which isn't used. - */ -export class SavedObjectLoader { - private readonly Class: (id: string) => SavedObject; - public type: string; - public lowercaseType: string; - public loaderProperties: Record; - - constructor( - SavedObjectClass: any, - private readonly savedObjectsClient: SavedObjectsClientContract - ) { - this.type = SavedObjectClass.type; - this.Class = SavedObjectClass; - this.lowercaseType = this.type.toLowerCase(); - - this.loaderProperties = { - name: `${this.lowercaseType}s`, - noun: StringUtils.upperFirst(this.type), - nouns: `${this.lowercaseType}s`, - }; - } - - /** - * Retrieve a saved object by id or create new one. - * Returns a promise that completes when the object finishes - * initializing. - * @param opts - * @returns {Promise} - */ - async get(opts?: Record | string) { - // can accept object as argument in accordance to SavedVis class - // see src/plugins/saved_objects/public/saved_object/saved_object_loader.ts - // @ts-ignore - const obj = new this.Class(opts); - return obj.init(); - } - - urlFor(id: string) { - return `#/${this.lowercaseType}/${encodeURIComponent(id)}`; - } - - async delete(ids: string | string[]) { - const idsUsed = !Array.isArray(ids) ? [ids] : ids; - - const deletions = idsUsed.map((id) => { - // @ts-ignore - const savedObject = new this.Class(id); - return savedObject.delete(); - }); - await Promise.all(deletions); - } - - /** - * Updates source to contain an id, url and references fields, and returns the updated - * source object. - * @param source - * @param id - * @param references - * @returns {source} The modified source object, with an id and url field. - */ - mapHitSource( - source: Record, - id: string, - references: SavedObjectReference[] = [] - ) { - source.id = id; - source.url = this.urlFor(id); - source.references = references; - return source; - } - - /** - * Updates hit.attributes to contain an id and url field, and returns the updated - * attributes object. - * @param hit - * @returns {hit.attributes} The modified hit.attributes object, with an id and url field. - */ - mapSavedObjectApiHits({ - attributes, - id, - references = [], - }: { - attributes: Record; - id: string; - references?: SavedObjectReference[]; - }) { - return this.mapHitSource(attributes, id, references); - } - - /** - * TODO: Rather than use a hardcoded limit, implement pagination. See - * https://github.com/elastic/kibana/issues/8044 for reference. - * - * @param search - * @param size - * @param fields - * @returns {Promise} - */ - private findAll( - search: string = '', - { size = 100, fields, hasReference }: SavedObjectLoaderFindOptions - ) { - return this.savedObjectsClient - .find>({ - type: this.lowercaseType, - search: search ? `${search}*` : undefined, - perPage: size, - page: 1, - searchFields: ['title^3', 'description'], - defaultSearchOperator: 'AND', - fields, - hasReference, - } as SavedObjectsFindOptions) - .then((resp) => { - return { - total: resp.total, - hits: resp.savedObjects.map((savedObject) => this.mapSavedObjectApiHits(savedObject)), - }; - }); - } - - find(search: string = '', sizeOrOptions: number | SavedObjectLoaderFindOptions = 100) { - const options: SavedObjectLoaderFindOptions = - typeof sizeOrOptions === 'number' - ? { - size: sizeOrOptions, - } - : sizeOrOptions; - - return this.findAll(search, options).then((resp) => { - return { - total: resp.total, - hits: resp.hits.filter((savedObject) => !savedObject.error), - }; - }); - } -} From 04782fbdf32d7fe4a37356177b66850cef4c14e3 Mon Sep 17 00:00:00 2001 From: Rudolf Meijering Date: Fri, 4 Feb 2022 17:06:56 +0100 Subject: [PATCH 2/3] Remove old comment recommending savedobjectloader --- .../public/services/saved_object_loader.ts | 174 ++++++++++++++++++ .../public/services/string_utils.test.ts | 22 +++ .../dashboard/public/services/string_utils.ts | 16 ++ src/plugins/saved_objects/public/mocks.ts | 1 - src/plugins/saved_objects/public/plugin.ts | 1 - .../public/saved_object/saved_object.ts | 2 - 6 files changed, 212 insertions(+), 4 deletions(-) create mode 100644 src/plugins/dashboard/public/services/saved_object_loader.ts create mode 100644 src/plugins/dashboard/public/services/string_utils.test.ts create mode 100644 src/plugins/dashboard/public/services/string_utils.ts diff --git a/src/plugins/dashboard/public/services/saved_object_loader.ts b/src/plugins/dashboard/public/services/saved_object_loader.ts new file mode 100644 index 0000000000000..521d6645e30f9 --- /dev/null +++ b/src/plugins/dashboard/public/services/saved_object_loader.ts @@ -0,0 +1,174 @@ +/* + * 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 { + SavedObjectsClientContract, + SavedObjectsFindOptions, + SavedObjectsFindOptionsReference, + SavedObjectReference, +} from 'kibana/public'; +import { SavedObject } from '../../../saved_objects/public'; +import { upperFirst } from './string_utils'; + +/** + * @deprecated + * @removeBy 8.0 + */ +export interface SavedObjectLoaderFindOptions { + size?: number; + fields?: string[]; + hasReference?: SavedObjectsFindOptionsReference[]; +} + +/** + * @deprecated + * @removeBy 8.0 + * + * The SavedObjectLoader class provides some convenience functions + * to load and save one kind of saved objects (specified in the constructor). + * + * It is based on the SavedObjectClient which implements loading and saving + * in an abstract, type-agnostic way. If possible, use SavedObjectClient directly + * to avoid pulling in extra functionality which isn't used. + */ +export class SavedObjectLoader { + private readonly Class: (id: string) => SavedObject; + public type: string; + public lowercaseType: string; + public loaderProperties: Record; + + constructor( + SavedObjectClass: any, + private readonly savedObjectsClient: SavedObjectsClientContract + ) { + this.type = SavedObjectClass.type; + this.Class = SavedObjectClass; + this.lowercaseType = this.type.toLowerCase(); + + this.loaderProperties = { + name: `${this.lowercaseType}s`, + noun: upperFirst(this.type), + nouns: `${this.lowercaseType}s`, + }; + } + + /** + * Retrieve a saved object by id or create new one. + * Returns a promise that completes when the object finishes + * initializing. + * @param opts + * @returns {Promise} + */ + async get(opts?: Record | string) { + // can accept object as argument in accordance to SavedVis class + // see src/plugins/saved_objects/public/saved_object/saved_object_loader.ts + // @ts-ignore + const obj = new this.Class(opts); + return obj.init(); + } + + urlFor(id: string) { + return `#/${this.lowercaseType}/${encodeURIComponent(id)}`; + } + + async delete(ids: string | string[]) { + const idsUsed = !Array.isArray(ids) ? [ids] : ids; + + const deletions = idsUsed.map((id) => { + // @ts-ignore + const savedObject = new this.Class(id); + return savedObject.delete(); + }); + await Promise.all(deletions); + } + + /** + * Updates source to contain an id, url and references fields, and returns the updated + * source object. + * @param source + * @param id + * @param references + * @returns {source} The modified source object, with an id and url field. + */ + mapHitSource( + source: Record, + id: string, + references: SavedObjectReference[] = [] + ) { + source.id = id; + source.url = this.urlFor(id); + source.references = references; + return source; + } + + /** + * Updates hit.attributes to contain an id and url field, and returns the updated + * attributes object. + * @param hit + * @returns {hit.attributes} The modified hit.attributes object, with an id and url field. + */ + mapSavedObjectApiHits({ + attributes, + id, + references = [], + }: { + attributes: Record; + id: string; + references?: SavedObjectReference[]; + }) { + return this.mapHitSource(attributes, id, references); + } + + /** + * TODO: Rather than use a hardcoded limit, implement pagination. See + * https://github.com/elastic/kibana/issues/8044 for reference. + * + * @param search + * @param size + * @param fields + * @returns {Promise} + */ + private findAll( + search: string = '', + { size = 100, fields, hasReference }: SavedObjectLoaderFindOptions + ) { + return this.savedObjectsClient + .find>({ + type: this.lowercaseType, + search: search ? `${search}*` : undefined, + perPage: size, + page: 1, + searchFields: ['title^3', 'description'], + defaultSearchOperator: 'AND', + fields, + hasReference, + } as SavedObjectsFindOptions) + .then((resp) => { + return { + total: resp.total, + hits: resp.savedObjects.map((savedObject) => this.mapSavedObjectApiHits(savedObject)), + }; + }); + } + + find(search: string = '', sizeOrOptions: number | SavedObjectLoaderFindOptions = 100) { + const options: SavedObjectLoaderFindOptions = + typeof sizeOrOptions === 'number' + ? { + size: sizeOrOptions, + } + : sizeOrOptions; + + return this.findAll(search, options).then((resp) => { + return { + total: resp.total, + hits: resp.hits.filter((savedObject) => !savedObject.error), + }; + }); + } +} diff --git a/src/plugins/dashboard/public/services/string_utils.test.ts b/src/plugins/dashboard/public/services/string_utils.test.ts new file mode 100644 index 0000000000000..ed96cb4f1a0a1 --- /dev/null +++ b/src/plugins/dashboard/public/services/string_utils.test.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 + * 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 { upperFirst } from './string_utils'; + +describe('StringUtils', () => { + describe('upperFirst', () => { + test('should converts the first character of string to upper case', () => { + expect(upperFirst()).toBe(''); + expect(upperFirst('')).toBe(''); + + expect(upperFirst('Fred')).toBe('Fred'); + expect(upperFirst('fred')).toBe('Fred'); + expect(upperFirst('FRED')).toBe('FRED'); + }); + }); +}); diff --git a/src/plugins/dashboard/public/services/string_utils.ts b/src/plugins/dashboard/public/services/string_utils.ts new file mode 100644 index 0000000000000..31a36b38155d7 --- /dev/null +++ b/src/plugins/dashboard/public/services/string_utils.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ + +/** + * Returns a version of the string with the first letter capitalized. + * @param str {string} + * @returns {string} + */ +export function upperFirst(str: string = ''): string { + return str ? str.charAt(0).toUpperCase() + str.slice(1) : ''; +} diff --git a/src/plugins/saved_objects/public/mocks.ts b/src/plugins/saved_objects/public/mocks.ts index 1a78c725de69e..e0fe863dc06b4 100644 --- a/src/plugins/saved_objects/public/mocks.ts +++ b/src/plugins/saved_objects/public/mocks.ts @@ -10,7 +10,6 @@ import { SavedObjectsStart, SavedObjectSetup } from './plugin'; const createStartContract = (): SavedObjectsStart => { return { - SavedObjectClass: jest.fn(), settings: { getPerPage: () => 20, getListingLimit: () => 100, diff --git a/src/plugins/saved_objects/public/plugin.ts b/src/plugins/saved_objects/public/plugin.ts index b75ff043cee8d..aa769a4526bf2 100644 --- a/src/plugins/saved_objects/public/plugin.ts +++ b/src/plugins/saved_objects/public/plugin.ts @@ -17,7 +17,6 @@ import { import { DataPublicPluginStart } from '../../data/public'; import { DataViewsPublicPluginStart } from '../../data_views/public'; import { PER_PAGE_SETTING, LISTING_LIMIT_SETTING } from '../common'; -import { SavedObject } from './types'; export interface SavedObjectSetup { registerDecorator: (config: SavedObjectDecoratorConfig) => void; diff --git a/src/plugins/saved_objects/public/saved_object/saved_object.ts b/src/plugins/saved_objects/public/saved_object/saved_object.ts index c038669510b78..a437431a9d827 100644 --- a/src/plugins/saved_objects/public/saved_object/saved_object.ts +++ b/src/plugins/saved_objects/public/saved_object/saved_object.ts @@ -29,8 +29,6 @@ export function createSavedObjectClass( * provides additional functionality besides loading/saving/deleting/etc. * * It is overloaded and configured to provide type-aware functionality. - * To just retrieve the attributes of saved objects, it is recommended to use SavedObjectLoader - * which returns instances of SimpleSavedObject which don't introduce additional type-specific complexity. * @param {*} config */ class SavedObjectClass { From ce8d0046ecda6fbfd90e61bcb653925a0987da7d Mon Sep 17 00:00:00 2001 From: Rudolf Meijering Date: Mon, 7 Feb 2022 16:20:52 +0100 Subject: [PATCH 3/3] Fix types --- src/plugins/dashboard/public/services/saved_objects.ts | 2 +- src/plugins/saved_objects/public/mocks.ts | 1 + src/plugins/saved_objects/public/plugin.ts | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/dashboard/public/services/saved_objects.ts b/src/plugins/dashboard/public/services/saved_objects.ts index fa6b6256bcac1..afd778d78b271 100644 --- a/src/plugins/dashboard/public/services/saved_objects.ts +++ b/src/plugins/dashboard/public/services/saved_objects.ts @@ -11,7 +11,6 @@ export type { SavedObject, SavedObjectsStart, SavedObjectSaveOpts, - SavedObjectLoaderFindOptions, } from '../../../saved_objects/public'; export { showSaveModal, @@ -19,3 +18,4 @@ export { getSavedObjectFinder, } from '../../../saved_objects/public'; export { SavedObjectLoader } from './saved_object_loader'; +export type { SavedObjectLoaderFindOptions } from './saved_object_loader'; diff --git a/src/plugins/saved_objects/public/mocks.ts b/src/plugins/saved_objects/public/mocks.ts index e0fe863dc06b4..1a78c725de69e 100644 --- a/src/plugins/saved_objects/public/mocks.ts +++ b/src/plugins/saved_objects/public/mocks.ts @@ -10,6 +10,7 @@ import { SavedObjectsStart, SavedObjectSetup } from './plugin'; const createStartContract = (): SavedObjectsStart => { return { + SavedObjectClass: jest.fn(), settings: { getPerPage: () => 20, getListingLimit: () => 100, diff --git a/src/plugins/saved_objects/public/plugin.ts b/src/plugins/saved_objects/public/plugin.ts index aa769a4526bf2..b75ff043cee8d 100644 --- a/src/plugins/saved_objects/public/plugin.ts +++ b/src/plugins/saved_objects/public/plugin.ts @@ -17,6 +17,7 @@ import { import { DataPublicPluginStart } from '../../data/public'; import { DataViewsPublicPluginStart } from '../../data_views/public'; import { PER_PAGE_SETTING, LISTING_LIMIT_SETTING } from '../common'; +import { SavedObject } from './types'; export interface SavedObjectSetup { registerDecorator: (config: SavedObjectDecoratorConfig) => void;