From db5af58351199b5255cd9714e24abb1c111b5deb Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Mon, 2 Mar 2020 16:34:39 +0100 Subject: [PATCH 01/20] unexpose SavedObjectsManagement from legacy server --- src/core/server/saved_objects/service/index.ts | 1 + src/legacy/core_plugins/kibana/inject_vars.js | 6 +----- src/legacy/server/kbn_server.d.ts | 4 ---- .../server/saved_objects/saved_objects_mixin.js | 12 ++++++------ .../server/saved_objects/saved_objects_mixin.test.js | 2 +- 5 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/core/server/saved_objects/service/index.ts b/src/core/server/saved_objects/service/index.ts index 9f625b4732e26..f44824238aa21 100644 --- a/src/core/server/saved_objects/service/index.ts +++ b/src/core/server/saved_objects/service/index.ts @@ -36,6 +36,7 @@ export interface SavedObjectsLegacyService { getScopedSavedObjectsClient: SavedObjectsClientProvider['getClient']; SavedObjectsClient: typeof SavedObjectsClient; types: string[]; + importAndExportableTypes: string[]; schema: SavedObjectsSchema; getSavedObjectsRepository(...rest: any[]): any; importExport: { diff --git a/src/legacy/core_plugins/kibana/inject_vars.js b/src/legacy/core_plugins/kibana/inject_vars.js index 01623341e4d38..c26c1966f8e66 100644 --- a/src/legacy/core_plugins/kibana/inject_vars.js +++ b/src/legacy/core_plugins/kibana/inject_vars.js @@ -21,11 +21,7 @@ export function injectVars(server) { const serverConfig = server.config(); // Get types that are import and exportable, by default yes unless isImportableAndExportable is set to false - const { types: allTypes } = server.savedObjects; - const savedObjectsManagement = server.getSavedObjectsManagement(); - const importAndExportableTypes = allTypes.filter(type => - savedObjectsManagement.isImportAndExportable(type) - ); + const { importAndExportableTypes } = server.savedObjects; return { importAndExportableTypes, diff --git a/src/legacy/server/kbn_server.d.ts b/src/legacy/server/kbn_server.d.ts index 68b5a63871372..50cbb75d7aeaf 100644 --- a/src/legacy/server/kbn_server.d.ts +++ b/src/legacy/server/kbn_server.d.ts @@ -38,9 +38,6 @@ import { LegacyServiceDiscoverPlugins, } from '../../core/server'; -// Disable lint errors for imports from src/core/server/saved_objects until SavedObjects migration is complete -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { SavedObjectsManagement } from '../../core/server/saved_objects/management'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { LegacyConfig, ILegacyService, ILegacyInternals } from '../../core/server/legacy'; import { ApmOssPlugin } from '../core_plugins/apm_oss'; @@ -77,7 +74,6 @@ declare module 'hapi' { addScopedTutorialContextFactory: ( scopedTutorialContextFactory: (...args: any[]) => any ) => void; - getSavedObjectsManagement(): SavedObjectsManagement; getInjectedUiAppVars: (pluginName: string) => { [key: string]: any }; getUiNavLinks(): Array<{ _id: string }>; addMemoizedFactoryToRequest: ( diff --git a/src/legacy/server/saved_objects/saved_objects_mixin.js b/src/legacy/server/saved_objects/saved_objects_mixin.js index cc63099c8a211..37ecdfaa659bd 100644 --- a/src/legacy/server/saved_objects/saved_objects_mixin.js +++ b/src/legacy/server/saved_objects/saved_objects_mixin.js @@ -29,7 +29,6 @@ import { } from '../../../core/server/saved_objects'; import { getRootPropertiesObjects } from '../../../core/server/saved_objects/mappings'; import { convertTypesToLegacySchema } from '../../../core/server/saved_objects/utils'; -import { SavedObjectsManagement } from '../../../core/server/saved_objects/management'; export function savedObjectsMixin(kbnServer, server) { const migrator = kbnServer.newPlatform.__internals.kibanaMigrator; @@ -40,11 +39,6 @@ export function savedObjectsMixin(kbnServer, server) { const visibleTypes = allTypes.filter(type => !schema.isHiddenType(type)); server.decorate('server', 'kibanaMigrator', migrator); - server.decorate( - 'server', - 'getSavedObjectsManagement', - () => new SavedObjectsManagement(typeRegistry) - ); const warn = message => server.log(['warning', 'saved-objects'], message); // we use kibana.index which is technically defined in the kibana plugin, so if @@ -84,8 +78,14 @@ export function savedObjectsMixin(kbnServer, server) { const provider = kbnServer.newPlatform.__internals.savedObjectsClientProvider; + const importAndExportableTypes = typeRegistry + .getAllTypes() + .map(type => type.name) + .filter(type => typeRegistry.isImportableAndExportable(type)); + const service = { types: visibleTypes, + importAndExportableTypes, SavedObjectsClient, SavedObjectsRepository, getSavedObjectsRepository: createRepository, diff --git a/src/legacy/server/saved_objects/saved_objects_mixin.test.js b/src/legacy/server/saved_objects/saved_objects_mixin.test.js index 3745f0b92123c..3fa9f9a936988 100644 --- a/src/legacy/server/saved_objects/saved_objects_mixin.test.js +++ b/src/legacy/server/saved_objects/saved_objects_mixin.test.js @@ -183,7 +183,7 @@ describe('Saved Objects Mixin', () => { 'kibanaMigrator', expect.any(Object) ); - expect(mockServer.decorate).toHaveBeenCalledTimes(2); + expect(mockServer.decorate).toHaveBeenCalledTimes(1); expect(mockServer.route).not.toHaveBeenCalled(); }); }); From b0f12e8bb0c145e2d904e55e5afb92756015be96 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Tue, 3 Mar 2020 08:18:48 +0100 Subject: [PATCH 02/20] migrate saved object management routes to new plugin --- src/core/server/saved_objects/index.ts | 2 - .../saved_objects/saved_objects_service.ts | 4 +- src/legacy/core_plugins/kibana/index.js | 2 - .../api/management/saved_objects/find.js | 107 ---------------- .../management/saved_objects/relationships.js | 59 --------- .../api/management/saved_objects/scroll.js | 120 ------------------ .../management/saved_objects/scroll.test.js | 95 -------------- src/plugins/so_management/kibana.json | 6 + .../so_management/server/index.ts} | 13 +- .../so_management/server/lib/find_all.ts | 39 ++++++ .../server/lib/find_relationships.ts | 94 ++++++++++++++ src/plugins/so_management/server/lib/index.ts | 22 ++++ .../server/lib/inject_meta_attributes.ts | 48 +++++++ src/plugins/so_management/server/plugin.ts | 51 ++++++++ .../so_management/server/routes/find.ts | 97 ++++++++++++++ .../so_management/server/routes/index.ts | 38 ++++++ .../server/routes/relationships.ts | 66 ++++++++++ .../server/routes/scroll_count.ts | 67 ++++++++++ .../server/routes/scroll_export.ts | 56 ++++++++ .../so_management/server/services}/index.ts | 2 +- .../server/services}/management.mock.ts | 0 .../server/services}/management.test.ts | 3 +- .../server/services}/management.ts | 12 +- 23 files changed, 596 insertions(+), 407 deletions(-) delete mode 100644 src/legacy/core_plugins/kibana/server/routes/api/management/saved_objects/find.js delete mode 100644 src/legacy/core_plugins/kibana/server/routes/api/management/saved_objects/relationships.js delete mode 100644 src/legacy/core_plugins/kibana/server/routes/api/management/saved_objects/scroll.js delete mode 100644 src/legacy/core_plugins/kibana/server/routes/api/management/saved_objects/scroll.test.js create mode 100644 src/plugins/so_management/kibana.json rename src/{legacy/core_plugins/kibana/server/routes/api/management/index.js => plugins/so_management/server/index.ts} (66%) create mode 100644 src/plugins/so_management/server/lib/find_all.ts create mode 100644 src/plugins/so_management/server/lib/find_relationships.ts create mode 100644 src/plugins/so_management/server/lib/index.ts create mode 100644 src/plugins/so_management/server/lib/inject_meta_attributes.ts create mode 100644 src/plugins/so_management/server/plugin.ts create mode 100644 src/plugins/so_management/server/routes/find.ts create mode 100644 src/plugins/so_management/server/routes/index.ts create mode 100644 src/plugins/so_management/server/routes/relationships.ts create mode 100644 src/plugins/so_management/server/routes/scroll_count.ts create mode 100644 src/plugins/so_management/server/routes/scroll_export.ts rename src/{core/server/saved_objects/management => plugins/so_management/server/services}/index.ts (90%) rename src/{core/server/saved_objects/management => plugins/so_management/server/services}/management.mock.ts (100%) rename src/{core/server/saved_objects/management => plugins/so_management/server/services}/management.test.ts (97%) rename src/{core/server/saved_objects/management => plugins/so_management/server/services}/management.ts (86%) diff --git a/src/core/server/saved_objects/index.ts b/src/core/server/saved_objects/index.ts index 0af8ea7d0e830..b50e47b9eab73 100644 --- a/src/core/server/saved_objects/index.ts +++ b/src/core/server/saved_objects/index.ts @@ -21,8 +21,6 @@ export * from './service'; export { SavedObjectsSchema } from './schema'; -export { SavedObjectsManagement } from './management'; - export * from './import'; export { diff --git a/src/core/server/saved_objects/saved_objects_service.ts b/src/core/server/saved_objects/saved_objects_service.ts index 175eac3c1bd95..943be5b7c5c22 100644 --- a/src/core/server/saved_objects/saved_objects_service.ts +++ b/src/core/server/saved_objects/saved_objects_service.ts @@ -450,7 +450,9 @@ export class SavedObjectsService }; } - public async stop() {} + public async stop() { + this.migrator$.complete(); + } private createMigrator( kibanaConfig: KibanaConfigType, diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index 092eed924f330..98525d2dca267 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -24,7 +24,6 @@ import { promisify } from 'util'; import { migrations } from './migrations'; import { importApi } from './server/routes/api/import'; import { exportApi } from './server/routes/api/export'; -import { managementApi } from './server/routes/api/management'; import mappings from './mappings.json'; import { getUiSettingDefaults } from './ui_setting_defaults'; import { registerCspCollector } from './server/lib/csp_usage_collector'; @@ -310,7 +309,6 @@ export default function(kibana) { // routes importApi(server); exportApi(server); - managementApi(server); registerCspCollector(usageCollection, server); server.injectUiAppVars('kibana', () => injectVars(server)); }, diff --git a/src/legacy/core_plugins/kibana/server/routes/api/management/saved_objects/find.js b/src/legacy/core_plugins/kibana/server/routes/api/management/saved_objects/find.js deleted file mode 100644 index 920b5c43678d1..0000000000000 --- a/src/legacy/core_plugins/kibana/server/routes/api/management/saved_objects/find.js +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/** - * This file wraps the saved object `_find` API and is designed specifically for the saved object - * management UI. The main difference is this will inject a root `meta` attribute on each saved object - * that the UI depends on. The meta fields come from functions within uiExports which can't be - * injected into the front end when defined within uiExports. There are alternatives to this but have - * decided to go with this approach at the time of development. - */ - -import Joi from 'joi'; -import { injectMetaAttributes } from '../../../../lib/management/saved_objects/inject_meta_attributes'; - -export function registerFind(server) { - server.route({ - path: '/api/kibana/management/saved_objects/_find', - method: 'GET', - config: { - validate: { - query: Joi.object() - .keys({ - perPage: Joi.number() - .min(0) - .default(20), - page: Joi.number() - .min(0) - .default(1), - type: Joi.array() - .items(Joi.string()) - .single() - .required(), - search: Joi.string() - .allow('') - .optional(), - defaultSearchOperator: Joi.string() - .valid('OR', 'AND') - .default('OR'), - sortField: Joi.string(), - hasReference: Joi.object() - .keys({ - type: Joi.string().required(), - id: Joi.string().required(), - }) - .optional(), - fields: Joi.array() - .items(Joi.string()) - .single(), - }) - .default(), - }, - }, - async handler(request) { - const searchFields = new Set(); - const searchTypes = request.query.type; - const savedObjectsClient = request.getSavedObjectsClient(); - const savedObjectsManagement = server.getSavedObjectsManagement(); - const importAndExportableTypes = searchTypes.filter(type => - savedObjectsManagement.isImportAndExportable(type) - ); - - // Accumulate "defaultSearchField" attributes from savedObjectsManagement. Unfortunately - // search fields apply to all types of saved objects, the sum of these fields will - // be searched on for each object. - for (const type of importAndExportableTypes) { - const searchField = savedObjectsManagement.getDefaultSearchField(type); - if (searchField) { - searchFields.add(searchField); - } - } - - const findResponse = await savedObjectsClient.find({ - ...request.query, - fields: undefined, - searchFields: [...searchFields], - }); - return { - ...findResponse, - saved_objects: findResponse.saved_objects - .map(obj => injectMetaAttributes(obj, savedObjectsManagement)) - .map(obj => { - const result = { ...obj, attributes: {} }; - for (const field of request.query.fields || []) { - result.attributes[field] = obj.attributes[field]; - } - return result; - }), - }; - }, - }); -} diff --git a/src/legacy/core_plugins/kibana/server/routes/api/management/saved_objects/relationships.js b/src/legacy/core_plugins/kibana/server/routes/api/management/saved_objects/relationships.js deleted file mode 100644 index eb6a7fc7b5195..0000000000000 --- a/src/legacy/core_plugins/kibana/server/routes/api/management/saved_objects/relationships.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import Joi from 'joi'; -import { findRelationships } from '../../../../lib/management/saved_objects/relationships'; - -export function registerRelationships(server) { - server.route({ - path: '/api/kibana/management/saved_objects/relationships/{type}/{id}', - method: ['GET'], - config: { - validate: { - params: Joi.object().keys({ - type: Joi.string(), - id: Joi.string(), - }), - query: Joi.object().keys({ - size: Joi.number().default(10000), - savedObjectTypes: Joi.array() - .single() - .items(Joi.string()) - .required(), - }), - }, - }, - - handler: async req => { - const type = req.params.type; - const id = req.params.id; - const size = req.query.size; - const savedObjectTypes = req.query.savedObjectTypes; - const savedObjectsClient = req.getSavedObjectsClient(); - const savedObjectsManagement = req.server.getSavedObjectsManagement(); - - return await findRelationships(type, id, { - size, - savedObjectsClient, - savedObjectsManagement, - savedObjectTypes, - }); - }, - }); -} diff --git a/src/legacy/core_plugins/kibana/server/routes/api/management/saved_objects/scroll.js b/src/legacy/core_plugins/kibana/server/routes/api/management/saved_objects/scroll.js deleted file mode 100644 index b3edd42149d45..0000000000000 --- a/src/legacy/core_plugins/kibana/server/routes/api/management/saved_objects/scroll.js +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import Joi from 'joi'; - -async function findAll(savedObjectsClient, findOptions, page = 1, allObjects = []) { - const objects = await savedObjectsClient.find({ - ...findOptions, - page, - }); - - allObjects.push(...objects.saved_objects); - if (allObjects.length < objects.total) { - return findAll(savedObjectsClient, findOptions, page + 1, allObjects); - } - - return allObjects; -} - -export function registerScrollForExportRoute(server) { - server.route({ - path: '/api/kibana/management/saved_objects/scroll/export', - method: ['POST'], - config: { - validate: { - payload: Joi.object() - .keys({ - typesToInclude: Joi.array() - .items(Joi.string()) - .required(), - }) - .required(), - }, - }, - - handler: async req => { - const savedObjectsClient = req.getSavedObjectsClient(); - const objects = await findAll(savedObjectsClient, { - perPage: 1000, - type: req.payload.typesToInclude, - }); - - return objects.map(hit => { - return { - _id: hit.id, - _source: hit.attributes, - _meta: { - savedObjectVersion: 2, - }, - _migrationVersion: hit.migrationVersion, - _references: hit.references || [], - }; - }); - }, - }); -} - -export function registerScrollForCountRoute(server) { - server.route({ - path: '/api/kibana/management/saved_objects/scroll/counts', - method: ['POST'], - config: { - validate: { - payload: Joi.object() - .keys({ - typesToInclude: Joi.array() - .items(Joi.string()) - .required(), - searchString: Joi.string(), - }) - .required(), - }, - }, - - handler: async req => { - const savedObjectsClient = req.getSavedObjectsClient(); - const findOptions = { - type: req.payload.typesToInclude, - perPage: 1000, - }; - - if (req.payload.searchString) { - findOptions.search = `${req.payload.searchString}*`; - findOptions.searchFields = ['title']; - } - - const objects = await findAll(savedObjectsClient, findOptions); - const counts = objects.reduce((accum, result) => { - const type = result.type; - accum[type] = accum[type] || 0; - accum[type]++; - return accum; - }, {}); - - for (const type of req.payload.typesToInclude) { - if (!counts[type]) { - counts[type] = 0; - } - } - - return counts; - }, - }); -} diff --git a/src/legacy/core_plugins/kibana/server/routes/api/management/saved_objects/scroll.test.js b/src/legacy/core_plugins/kibana/server/routes/api/management/saved_objects/scroll.test.js deleted file mode 100644 index 0d14da39d73b3..0000000000000 --- a/src/legacy/core_plugins/kibana/server/routes/api/management/saved_objects/scroll.test.js +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import Hapi from 'hapi'; -import { registerScrollForExportRoute } from './scroll'; - -const createMockServer = () => { - const mockServer = new Hapi.Server({ - debug: false, - port: 8080, - routes: { - validate: { - failAction: (r, h, err) => { - throw err; - }, - }, - }, - }); - return mockServer; -}; - -describe(`POST /api/kibana/management/saved_objects/scroll/export`, () => { - test('requires "typesToInclude"', async () => { - const mockServer = createMockServer(); - registerScrollForExportRoute(mockServer); - - const headers = {}; - const payload = {}; - - const request = { - method: 'POST', - url: `/api/kibana/management/saved_objects/scroll/export`, - headers, - payload, - }; - - const { result, statusCode } = await mockServer.inject(request); - expect(statusCode).toEqual(400); - expect(result).toMatchObject({ - message: `child "typesToInclude" fails because ["typesToInclude" is required]`, - }); - }); - - test(`uses "typesToInclude" when searching for objects to export`, async () => { - const mockServer = createMockServer(); - const mockClient = { - find: jest.fn(() => { - return { - saved_objects: [], - }; - }), - }; - - mockServer.decorate('request', 'getSavedObjectsClient', () => mockClient); - - registerScrollForExportRoute(mockServer); - - const headers = {}; - const payload = { - typesToInclude: ['foo', 'bar'], - }; - - const request = { - method: 'POST', - url: `/api/kibana/management/saved_objects/scroll/export`, - headers, - payload, - }; - - const { result, statusCode } = await mockServer.inject(request); - expect(statusCode).toEqual(200); - expect(result).toEqual([]); - - expect(mockClient.find).toHaveBeenCalledWith({ - page: 1, - perPage: 1000, - type: ['foo', 'bar'], - }); - }); -}); diff --git a/src/plugins/so_management/kibana.json b/src/plugins/so_management/kibana.json new file mode 100644 index 0000000000000..83c3034e17ef3 --- /dev/null +++ b/src/plugins/so_management/kibana.json @@ -0,0 +1,6 @@ +{ + "id": "savedObjectsManagement", + "version": "kibana", + "server": true, + "ui": false +} diff --git a/src/legacy/core_plugins/kibana/server/routes/api/management/index.js b/src/plugins/so_management/server/index.ts similarity index 66% rename from src/legacy/core_plugins/kibana/server/routes/api/management/index.js rename to src/plugins/so_management/server/index.ts index b98ce360f57d3..ef4f97b1bc1ac 100644 --- a/src/legacy/core_plugins/kibana/server/routes/api/management/index.js +++ b/src/plugins/so_management/server/index.ts @@ -17,13 +17,8 @@ * under the License. */ -import { registerFind } from './saved_objects/find'; -import { registerRelationships } from './saved_objects/relationships'; -import { registerScrollForExportRoute, registerScrollForCountRoute } from './saved_objects/scroll'; +import { PluginInitializerContext } from 'src/core/server'; +import { SavedObjectsManagementPlugin } from './plugin'; -export function managementApi(server) { - registerRelationships(server); - registerFind(server); - registerScrollForExportRoute(server); - registerScrollForCountRoute(server); -} +export const plugin = (context: PluginInitializerContext) => + new SavedObjectsManagementPlugin(context); diff --git a/src/plugins/so_management/server/lib/find_all.ts b/src/plugins/so_management/server/lib/find_all.ts new file mode 100644 index 0000000000000..e37588e952685 --- /dev/null +++ b/src/plugins/so_management/server/lib/find_all.ts @@ -0,0 +1,39 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObjectsClientContract, SavedObject, SavedObjectsFindOptions } from 'src/core/server'; + +export const findAll = async ( + client: SavedObjectsClientContract, + findOptions: SavedObjectsFindOptions, + page = 1, + allObjects: SavedObject[] = [] +): Promise => { + const objects = await client.find({ + ...findOptions, + page, + }); + + allObjects.push(...objects.saved_objects); + if (allObjects.length < objects.total) { + return findAll(client, findOptions, page + 1, allObjects); + } + + return allObjects; +}; diff --git a/src/plugins/so_management/server/lib/find_relationships.ts b/src/plugins/so_management/server/lib/find_relationships.ts new file mode 100644 index 0000000000000..e9e5f54b734d9 --- /dev/null +++ b/src/plugins/so_management/server/lib/find_relationships.ts @@ -0,0 +1,94 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObjectsClientContract } from 'src/core/server'; +import { injectMetaAttributes, SavedObjectWithMetadata } from './inject_meta_attributes'; +import { ISavedObjectsManagement } from '../services'; + +interface SavedObjectRelation { + id: string; + type: string; + relationship: 'child' | 'parent'; + meta: SavedObjectWithMetadata['meta']; +} + +export async function findRelationships({ + type, + id, + size, + client, + savedObjectTypes, + savedObjectsManagement, +}: { + type: string; + id: string; + size: number; + client: SavedObjectsClientContract; + savedObjectTypes: string[]; + savedObjectsManagement: ISavedObjectsManagement; +}): Promise { + const { references = [] } = await client.get(type, id); + + // Use a map to avoid duplicates, it does happen but have a different "name" in the reference + const referencedToBulkGetOpts = new Map( + references.map(ref => [`${ref.type}:${ref.id}`, { id: ref.id, type: ref.type }]) + ); + + const [referencedObjects, referencedResponse] = await Promise.all([ + referencedToBulkGetOpts.size > 0 + ? client.bulkGet([...referencedToBulkGetOpts.values()]) + : Promise.resolve({ saved_objects: [] }), + client.find({ + hasReference: { type, id }, + perPage: size, + type: savedObjectTypes, + }), + ]); + + return referencedObjects.saved_objects + .map(obj => injectMetaAttributes(obj, savedObjectsManagement)) + .map(extractCommonProperties) + .map( + obj => + ({ + ...obj, + relationship: 'child', + } as SavedObjectRelation) + ) + .concat( + referencedResponse.saved_objects + .map(obj => injectMetaAttributes(obj, savedObjectsManagement)) + .map(extractCommonProperties) + .map( + obj => + ({ + ...obj, + relationship: 'parent', + } as SavedObjectRelation) + ) + ); +} + +function extractCommonProperties(savedObject: SavedObjectWithMetadata) { + return { + id: savedObject.id, + type: savedObject.type, + meta: savedObject.meta, + }; +} diff --git a/src/plugins/so_management/server/lib/index.ts b/src/plugins/so_management/server/lib/index.ts new file mode 100644 index 0000000000000..dea6813d690ec --- /dev/null +++ b/src/plugins/so_management/server/lib/index.ts @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { injectMetaAttributes } from './inject_meta_attributes'; +export { findAll } from './find_all'; +export { findRelationships } from './find_relationships'; diff --git a/src/plugins/so_management/server/lib/inject_meta_attributes.ts b/src/plugins/so_management/server/lib/inject_meta_attributes.ts new file mode 100644 index 0000000000000..1387e7ed7046d --- /dev/null +++ b/src/plugins/so_management/server/lib/inject_meta_attributes.ts @@ -0,0 +1,48 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObject } from 'src/core/server'; +import { ISavedObjectsManagement } from '../services'; + +export type SavedObjectWithMetadata = SavedObject & { + meta: { + icon?: string; + title?: string; + editUrl?: string; + inAppUrl?: { path: string; uiCapabilitiesPath: string }; + }; +}; + +export function injectMetaAttributes( + savedObject: SavedObject | SavedObjectWithMetadata, + savedObjectsManagement: ISavedObjectsManagement +): SavedObjectWithMetadata { + const result = { + ...savedObject, + meta: (savedObject as SavedObjectWithMetadata).meta || {}, + }; + + // Add extra meta information + result.meta.icon = savedObjectsManagement.getIcon(savedObject.type); + result.meta.title = savedObjectsManagement.getTitle(savedObject); + result.meta.editUrl = savedObjectsManagement.getEditUrl(savedObject); + result.meta.inAppUrl = savedObjectsManagement.getInAppUrl(savedObject); + + return result; +} diff --git a/src/plugins/so_management/server/plugin.ts b/src/plugins/so_management/server/plugin.ts new file mode 100644 index 0000000000000..d5dc0dad2cbea --- /dev/null +++ b/src/plugins/so_management/server/plugin.ts @@ -0,0 +1,51 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Subject } from 'rxjs'; +import { first } from 'rxjs/operators'; +import { CoreSetup, CoreStart, Logger, Plugin, PluginInitializerContext } from 'src/core/server'; +import { SavedObjectsManagement } from './services'; +import { registerRoutes } from './routes'; + +export class SavedObjectsManagementPlugin implements Plugin<{}, {}> { + private readonly logger: Logger; + private managementService$ = new Subject(); + + constructor(private readonly context: PluginInitializerContext) { + this.logger = this.context.logger.get(); + } + + public async setup({ http }: CoreSetup) { + this.logger.debug('Setting up SavedObjectsManagement plugin'); + + registerRoutes({ + http, + managementServicePromise: this.managementService$.pipe(first()).toPromise(), + }); + + return {}; + } + + public async start(core: CoreStart) { + this.logger.debug('Starting up SavedObjectsManagement plugin'); + const managementService = new SavedObjectsManagement(core.savedObjects.getTypeRegistry()); + this.managementService$.next(managementService); + return {}; + } +} diff --git a/src/plugins/so_management/server/routes/find.ts b/src/plugins/so_management/server/routes/find.ts new file mode 100644 index 0000000000000..781cad0d7fbd6 --- /dev/null +++ b/src/plugins/so_management/server/routes/find.ts @@ -0,0 +1,97 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { schema } from '@kbn/config-schema'; +import { IRouter } from 'src/core/server'; +import { injectMetaAttributes } from '../lib'; +import { SavedObjectsManagement } from '../services'; + +export const registerFindRoute = ( + router: IRouter, + managementServicePromise: Promise +) => { + router.get( + { + path: '/api/kibana/management/saved_objects/_find', // TODO: change + validate: { + query: schema.object({ + perPage: schema.number({ min: 0, defaultValue: 20 }), + page: schema.number({ min: 0, defaultValue: 1 }), + type: schema.oneOf([schema.string(), schema.arrayOf(schema.string())]), + search: schema.maybe(schema.string()), + defaultSearchOperator: schema.oneOf([schema.literal('OR'), schema.literal('AND')], { + defaultValue: 'OR', + }), + sortField: schema.maybe(schema.string()), + hasReference: schema.maybe( + schema.object({ + type: schema.string(), + id: schema.string(), + }) + ), + fields: schema.oneOf([schema.string(), schema.arrayOf(schema.string())], { + defaultValue: [], + }), + }), + }, + }, + router.handleLegacyErrors(async (context, req, res) => { + const managementService = await managementServicePromise; + const { client } = context.core.savedObjects; + const searchTypes = Array.isArray(req.query.type) ? req.query.type : [req.query.type]; + const includedFields = Array.isArray(req.query.fields) + ? req.query.fields + : [req.query.fields]; + const importAndExportableTypes = searchTypes.filter(type => + managementService.isImportAndExportable(type) + ); + + const searchFields = new Set(); + importAndExportableTypes.forEach(type => { + const searchField = managementService.getDefaultSearchField(type); + if (searchField) { + searchFields.add(searchField); + } + }); + + const findResponse = await client.find({ + ...req.query, + fields: undefined, + searchFields: [...searchFields], + }); + + const enhancedSavedObjects = findResponse.saved_objects + .map(so => injectMetaAttributes(so, managementService)) + .map(obj => { + const result = { ...obj, attributes: {} as Record }; + for (const field of includedFields || []) { + result.attributes[field] = obj.attributes[field]; + } + return result; + }); + + return res.ok({ + body: { + ...findResponse, + saved_objects: enhancedSavedObjects, + }, + }); + }) + ); +}; diff --git a/src/plugins/so_management/server/routes/index.ts b/src/plugins/so_management/server/routes/index.ts new file mode 100644 index 0000000000000..c952838748afe --- /dev/null +++ b/src/plugins/so_management/server/routes/index.ts @@ -0,0 +1,38 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { HttpServiceSetup } from 'src/core/server'; +import { SavedObjectsManagement } from '../services'; +import { registerFindRoute } from './find'; +import { registerScrollForCountRoute } from './scroll_count'; +import { registerScrollForExportRoute } from './scroll_export'; +import { registerRelationshipsRoute } from './relationships'; + +interface RegisterRouteOptions { + http: HttpServiceSetup; + managementServicePromise: Promise; +} + +export function registerRoutes({ http, managementServicePromise }: RegisterRouteOptions) { + const router = http.createRouter(); + registerFindRoute(router, managementServicePromise); + registerScrollForCountRoute(router); + registerScrollForExportRoute(router); + registerRelationshipsRoute(router, managementServicePromise); +} diff --git a/src/plugins/so_management/server/routes/relationships.ts b/src/plugins/so_management/server/routes/relationships.ts new file mode 100644 index 0000000000000..53e82fe1a7999 --- /dev/null +++ b/src/plugins/so_management/server/routes/relationships.ts @@ -0,0 +1,66 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { schema } from '@kbn/config-schema'; +import { IRouter } from 'src/core/server'; +import { findRelationships } from '../lib'; +import { SavedObjectsManagement } from '../services'; + +export const registerRelationshipsRoute = ( + router: IRouter, + managementServicePromise: Promise +) => { + router.get( + { + path: '/api/kibana/management/saved_objects/relationships/{type}/{id}', // TODO: change + validate: { + params: schema.object({ + type: schema.string(), + id: schema.string(), + }), + query: schema.object({ + size: schema.number({ defaultValue: 10000 }), + savedObjectTypes: schema.oneOf([schema.string(), schema.arrayOf(schema.string())]), + }), + }, + }, + router.handleLegacyErrors(async (context, req, res) => { + const managementService = await managementServicePromise; + const { client } = context.core.savedObjects; + const { type, id } = req.params; + const { size } = req.query; + const savedObjectTypes = Array.isArray(req.query.savedObjectTypes) + ? req.query.savedObjectTypes + : [req.query.savedObjectTypes]; + + const relations = await findRelationships({ + type, + id, + client, + size, + savedObjectTypes, + savedObjectsManagement: managementService, + }); + + return res.ok({ + body: relations, + }); + }) + ); +}; diff --git a/src/plugins/so_management/server/routes/scroll_count.ts b/src/plugins/so_management/server/routes/scroll_count.ts new file mode 100644 index 0000000000000..f1619cd7ef9c2 --- /dev/null +++ b/src/plugins/so_management/server/routes/scroll_count.ts @@ -0,0 +1,67 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { schema } from '@kbn/config-schema'; +import { IRouter, SavedObjectsFindOptions } from 'src/core/server'; +import { findAll } from '../lib'; + +export const registerScrollForCountRoute = (router: IRouter) => { + router.get( + { + path: '/api/kibana/management/saved_objects/scroll/counts', // TODO: change + validate: { + body: schema.object({ + typesToInclude: schema.arrayOf(schema.string()), + searchString: schema.maybe(schema.string()), + }), + }, + }, + router.handleLegacyErrors(async (context, req, res) => { + const { client } = context.core.savedObjects; + + const findOptions: SavedObjectsFindOptions = { + type: req.body.typesToInclude, + perPage: 1000, + }; + if (req.body.searchString) { + findOptions.search = `${req.body.searchString}*`; + findOptions.searchFields = ['title']; + } + + const objects = await findAll(client, findOptions); + + const counts = objects.reduce((accum, result) => { + const type = result.type; + accum[type] = accum[type] || 0; + accum[type]++; + return accum; + }, {} as Record); + + for (const type of req.body.typesToInclude) { + if (!counts[type]) { + counts[type] = 0; + } + } + + return res.ok({ + body: counts, + }); + }) + ); +}; diff --git a/src/plugins/so_management/server/routes/scroll_export.ts b/src/plugins/so_management/server/routes/scroll_export.ts new file mode 100644 index 0000000000000..8bb14877ed780 --- /dev/null +++ b/src/plugins/so_management/server/routes/scroll_export.ts @@ -0,0 +1,56 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { schema } from '@kbn/config-schema'; +import { IRouter } from 'src/core/server'; +import { findAll } from '../lib'; + +export const registerScrollForExportRoute = (router: IRouter) => { + router.get( + { + path: '/api/kibana/management/saved_objects/scroll/export', // TODO: change + validate: { + body: schema.object({ + typesToInclude: schema.arrayOf(schema.string()), + }), + }, + }, + router.handleLegacyErrors(async (context, req, res) => { + const { client } = context.core.savedObjects; + const objects = await findAll(client, { + perPage: 1000, + type: req.body.typesToInclude, + }); + + return res.ok({ + body: objects.map(hit => { + return { + _id: hit.id, + _source: hit.attributes, + _meta: { + savedObjectVersion: 2, + }, + _migrationVersion: hit.migrationVersion, + _references: hit.references || [], + }; + }), + }); + }) + ); +}; diff --git a/src/core/server/saved_objects/management/index.ts b/src/plugins/so_management/server/services/index.ts similarity index 90% rename from src/core/server/saved_objects/management/index.ts rename to src/plugins/so_management/server/services/index.ts index a256a1333c5cc..fddd53c73634e 100644 --- a/src/core/server/saved_objects/management/index.ts +++ b/src/plugins/so_management/server/services/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { SavedObjectsManagement } from './management'; +export { SavedObjectsManagement, ISavedObjectsManagement } from './management'; diff --git a/src/core/server/saved_objects/management/management.mock.ts b/src/plugins/so_management/server/services/management.mock.ts similarity index 100% rename from src/core/server/saved_objects/management/management.mock.ts rename to src/plugins/so_management/server/services/management.mock.ts diff --git a/src/core/server/saved_objects/management/management.test.ts b/src/plugins/so_management/server/services/management.test.ts similarity index 97% rename from src/core/server/saved_objects/management/management.test.ts rename to src/plugins/so_management/server/services/management.test.ts index dc110dec020f0..6b95048749fae 100644 --- a/src/core/server/saved_objects/management/management.test.ts +++ b/src/plugins/so_management/server/services/management.test.ts @@ -18,8 +18,7 @@ */ import { SavedObjectsManagement } from './management'; -import { SavedObjectsType } from '../types'; -import { SavedObjectTypeRegistry } from '../saved_objects_type_registry'; +import { SavedObjectsType, SavedObjectTypeRegistry } from '../../../../core/server'; describe('SavedObjectsManagement', () => { let registry: SavedObjectTypeRegistry; diff --git a/src/core/server/saved_objects/management/management.ts b/src/plugins/so_management/server/services/management.ts similarity index 86% rename from src/core/server/saved_objects/management/management.ts rename to src/plugins/so_management/server/services/management.ts index db759c4aec752..7aee974182497 100644 --- a/src/core/server/saved_objects/management/management.ts +++ b/src/plugins/so_management/server/services/management.ts @@ -17,19 +17,13 @@ * under the License. */ -import { SavedObject } from '../types'; -import { ISavedObjectTypeRegistry } from '../saved_objects_type_registry'; +import { ISavedObjectTypeRegistry, SavedObject } from 'src/core/server'; + +export type ISavedObjectsManagement = PublicMethodsOf; export class SavedObjectsManagement { constructor(private readonly registry: ISavedObjectTypeRegistry) {} - public getImportableAndExportableTypes() { - return this.registry - .getAllTypes() - .map(type => type.name) - .filter(type => this.isImportAndExportable(type)); - } - public isImportAndExportable(type: string) { return this.registry.isImportableAndExportable(type); } From 1f0e935e8ab2ec4c5107a786452971f101cf0c77 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 4 Mar 2020 15:25:50 +0100 Subject: [PATCH 03/20] fix endpoint methods --- src/plugins/so_management/server/routes/scroll_count.ts | 2 +- src/plugins/so_management/server/routes/scroll_export.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/so_management/server/routes/scroll_count.ts b/src/plugins/so_management/server/routes/scroll_count.ts index f1619cd7ef9c2..7a0fe7ae78d0c 100644 --- a/src/plugins/so_management/server/routes/scroll_count.ts +++ b/src/plugins/so_management/server/routes/scroll_count.ts @@ -22,7 +22,7 @@ import { IRouter, SavedObjectsFindOptions } from 'src/core/server'; import { findAll } from '../lib'; export const registerScrollForCountRoute = (router: IRouter) => { - router.get( + router.post( { path: '/api/kibana/management/saved_objects/scroll/counts', // TODO: change validate: { diff --git a/src/plugins/so_management/server/routes/scroll_export.ts b/src/plugins/so_management/server/routes/scroll_export.ts index 8bb14877ed780..d1c4b933f400e 100644 --- a/src/plugins/so_management/server/routes/scroll_export.ts +++ b/src/plugins/so_management/server/routes/scroll_export.ts @@ -22,7 +22,7 @@ import { IRouter } from 'src/core/server'; import { findAll } from '../lib'; export const registerScrollForExportRoute = (router: IRouter) => { - router.get( + router.post( { path: '/api/kibana/management/saved_objects/scroll/export', // TODO: change validate: { From 53030ff1a85a3a2887fc015ecb790cfe5d48853e Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Tue, 10 Mar 2020 11:37:06 +0100 Subject: [PATCH 04/20] adapt code due to rebase --- src/legacy/server/saved_objects/saved_objects_mixin.js | 5 ++--- src/plugins/so_management/server/routes/find.ts | 2 +- src/plugins/so_management/server/routes/relationships.ts | 2 +- src/plugins/so_management/server/routes/scroll_count.ts | 2 +- src/plugins/so_management/server/routes/scroll_export.ts | 2 +- src/plugins/so_management/server/services/management.mock.ts | 1 - 6 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/legacy/server/saved_objects/saved_objects_mixin.js b/src/legacy/server/saved_objects/saved_objects_mixin.js index 37ecdfaa659bd..bcf766231dc9c 100644 --- a/src/legacy/server/saved_objects/saved_objects_mixin.js +++ b/src/legacy/server/saved_objects/saved_objects_mixin.js @@ -79,9 +79,8 @@ export function savedObjectsMixin(kbnServer, server) { const provider = kbnServer.newPlatform.__internals.savedObjectsClientProvider; const importAndExportableTypes = typeRegistry - .getAllTypes() - .map(type => type.name) - .filter(type => typeRegistry.isImportableAndExportable(type)); + .getImportableAndExportableTypes() + .map(type => type.name); const service = { types: visibleTypes, diff --git a/src/plugins/so_management/server/routes/find.ts b/src/plugins/so_management/server/routes/find.ts index 781cad0d7fbd6..44c80201e98fe 100644 --- a/src/plugins/so_management/server/routes/find.ts +++ b/src/plugins/so_management/server/routes/find.ts @@ -28,7 +28,7 @@ export const registerFindRoute = ( ) => { router.get( { - path: '/api/kibana/management/saved_objects/_find', // TODO: change + path: '/api/kibana/management/saved_objects/_find', validate: { query: schema.object({ perPage: schema.number({ min: 0, defaultValue: 20 }), diff --git a/src/plugins/so_management/server/routes/relationships.ts b/src/plugins/so_management/server/routes/relationships.ts index 53e82fe1a7999..54818012d89a0 100644 --- a/src/plugins/so_management/server/routes/relationships.ts +++ b/src/plugins/so_management/server/routes/relationships.ts @@ -28,7 +28,7 @@ export const registerRelationshipsRoute = ( ) => { router.get( { - path: '/api/kibana/management/saved_objects/relationships/{type}/{id}', // TODO: change + path: '/api/kibana/management/saved_objects/relationships/{type}/{id}', validate: { params: schema.object({ type: schema.string(), diff --git a/src/plugins/so_management/server/routes/scroll_count.ts b/src/plugins/so_management/server/routes/scroll_count.ts index 7a0fe7ae78d0c..58ba90d847791 100644 --- a/src/plugins/so_management/server/routes/scroll_count.ts +++ b/src/plugins/so_management/server/routes/scroll_count.ts @@ -24,7 +24,7 @@ import { findAll } from '../lib'; export const registerScrollForCountRoute = (router: IRouter) => { router.post( { - path: '/api/kibana/management/saved_objects/scroll/counts', // TODO: change + path: '/api/kibana/management/saved_objects/scroll/counts', validate: { body: schema.object({ typesToInclude: schema.arrayOf(schema.string()), diff --git a/src/plugins/so_management/server/routes/scroll_export.ts b/src/plugins/so_management/server/routes/scroll_export.ts index d1c4b933f400e..cda2770234911 100644 --- a/src/plugins/so_management/server/routes/scroll_export.ts +++ b/src/plugins/so_management/server/routes/scroll_export.ts @@ -24,7 +24,7 @@ import { findAll } from '../lib'; export const registerScrollForExportRoute = (router: IRouter) => { router.post( { - path: '/api/kibana/management/saved_objects/scroll/export', // TODO: change + path: '/api/kibana/management/saved_objects/scroll/export', validate: { body: schema.object({ typesToInclude: schema.arrayOf(schema.string()), diff --git a/src/plugins/so_management/server/services/management.mock.ts b/src/plugins/so_management/server/services/management.mock.ts index e7242c30d3961..2099cc0f77bcc 100644 --- a/src/plugins/so_management/server/services/management.mock.ts +++ b/src/plugins/so_management/server/services/management.mock.ts @@ -24,7 +24,6 @@ const createManagementMock = () => { const mocked: jest.Mocked = { isImportAndExportable: jest.fn().mockReturnValue(true), getDefaultSearchField: jest.fn(), - getImportableAndExportableTypes: jest.fn(), getIcon: jest.fn(), getTitle: jest.fn(), getEditUrl: jest.fn(), From 8e71b9217fd72465937f2ed01e167e0e14d4938b Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Tue, 10 Mar 2020 11:48:12 +0100 Subject: [PATCH 05/20] extract types --- .../server/lib/find_relationships.ts | 5 +-- .../server/lib/inject_meta_attributes.ts | 10 +----- src/plugins/so_management/server/types.ts | 31 +++++++++++++++++++ 3 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 src/plugins/so_management/server/types.ts diff --git a/src/plugins/so_management/server/lib/find_relationships.ts b/src/plugins/so_management/server/lib/find_relationships.ts index e9e5f54b734d9..2031ad708ea14 100644 --- a/src/plugins/so_management/server/lib/find_relationships.ts +++ b/src/plugins/so_management/server/lib/find_relationships.ts @@ -18,14 +18,15 @@ */ import { SavedObjectsClientContract } from 'src/core/server'; -import { injectMetaAttributes, SavedObjectWithMetadata } from './inject_meta_attributes'; +import { injectMetaAttributes } from './inject_meta_attributes'; import { ISavedObjectsManagement } from '../services'; +import { SavedObjectMetadata, SavedObjectWithMetadata } from '../types'; interface SavedObjectRelation { id: string; type: string; relationship: 'child' | 'parent'; - meta: SavedObjectWithMetadata['meta']; + meta: SavedObjectMetadata; } export async function findRelationships({ diff --git a/src/plugins/so_management/server/lib/inject_meta_attributes.ts b/src/plugins/so_management/server/lib/inject_meta_attributes.ts index 1387e7ed7046d..615caffd3b60b 100644 --- a/src/plugins/so_management/server/lib/inject_meta_attributes.ts +++ b/src/plugins/so_management/server/lib/inject_meta_attributes.ts @@ -19,15 +19,7 @@ import { SavedObject } from 'src/core/server'; import { ISavedObjectsManagement } from '../services'; - -export type SavedObjectWithMetadata = SavedObject & { - meta: { - icon?: string; - title?: string; - editUrl?: string; - inAppUrl?: { path: string; uiCapabilitiesPath: string }; - }; -}; +import { SavedObjectWithMetadata } from '../types'; export function injectMetaAttributes( savedObject: SavedObject | SavedObjectWithMetadata, diff --git a/src/plugins/so_management/server/types.ts b/src/plugins/so_management/server/types.ts new file mode 100644 index 0000000000000..5d1495ba0506e --- /dev/null +++ b/src/plugins/so_management/server/types.ts @@ -0,0 +1,31 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObject } from 'src/core/server'; + +export interface SavedObjectMetadata { + icon?: string; + title?: string; + editUrl?: string; + inAppUrl?: { path: string; uiCapabilitiesPath: string }; +} + +export type SavedObjectWithMetadata = SavedObject & { + meta: SavedObjectMetadata; +}; From 6eaff3498a72bb57033e005a8fbdc010636a5531 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Tue, 10 Mar 2020 11:52:11 +0100 Subject: [PATCH 06/20] improve findAll params --- src/plugins/so_management/server/lib/find_all.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/plugins/so_management/server/lib/find_all.ts b/src/plugins/so_management/server/lib/find_all.ts index e37588e952685..6bc3e46d026bf 100644 --- a/src/plugins/so_management/server/lib/find_all.ts +++ b/src/plugins/so_management/server/lib/find_all.ts @@ -20,10 +20,17 @@ import { SavedObjectsClientContract, SavedObject, SavedObjectsFindOptions } from 'src/core/server'; export const findAll = async ( + client: SavedObjectsClientContract, + findOptions: SavedObjectsFindOptions +): Promise => { + return recursiveFind(client, findOptions, 1, []); +}; + +const recursiveFind = async ( client: SavedObjectsClientContract, findOptions: SavedObjectsFindOptions, - page = 1, - allObjects: SavedObject[] = [] + page: number, + allObjects: SavedObject[] ): Promise => { const objects = await client.find({ ...findOptions, @@ -32,7 +39,7 @@ export const findAll = async ( allObjects.push(...objects.saved_objects); if (allObjects.length < objects.total) { - return findAll(client, findOptions, page + 1, allObjects); + return recursiveFind(client, findOptions, page + 1, allObjects); } return allObjects; From dc9bad9a8664613276538f25a21d5691517710c8 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 11 Mar 2020 08:28:48 +0100 Subject: [PATCH 07/20] adapt existing api integration tests and migrate to TS --- test/api_integration/apis/index.js | 2 +- test/api_integration/apis/management/index.js | 24 - .../management/saved_objects/relationships.js | 449 ------------------ .../find.ts} | 49 +- .../index.ts} | 6 +- .../saved_objects_management/relationships.ts | 423 +++++++++++++++++ 6 files changed, 448 insertions(+), 505 deletions(-) delete mode 100644 test/api_integration/apis/management/index.js delete mode 100644 test/api_integration/apis/management/saved_objects/relationships.js rename test/api_integration/apis/{management/saved_objects/find.js => saved_objects_management/find.ts} (91%) rename test/api_integration/apis/{management/saved_objects/index.js => saved_objects_management/index.ts} (83%) create mode 100644 test/api_integration/apis/saved_objects_management/relationships.ts diff --git a/test/api_integration/apis/index.js b/test/api_integration/apis/index.js index 8cdbbf8e74a3d..07c6dfcb210d2 100644 --- a/test/api_integration/apis/index.js +++ b/test/api_integration/apis/index.js @@ -25,7 +25,7 @@ export default function({ loadTestFile }) { loadTestFile(require.resolve('./home')); loadTestFile(require.resolve('./index_patterns')); loadTestFile(require.resolve('./kql_telemetry')); - loadTestFile(require.resolve('./management')); + loadTestFile(require.resolve('./saved_objects_management')); loadTestFile(require.resolve('./saved_objects')); loadTestFile(require.resolve('./scripts')); loadTestFile(require.resolve('./shorten')); diff --git a/test/api_integration/apis/management/index.js b/test/api_integration/apis/management/index.js deleted file mode 100644 index a442e3e4fee74..0000000000000 --- a/test/api_integration/apis/management/index.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export default function({ loadTestFile }) { - describe('management apis', () => { - loadTestFile(require.resolve('./saved_objects')); - }); -} diff --git a/test/api_integration/apis/management/saved_objects/relationships.js b/test/api_integration/apis/management/saved_objects/relationships.js deleted file mode 100644 index d202094e6d6f8..0000000000000 --- a/test/api_integration/apis/management/saved_objects/relationships.js +++ /dev/null @@ -1,449 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -const Joi = require('joi'); - -export default function({ getService }) { - const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - - const GENERIC_RESPONSE_SCHEMA = Joi.array().items( - Joi.object().keys({ - id: Joi.string() - .uuid() - .required(), - type: Joi.string().required(), - relationship: Joi.string() - .valid('parent', 'child') - .required(), - meta: Joi.object() - .keys({ - title: Joi.string().required(), - icon: Joi.string().required(), - editUrl: Joi.string().required(), - inAppUrl: Joi.object() - .keys({ - path: Joi.string().required(), - uiCapabilitiesPath: Joi.string().required(), - }) - .required(), - }) - .required(), - }) - ); - - describe('relationships', () => { - before(() => esArchiver.load('management/saved_objects')); - after(() => esArchiver.unload('management/saved_objects')); - - const baseApiUrl = `/api/kibana/management/saved_objects/relationships`; - const coerceToArray = itemOrItems => [].concat(itemOrItems); - const getSavedObjectTypesQuery = types => - coerceToArray(types) - .map(type => `savedObjectTypes=${type}`) - .join('&'); - const defaultQuery = getSavedObjectTypesQuery([ - 'visualization', - 'index-pattern', - 'search', - 'dashboard', - ]); - - describe('searches', () => { - it('should validate search response schema', async () => { - await supertest - .get(`${baseApiUrl}/search/960372e0-3224-11e8-a572-ffca06da1357?${defaultQuery}`) - .expect(200) - .then(resp => { - const validationResult = Joi.validate(resp.body, GENERIC_RESPONSE_SCHEMA); - expect(validationResult.error).to.be(null); - }); - }); - - it('should work for searches', async () => { - await supertest - .get(`${baseApiUrl}/search/960372e0-3224-11e8-a572-ffca06da1357?${defaultQuery}`) - .expect(200) - .then(resp => { - expect(resp.body).to.eql([ - { - id: '8963ca30-3224-11e8-a572-ffca06da1357', - type: 'index-pattern', - relationship: 'child', - meta: { - title: 'saved_objects*', - icon: 'indexPatternApp', - editUrl: '/management/kibana/index_patterns/8963ca30-3224-11e8-a572-ffca06da1357', - inAppUrl: { - path: - '/app/kibana#/management/kibana/index_patterns/8963ca30-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'management.kibana.index_patterns', - }, - }, - }, - { - id: 'a42c0580-3224-11e8-a572-ffca06da1357', - type: 'visualization', - relationship: 'parent', - meta: { - title: 'VisualizationFromSavedSearch', - icon: 'visualizeApp', - editUrl: - '/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357', - inAppUrl: { - path: '/app/kibana#/visualize/edit/a42c0580-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, - }, - }, - ]); - }); - }); - - it('should filter based on savedObjectTypes', async () => { - await supertest - .get( - `${baseApiUrl}/search/960372e0-3224-11e8-a572-ffca06da1357?${getSavedObjectTypesQuery( - 'visualization' - )}` - ) - .expect(200) - .then(resp => { - expect(resp.body).to.eql([ - { - id: '8963ca30-3224-11e8-a572-ffca06da1357', - type: 'index-pattern', - meta: { - icon: 'indexPatternApp', - title: 'saved_objects*', - editUrl: '/management/kibana/index_patterns/8963ca30-3224-11e8-a572-ffca06da1357', - inAppUrl: { - path: - '/app/kibana#/management/kibana/index_patterns/8963ca30-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'management.kibana.index_patterns', - }, - }, - relationship: 'child', - }, - { - id: 'a42c0580-3224-11e8-a572-ffca06da1357', - type: 'visualization', - meta: { - icon: 'visualizeApp', - title: 'VisualizationFromSavedSearch', - editUrl: - '/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357', - inAppUrl: { - path: '/app/kibana#/visualize/edit/a42c0580-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, - }, - relationship: 'parent', - }, - ]); - }); - }); - - //TODO: https://github.com/elastic/kibana/issues/19713 causes this test to fail. - it.skip('should return 404 if search finds no results', async () => { - await supertest - .get(`${baseApiUrl}/search/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx${defaultQuery}`) - .expect(404); - }); - }); - - describe('dashboards', () => { - it('should validate dashboard response schema', async () => { - await supertest - .get(`${baseApiUrl}/dashboard/b70c7ae0-3224-11e8-a572-ffca06da1357?${defaultQuery}`) - .expect(200) - .then(resp => { - const validationResult = Joi.validate(resp.body, GENERIC_RESPONSE_SCHEMA); - expect(validationResult.error).to.be(null); - }); - }); - - it('should work for dashboards', async () => { - await supertest - .get(`${baseApiUrl}/dashboard/b70c7ae0-3224-11e8-a572-ffca06da1357?${defaultQuery}`) - .expect(200) - .then(resp => { - expect(resp.body).to.eql([ - { - id: 'add810b0-3224-11e8-a572-ffca06da1357', - type: 'visualization', - relationship: 'child', - meta: { - icon: 'visualizeApp', - title: 'Visualization', - editUrl: - '/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357', - inAppUrl: { - path: '/app/kibana#/visualize/edit/add810b0-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, - }, - }, - { - id: 'a42c0580-3224-11e8-a572-ffca06da1357', - type: 'visualization', - relationship: 'child', - meta: { - icon: 'visualizeApp', - title: 'VisualizationFromSavedSearch', - editUrl: - '/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357', - inAppUrl: { - path: '/app/kibana#/visualize/edit/a42c0580-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, - }, - }, - ]); - }); - }); - - it('should filter based on savedObjectTypes', async () => { - await supertest - .get( - `${baseApiUrl}/dashboard/b70c7ae0-3224-11e8-a572-ffca06da1357?${getSavedObjectTypesQuery( - 'search' - )}` - ) - .expect(200) - .then(resp => { - expect(resp.body).to.eql([ - { - id: 'add810b0-3224-11e8-a572-ffca06da1357', - type: 'visualization', - meta: { - icon: 'visualizeApp', - title: 'Visualization', - editUrl: - '/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357', - inAppUrl: { - path: '/app/kibana#/visualize/edit/add810b0-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, - }, - relationship: 'child', - }, - { - id: 'a42c0580-3224-11e8-a572-ffca06da1357', - type: 'visualization', - meta: { - icon: 'visualizeApp', - title: 'VisualizationFromSavedSearch', - editUrl: - '/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357', - inAppUrl: { - path: '/app/kibana#/visualize/edit/a42c0580-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, - }, - relationship: 'child', - }, - ]); - }); - }); - - //TODO: https://github.com/elastic/kibana/issues/19713 causes this test to fail. - it.skip('should return 404 if dashboard finds no results', async () => { - await supertest - .get(`${baseApiUrl}/dashboard/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx${defaultQuery}`) - .expect(404); - }); - }); - - describe('visualizations', () => { - it('should validate visualization response schema', async () => { - await supertest - .get(`${baseApiUrl}/visualization/a42c0580-3224-11e8-a572-ffca06da1357?${defaultQuery}`) - .expect(200) - .then(resp => { - const validationResult = Joi.validate(resp.body, GENERIC_RESPONSE_SCHEMA); - expect(validationResult.error).to.be(null); - }); - }); - - it('should work for visualizations', async () => { - await supertest - .get(`${baseApiUrl}/visualization/a42c0580-3224-11e8-a572-ffca06da1357?${defaultQuery}`) - .expect(200) - .then(resp => { - expect(resp.body).to.eql([ - { - id: '960372e0-3224-11e8-a572-ffca06da1357', - type: 'search', - relationship: 'child', - meta: { - icon: 'discoverApp', - title: 'OneRecord', - editUrl: - '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', - inAppUrl: { - path: '/app/kibana#/discover/960372e0-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'discover.show', - }, - }, - }, - { - id: 'b70c7ae0-3224-11e8-a572-ffca06da1357', - type: 'dashboard', - relationship: 'parent', - meta: { - icon: 'dashboardApp', - title: 'Dashboard', - editUrl: - '/management/kibana/objects/savedDashboards/b70c7ae0-3224-11e8-a572-ffca06da1357', - inAppUrl: { - path: '/app/kibana#/dashboard/b70c7ae0-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'dashboard.show', - }, - }, - }, - ]); - }); - }); - - it('should filter based on savedObjectTypes', async () => { - await supertest - .get( - `${baseApiUrl}/visualization/a42c0580-3224-11e8-a572-ffca06da1357?${getSavedObjectTypesQuery( - 'search' - )}` - ) - .expect(200) - .then(resp => { - expect(resp.body).to.eql([ - { - id: '960372e0-3224-11e8-a572-ffca06da1357', - type: 'search', - meta: { - icon: 'discoverApp', - title: 'OneRecord', - editUrl: - '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', - inAppUrl: { - path: '/app/kibana#/discover/960372e0-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'discover.show', - }, - }, - relationship: 'child', - }, - ]); - }); - }); - - it('should return 404 if visualizations finds no results', async () => { - await supertest - .get(`${baseApiUrl}/visualization/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx?${defaultQuery}`) - .expect(404); - }); - }); - - describe('index patterns', () => { - it('should validate visualization response schema', async () => { - await supertest - .get(`${baseApiUrl}/index-pattern/8963ca30-3224-11e8-a572-ffca06da1357?${defaultQuery}`) - .expect(200) - .then(resp => { - const validationResult = Joi.validate(resp.body, GENERIC_RESPONSE_SCHEMA); - expect(validationResult.error).to.be(null); - }); - }); - - it('should work for index patterns', async () => { - await supertest - .get(`${baseApiUrl}/index-pattern/8963ca30-3224-11e8-a572-ffca06da1357?${defaultQuery}`) - .expect(200) - .then(resp => { - expect(resp.body).to.eql([ - { - id: '960372e0-3224-11e8-a572-ffca06da1357', - type: 'search', - relationship: 'parent', - meta: { - icon: 'discoverApp', - title: 'OneRecord', - editUrl: - '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', - inAppUrl: { - path: '/app/kibana#/discover/960372e0-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'discover.show', - }, - }, - }, - { - id: 'add810b0-3224-11e8-a572-ffca06da1357', - type: 'visualization', - relationship: 'parent', - meta: { - icon: 'visualizeApp', - title: 'Visualization', - editUrl: - '/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357', - inAppUrl: { - path: '/app/kibana#/visualize/edit/add810b0-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, - }, - }, - ]); - }); - }); - - it('should filter based on savedObjectTypes', async () => { - await supertest - .get( - `${baseApiUrl}/index-pattern/8963ca30-3224-11e8-a572-ffca06da1357?${getSavedObjectTypesQuery( - 'search' - )}` - ) - .expect(200) - .then(resp => { - expect(resp.body).to.eql([ - { - id: '960372e0-3224-11e8-a572-ffca06da1357', - type: 'search', - meta: { - icon: 'discoverApp', - title: 'OneRecord', - editUrl: - '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', - inAppUrl: { - path: '/app/kibana#/discover/960372e0-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'discover.show', - }, - }, - relationship: 'parent', - }, - ]); - }); - }); - - it('should return 404 if index pattern finds no results', async () => { - await supertest - .get(`${baseApiUrl}/index-pattern/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx?${defaultQuery}`) - .expect(404); - }); - }); - }); -} diff --git a/test/api_integration/apis/management/saved_objects/find.js b/test/api_integration/apis/saved_objects_management/find.ts similarity index 91% rename from test/api_integration/apis/management/saved_objects/find.js rename to test/api_integration/apis/saved_objects_management/find.ts index 89e158671c3ca..350cfb3dd987b 100644 --- a/test/api_integration/apis/management/saved_objects/find.js +++ b/test/api_integration/apis/saved_objects_management/find.ts @@ -18,8 +18,10 @@ */ import expect from '@kbn/expect'; +import { Response } from 'supertest'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function({ getService }) { +export default function({ getService }: FtrProviderContext) { const es = getService('legacyEs'); const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); @@ -33,7 +35,7 @@ export default function({ getService }) { await supertest .get('/api/kibana/management/saved_objects/_find?type=visualization&fields=title') .expect(200) - .then(resp => { + .then((resp: Response) => { expect(resp.body).to.eql({ page: 1, per_page: 20, @@ -75,7 +77,7 @@ export default function({ getService }) { await supertest .get('/api/kibana/management/saved_objects/_find?type=wigwags') .expect(200) - .then(resp => { + .then((resp: Response) => { expect(resp.body).to.eql({ page: 1, per_page: 20, @@ -92,7 +94,7 @@ export default function({ getService }) { '/api/kibana/management/saved_objects/_find?type=visualization&page=100&perPage=100' ) .expect(200) - .then(resp => { + .then((resp: Response) => { expect(resp.body).to.eql({ page: 100, per_page: 100, @@ -107,15 +109,11 @@ export default function({ getService }) { await supertest .get('/api/kibana/management/saved_objects/_find?type=url&searchFields=a') .expect(400) - .then(resp => { + .then((resp: Response) => { expect(resp.body).to.eql({ statusCode: 400, error: 'Bad Request', - message: '"searchFields" is not allowed', - validation: { - source: 'query', - keys: ['searchFields'], - }, + message: '[request query.searchFields]: definition for this key is missing', }); })); }); @@ -135,7 +133,7 @@ export default function({ getService }) { await supertest .get('/api/kibana/management/saved_objects/_find?type=visualization') .expect(200) - .then(resp => { + .then((resp: Response) => { expect(resp.body).to.eql({ page: 1, per_page: 20, @@ -149,7 +147,7 @@ export default function({ getService }) { await supertest .get('/api/kibana/management/saved_objects/_find?type=wigwags') .expect(200) - .then(resp => { + .then((resp: Response) => { expect(resp.body).to.eql({ page: 1, per_page: 20, @@ -164,15 +162,12 @@ export default function({ getService }) { await supertest .get('/api/kibana/management/saved_objects/_find') .expect(400) - .then(resp => { + .then((resp: Response) => { expect(resp.body).to.eql({ error: 'Bad Request', - message: 'child "type" fails because ["type" is required]', + message: + '[request query.type]: expected at least one defined value but got [undefined]', statusCode: 400, - validation: { - keys: ['type'], - source: 'query', - }, }); })); }); @@ -184,7 +179,7 @@ export default function({ getService }) { '/api/kibana/management/saved_objects/_find?type=visualization&page=100&perPage=100' ) .expect(200) - .then(resp => { + .then((resp: Response) => { expect(resp.body).to.eql({ page: 100, per_page: 100, @@ -199,15 +194,11 @@ export default function({ getService }) { await supertest .get('/api/kibana/management/saved_objects/_find?type=url&searchFields=a') .expect(400) - .then(resp => { + .then((resp: Response) => { expect(resp.body).to.eql({ statusCode: 400, error: 'Bad Request', - message: '"searchFields" is not allowed', - validation: { - source: 'query', - keys: ['searchFields'], - }, + message: '[request query.searchFields]: definition for this key is missing', }); })); }); @@ -221,7 +212,7 @@ export default function({ getService }) { await supertest .get('/api/kibana/management/saved_objects/_find?type=search') .expect(200) - .then(resp => { + .then((resp: Response) => { expect(resp.body.saved_objects).to.have.length(1); expect(resp.body.saved_objects[0].meta).to.eql({ icon: 'discoverApp', @@ -239,7 +230,7 @@ export default function({ getService }) { await supertest .get('/api/kibana/management/saved_objects/_find?type=dashboard') .expect(200) - .then(resp => { + .then((resp: Response) => { expect(resp.body.saved_objects).to.have.length(1); expect(resp.body.saved_objects[0].meta).to.eql({ icon: 'dashboardApp', @@ -257,7 +248,7 @@ export default function({ getService }) { await supertest .get('/api/kibana/management/saved_objects/_find?type=visualization') .expect(200) - .then(resp => { + .then((resp: Response) => { expect(resp.body.saved_objects).to.have.length(2); expect(resp.body.saved_objects[0].meta).to.eql({ icon: 'visualizeApp', @@ -285,7 +276,7 @@ export default function({ getService }) { await supertest .get('/api/kibana/management/saved_objects/_find?type=index-pattern') .expect(200) - .then(resp => { + .then((resp: Response) => { expect(resp.body.saved_objects).to.have.length(1); expect(resp.body.saved_objects[0].meta).to.eql({ icon: 'indexPatternApp', diff --git a/test/api_integration/apis/management/saved_objects/index.js b/test/api_integration/apis/saved_objects_management/index.ts similarity index 83% rename from test/api_integration/apis/management/saved_objects/index.js rename to test/api_integration/apis/saved_objects_management/index.ts index 01fab4a3c74b1..91bb3e27e99bf 100644 --- a/test/api_integration/apis/management/saved_objects/index.js +++ b/test/api_integration/apis/saved_objects_management/index.ts @@ -17,8 +17,10 @@ * under the License. */ -export default function({ loadTestFile }) { - describe('saved_objects', () => { +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function({ loadTestFile }: FtrProviderContext) { + describe('saved objects management apis', () => { loadTestFile(require.resolve('./find')); loadTestFile(require.resolve('./relationships')); }); diff --git a/test/api_integration/apis/saved_objects_management/relationships.ts b/test/api_integration/apis/saved_objects_management/relationships.ts new file mode 100644 index 0000000000000..5c16dbc337e07 --- /dev/null +++ b/test/api_integration/apis/saved_objects_management/relationships.ts @@ -0,0 +1,423 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; +import { schema } from '@kbn/config-schema'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + + const responseSchema = schema.arrayOf( + schema.object({ + id: schema.string(), + type: schema.string(), + relationship: schema.oneOf([schema.literal('parent'), schema.literal('child')]), + meta: schema.object({ + title: schema.string(), + icon: schema.string(), + editUrl: schema.string(), + inAppUrl: schema.object({ + path: schema.string(), + uiCapabilitiesPath: schema.string(), + }), + }), + }) + ); + + describe('relationships', () => { + before(async () => { + await esArchiver.load('management/saved_objects'); + }); + after(async () => { + await esArchiver.unload('management/saved_objects'); + }); + + const baseApiUrl = `/api/kibana/management/saved_objects/relationships`; + const defaultTypes = ['visualization', 'index-pattern', 'search', 'dashboard']; + + const relationshipsUrl = (type: string, id: string, types: string[] = defaultTypes) => { + const typesQuery = types.map(t => `savedObjectTypes=${t}`).join('&'); + return `${baseApiUrl}/${type}/${id}?${typesQuery}`; + }; + + describe('searches', () => { + it('should validate search response schema', async () => { + const resp = await supertest + .get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357')) + .expect(200); + + expect(() => { + responseSchema.validate(resp.body); + }).not.to.throwError(); + }); + + it('should work for searches', async () => { + const resp = await supertest + .get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357')) + .expect(200); + + expect(resp.body).to.eql([ + { + id: '8963ca30-3224-11e8-a572-ffca06da1357', + type: 'index-pattern', + relationship: 'child', + meta: { + title: 'saved_objects*', + icon: 'indexPatternApp', + editUrl: '/management/kibana/index_patterns/8963ca30-3224-11e8-a572-ffca06da1357', + inAppUrl: { + path: + '/app/kibana#/management/kibana/index_patterns/8963ca30-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'management.kibana.index_patterns', + }, + }, + }, + { + id: 'a42c0580-3224-11e8-a572-ffca06da1357', + type: 'visualization', + relationship: 'parent', + meta: { + title: 'VisualizationFromSavedSearch', + icon: 'visualizeApp', + editUrl: + '/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357', + inAppUrl: { + path: '/app/kibana#/visualize/edit/a42c0580-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + }, + }, + ]); + }); + + it('should filter based on savedObjectTypes', async () => { + const resp = await supertest + .get( + relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357', ['visualization']) + ) + .expect(200); + + expect(resp.body).to.eql([ + { + id: '8963ca30-3224-11e8-a572-ffca06da1357', + type: 'index-pattern', + meta: { + icon: 'indexPatternApp', + title: 'saved_objects*', + editUrl: '/management/kibana/index_patterns/8963ca30-3224-11e8-a572-ffca06da1357', + inAppUrl: { + path: + '/app/kibana#/management/kibana/index_patterns/8963ca30-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'management.kibana.index_patterns', + }, + }, + relationship: 'child', + }, + { + id: 'a42c0580-3224-11e8-a572-ffca06da1357', + type: 'visualization', + meta: { + icon: 'visualizeApp', + title: 'VisualizationFromSavedSearch', + editUrl: + '/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357', + inAppUrl: { + path: '/app/kibana#/visualize/edit/a42c0580-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + }, + relationship: 'parent', + }, + ]); + }); + + // TODO: https://github.com/elastic/kibana/issues/19713 causes this test to fail. + it.skip('should return 404 if search finds no results', async () => { + await supertest + .get(relationshipsUrl('search', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + .expect(404); + }); + }); + + describe('dashboards', () => { + it('should validate dashboard response schema', async () => { + const resp = await supertest + .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357')) + .expect(200); + + expect(() => { + responseSchema.validate(resp.body); + }).not.to.throwError(); + }); + + it('should work for dashboards', async () => { + const resp = await supertest + .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357')) + .expect(200); + + expect(resp.body).to.eql([ + { + id: 'add810b0-3224-11e8-a572-ffca06da1357', + type: 'visualization', + relationship: 'child', + meta: { + icon: 'visualizeApp', + title: 'Visualization', + editUrl: + '/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357', + inAppUrl: { + path: '/app/kibana#/visualize/edit/add810b0-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + }, + }, + { + id: 'a42c0580-3224-11e8-a572-ffca06da1357', + type: 'visualization', + relationship: 'child', + meta: { + icon: 'visualizeApp', + title: 'VisualizationFromSavedSearch', + editUrl: + '/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357', + inAppUrl: { + path: '/app/kibana#/visualize/edit/a42c0580-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + }, + }, + ]); + }); + + it('should filter based on savedObjectTypes', async () => { + const resp = await supertest + .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357', ['search'])) + .expect(200); + + expect(resp.body).to.eql([ + { + id: 'add810b0-3224-11e8-a572-ffca06da1357', + type: 'visualization', + meta: { + icon: 'visualizeApp', + title: 'Visualization', + editUrl: + '/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357', + inAppUrl: { + path: '/app/kibana#/visualize/edit/add810b0-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + }, + relationship: 'child', + }, + { + id: 'a42c0580-3224-11e8-a572-ffca06da1357', + type: 'visualization', + meta: { + icon: 'visualizeApp', + title: 'VisualizationFromSavedSearch', + editUrl: + '/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357', + inAppUrl: { + path: '/app/kibana#/visualize/edit/a42c0580-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + }, + relationship: 'child', + }, + ]); + }); + + // TODO: https://github.com/elastic/kibana/issues/19713 causes this test to fail. + it.skip('should return 404 if dashboard finds no results', async () => { + await supertest + .get(relationshipsUrl('dashboard', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + .expect(404); + }); + }); + + describe('visualizations', () => { + it('should validate visualization response schema', async () => { + const resp = await supertest + .get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357')) + .expect(200); + + expect(() => { + responseSchema.validate(resp.body); + }).not.to.throwError(); + }); + + it('should work for visualizations', async () => { + const resp = await supertest + .get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357')) + .expect(200); + + expect(resp.body).to.eql([ + { + id: '960372e0-3224-11e8-a572-ffca06da1357', + type: 'search', + relationship: 'child', + meta: { + icon: 'discoverApp', + title: 'OneRecord', + editUrl: + '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', + inAppUrl: { + path: '/app/kibana#/discover/960372e0-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'discover.show', + }, + }, + }, + { + id: 'b70c7ae0-3224-11e8-a572-ffca06da1357', + type: 'dashboard', + relationship: 'parent', + meta: { + icon: 'dashboardApp', + title: 'Dashboard', + editUrl: + '/management/kibana/objects/savedDashboards/b70c7ae0-3224-11e8-a572-ffca06da1357', + inAppUrl: { + path: '/app/kibana#/dashboard/b70c7ae0-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'dashboard.show', + }, + }, + }, + ]); + }); + + it('should filter based on savedObjectTypes', async () => { + const resp = await supertest + .get( + relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357', ['search']) + ) + .expect(200); + + expect(resp.body).to.eql([ + { + id: '960372e0-3224-11e8-a572-ffca06da1357', + type: 'search', + meta: { + icon: 'discoverApp', + title: 'OneRecord', + editUrl: + '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', + inAppUrl: { + path: '/app/kibana#/discover/960372e0-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'discover.show', + }, + }, + relationship: 'child', + }, + ]); + }); + + it('should return 404 if visualizations finds no results', async () => { + await supertest + .get(relationshipsUrl('visualization', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + .expect(404); + }); + }); + + describe('index patterns', () => { + it('should validate visualization response schema', async () => { + const resp = await supertest + .get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357')) + .expect(200); + + expect(() => { + responseSchema.validate(resp.body); + }).not.to.throwError(); + }); + + it('should work for index patterns', async () => { + const resp = await supertest + .get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357')) + .expect(200); + + expect(resp.body).to.eql([ + { + id: '960372e0-3224-11e8-a572-ffca06da1357', + type: 'search', + relationship: 'parent', + meta: { + icon: 'discoverApp', + title: 'OneRecord', + editUrl: + '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', + inAppUrl: { + path: '/app/kibana#/discover/960372e0-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'discover.show', + }, + }, + }, + { + id: 'add810b0-3224-11e8-a572-ffca06da1357', + type: 'visualization', + relationship: 'parent', + meta: { + icon: 'visualizeApp', + title: 'Visualization', + editUrl: + '/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357', + inAppUrl: { + path: '/app/kibana#/visualize/edit/add810b0-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + }, + }, + ]); + }); + + it('should filter based on savedObjectTypes', async () => { + const resp = await supertest + .get( + relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357', ['search']) + ) + .expect(200); + + expect(resp.body).to.eql([ + { + id: '960372e0-3224-11e8-a572-ffca06da1357', + type: 'search', + meta: { + icon: 'discoverApp', + title: 'OneRecord', + editUrl: + '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', + inAppUrl: { + path: '/app/kibana#/discover/960372e0-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'discover.show', + }, + }, + relationship: 'parent', + }, + ]); + }); + + it('should return 404 if index pattern finds no results', async () => { + await supertest + .get(relationshipsUrl('index-pattern', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + .expect(404); + }); + }); + }); +} From bc352e94894555eb47c2c0e44fa9802ab8509ae0 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 11 Mar 2020 08:33:12 +0100 Subject: [PATCH 08/20] update generated doc --- src/core/server/server.api.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 5ede98a1e6e6d..08d1958402692 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -2007,6 +2007,8 @@ export interface SavedObjectsLegacyService { // (undocumented) getScopedSavedObjectsClient: SavedObjectsClientProvider['getClient']; // (undocumented) + importAndExportableTypes: string[]; + // (undocumented) importExport: { objectLimit: number; importSavedObjects(options: SavedObjectsImportOptions): Promise; From 3982289a1708edc7c04754970122938cbd9a92da Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 11 Mar 2020 09:11:26 +0100 Subject: [PATCH 09/20] add API integration tests for /scroll/count --- .../apis/saved_objects_management/index.ts | 1 + .../saved_objects_management/relationships.ts | 4 +- .../saved_objects_management/scroll_count.ts | 86 +++++++ .../{ => relationships}/data.json.gz | Bin .../{ => relationships}/mappings.json | 0 .../saved_objects/scroll_count/data.json.gz | Bin 0 -> 1295 bytes .../saved_objects/scroll_count/mappings.json | 213 ++++++++++++++++++ 7 files changed, 302 insertions(+), 2 deletions(-) create mode 100644 test/api_integration/apis/saved_objects_management/scroll_count.ts rename test/api_integration/fixtures/es_archiver/management/saved_objects/{ => relationships}/data.json.gz (100%) rename test/api_integration/fixtures/es_archiver/management/saved_objects/{ => relationships}/mappings.json (100%) create mode 100644 test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count/data.json.gz create mode 100644 test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count/mappings.json diff --git a/test/api_integration/apis/saved_objects_management/index.ts b/test/api_integration/apis/saved_objects_management/index.ts index 91bb3e27e99bf..5895940c4bbef 100644 --- a/test/api_integration/apis/saved_objects_management/index.ts +++ b/test/api_integration/apis/saved_objects_management/index.ts @@ -23,5 +23,6 @@ export default function({ loadTestFile }: FtrProviderContext) { describe('saved objects management apis', () => { loadTestFile(require.resolve('./find')); loadTestFile(require.resolve('./relationships')); + loadTestFile(require.resolve('./scroll_count')); }); } diff --git a/test/api_integration/apis/saved_objects_management/relationships.ts b/test/api_integration/apis/saved_objects_management/relationships.ts index 5c16dbc337e07..78a437ab56cf7 100644 --- a/test/api_integration/apis/saved_objects_management/relationships.ts +++ b/test/api_integration/apis/saved_objects_management/relationships.ts @@ -44,10 +44,10 @@ export default function({ getService }: FtrProviderContext) { describe('relationships', () => { before(async () => { - await esArchiver.load('management/saved_objects'); + await esArchiver.load('management/saved_objects/relationships'); }); after(async () => { - await esArchiver.unload('management/saved_objects'); + await esArchiver.unload('management/saved_objects/relationships'); }); const baseApiUrl = `/api/kibana/management/saved_objects/relationships`; diff --git a/test/api_integration/apis/saved_objects_management/scroll_count.ts b/test/api_integration/apis/saved_objects_management/scroll_count.ts new file mode 100644 index 0000000000000..8dd3281efb98c --- /dev/null +++ b/test/api_integration/apis/saved_objects_management/scroll_count.ts @@ -0,0 +1,86 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SuperTest, Test } from 'supertest'; +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +const apiUrl = '/api/kibana/management/saved_objects/scroll/counts'; +const defaultTypes = ['visualization', 'index-pattern', 'search', 'dashboard']; + +export default function({ getService }: FtrProviderContext) { + const supertest = getService('supertest') as SuperTest; + const esArchiver = getService('esArchiver'); + + describe('scroll_count', () => { + before(async () => { + await esArchiver.load('management/saved_objects/scroll_count'); + }); + after(async () => { + await esArchiver.unload('management/saved_objects/scroll_count'); + }); + + it('returns the count for each included types', async () => { + const res = await supertest + .post(apiUrl) + .send({ + typesToInclude: defaultTypes, + }) + .expect(200); + + expect(res.body).to.eql({ + dashboard: 2, + 'index-pattern': 1, + search: 1, + visualization: 2, + }); + }); + + it('only returns count for types to include', async () => { + const res = await supertest + .post(apiUrl) + .send({ + typesToInclude: ['dashboard', 'search'], + }) + .expect(200); + + expect(res.body).to.eql({ + dashboard: 2, + search: 1, + }); + }); + + it('filters on title when `searchString` is provided', async () => { + const res = await supertest + .post(apiUrl) + .send({ + typesToInclude: defaultTypes, + searchString: 'Amazing', + }) + .expect(200); + + expect(res.body).to.eql({ + dashboard: 1, + visualization: 1, + 'index-pattern': 0, + search: 0, + }); + }); + }); +} diff --git a/test/api_integration/fixtures/es_archiver/management/saved_objects/data.json.gz b/test/api_integration/fixtures/es_archiver/management/saved_objects/relationships/data.json.gz similarity index 100% rename from test/api_integration/fixtures/es_archiver/management/saved_objects/data.json.gz rename to test/api_integration/fixtures/es_archiver/management/saved_objects/relationships/data.json.gz diff --git a/test/api_integration/fixtures/es_archiver/management/saved_objects/mappings.json b/test/api_integration/fixtures/es_archiver/management/saved_objects/relationships/mappings.json similarity index 100% rename from test/api_integration/fixtures/es_archiver/management/saved_objects/mappings.json rename to test/api_integration/fixtures/es_archiver/management/saved_objects/relationships/mappings.json diff --git a/test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count/data.json.gz b/test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..1c327e7e0769b5981367d5bc6e651dbdfa9f0658 GIT binary patch literal 1295 zcmV+q1@QVGiwFq+nP^@B17u-zVJ>QOZ*BnXm|IWdI23^2`4v_6ZAGgjy`_0WgIKW| zE}|6=OVP-Q-PAj7+_jw!rOJQbV<&AK=rZXd7FGfYCONi`kI&`v$!v~VEk~`F#0gst z=CO0E1uMidE!n^eywVht2R)(lokl1{W|Ed;Ei|L;B~prrGz>ynYp5(h;(003RKbW{)=muf8 z*YQX0AJzQ2nra`t6IIM?CD95r^5{QX@vJ`;R# z%Hvxd47t^kAJ=WsC;T|($e0UKHz&;&5fKNQvlTwEXDe=OkV&z+MYixAOe*a7AQ5|- z%l|T32b-dY@_VN&w)|-OH$TdcjyA7{Qq0pCO~PTv?YM2kmUHC&oI1s#=C~Pk{9a(; zrd(g+Oq5ZEFS7+mgWh0h;YATf7DS14$d~F|snVEtEm6!_wn(+~ zZYKnBi4JEcySLUzF4Xe{nuykBr6v0{tcZ|>eV!#Pf-%M3-M73^My-K;mhe@Q_d{Ux1o5E5s}{ zO240FX3_t8Vq^nC?wUV!d2Stj$2-D=5m{p1u{a-V;VnkLY_ma zTE5PxakW)t;Qi8Dmgf#VF#I&+m8?Q|Zj$@}CchD9tg`+mHoU-v_aFhrf#*i=LV_;b zW`pj5Bsf{1N9Yn;w|lp2jDUL0@6C6pCX6SXGewtHZ-?<$l91Hy0r%wkxXWxZ# z@I(1c57nTzec@2|b_}_Rth6c=Bk_F#czHtc3nUVn$_h(!ipjM;z(l81^}h~3=ZB&T z_-vC(e=j}%7Jdy~4u!)rx-|JD`j3f%Um!~0e;}f4`<73#e3IppEN?=VzX36INOHv) F001+VcESJv literal 0 HcmV?d00001 diff --git a/test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count/mappings.json b/test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count/mappings.json new file mode 100644 index 0000000000000..8270c573e4c1e --- /dev/null +++ b/test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count/mappings.json @@ -0,0 +1,213 @@ +{ + "type": "index", + "value": { + "index": ".kibana", + "settings": { + "index": { + "number_of_shards": "1", + "auto_expand_replicas": "0-1", + "number_of_replicas": "0" + } + }, + "mappings": { + "dynamic": "strict", + "properties": { + "config": { + "dynamic": "true", + "properties": { + "buildNum": { + "type": "keyword" + }, + "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" + } + } + }, + "index-pattern": { + "properties": { + "fieldFormatMap": { + "type": "text" + }, + "fields": { + "type": "text" + }, + "intervalName": { + "type": "keyword" + }, + "notExpandable": { + "type": "boolean" + }, + "sourceFilters": { + "type": "text" + }, + "timeFieldName": { + "type": "keyword" + }, + "title": { + "type": "text" + } + } + }, + "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" + } + } + }, + "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" + } + } + } + } + } + } +} From f350b8c5db34377e85a4ae253e14f60f1fc8548f Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 11 Mar 2020 10:40:13 +0100 Subject: [PATCH 10/20] add unit tests for plugin and routes --- src/plugins/so_management/server/index.ts | 7 ++ .../so_management/server/plugin.test.mocks.ts | 24 +++++++ .../so_management/server/plugin.test.ts | 45 +++++++++++++ src/plugins/so_management/server/plugin.ts | 10 ++- .../so_management/server/routes/find.ts | 4 +- .../so_management/server/routes/index.test.ts | 65 +++++++++++++++++++ .../so_management/server/routes/index.ts | 4 +- .../server/routes/relationships.ts | 4 +- src/plugins/so_management/server/types.ts | 15 +++++ 9 files changed, 169 insertions(+), 9 deletions(-) create mode 100644 src/plugins/so_management/server/plugin.test.mocks.ts create mode 100644 src/plugins/so_management/server/plugin.test.ts create mode 100644 src/plugins/so_management/server/routes/index.test.ts diff --git a/src/plugins/so_management/server/index.ts b/src/plugins/so_management/server/index.ts index ef4f97b1bc1ac..820118ebf3ec4 100644 --- a/src/plugins/so_management/server/index.ts +++ b/src/plugins/so_management/server/index.ts @@ -22,3 +22,10 @@ import { SavedObjectsManagementPlugin } from './plugin'; export const plugin = (context: PluginInitializerContext) => new SavedObjectsManagementPlugin(context); + +export { + SavedObjectsManagementPluginSetup, + SavedObjectsManagementPluginStart, + SavedObjectMetadata, + SavedObjectWithMetadata, +} from './types'; diff --git a/src/plugins/so_management/server/plugin.test.mocks.ts b/src/plugins/so_management/server/plugin.test.mocks.ts new file mode 100644 index 0000000000000..95dba4563a528 --- /dev/null +++ b/src/plugins/so_management/server/plugin.test.mocks.ts @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export const registerRoutesMock = jest.fn(); + +jest.doMock('./routes', () => ({ + registerRoutes: registerRoutesMock, +})); diff --git a/src/plugins/so_management/server/plugin.test.ts b/src/plugins/so_management/server/plugin.test.ts new file mode 100644 index 0000000000000..6c2c12c10efca --- /dev/null +++ b/src/plugins/so_management/server/plugin.test.ts @@ -0,0 +1,45 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { registerRoutesMock } from './plugin.test.mocks'; +import { SavedObjectsManagementPlugin } from './plugin'; +import { coreMock } from '../../../core/server/mocks'; + +describe('SavedObjectsManagementPlugin', () => { + let plugin: SavedObjectsManagementPlugin; + + beforeEach(() => { + plugin = new SavedObjectsManagementPlugin(coreMock.createPluginInitializerContext()); + }); + + describe('#setup', () => { + it('registers the routes', async () => { + const coreSetup = coreMock.createSetup(); + + await plugin.setup(coreSetup); + + expect(registerRoutesMock).toHaveBeenCalledTimes(1); + expect(registerRoutesMock).toHaveBeenCalledWith( + expect.objectContaining({ + http: coreSetup.http, + }) + ); + }); + }); +}); diff --git a/src/plugins/so_management/server/plugin.ts b/src/plugins/so_management/server/plugin.ts index d5dc0dad2cbea..562e9e5b07d44 100644 --- a/src/plugins/so_management/server/plugin.ts +++ b/src/plugins/so_management/server/plugin.ts @@ -20,10 +20,12 @@ import { Subject } from 'rxjs'; import { first } from 'rxjs/operators'; import { CoreSetup, CoreStart, Logger, Plugin, PluginInitializerContext } from 'src/core/server'; +import { SavedObjectsManagementPluginSetup, SavedObjectsManagementPluginStart } from './types'; import { SavedObjectsManagement } from './services'; import { registerRoutes } from './routes'; -export class SavedObjectsManagementPlugin implements Plugin<{}, {}> { +export class SavedObjectsManagementPlugin + implements Plugin { private readonly logger: Logger; private managementService$ = new Subject(); @@ -33,7 +35,6 @@ export class SavedObjectsManagementPlugin implements Plugin<{}, {}> { public async setup({ http }: CoreSetup) { this.logger.debug('Setting up SavedObjectsManagement plugin'); - registerRoutes({ http, managementServicePromise: this.managementService$.pipe(first()).toPromise(), @@ -46,6 +47,9 @@ export class SavedObjectsManagementPlugin implements Plugin<{}, {}> { this.logger.debug('Starting up SavedObjectsManagement plugin'); const managementService = new SavedObjectsManagement(core.savedObjects.getTypeRegistry()); this.managementService$.next(managementService); - return {}; + + return { + management: managementService, + }; } } diff --git a/src/plugins/so_management/server/routes/find.ts b/src/plugins/so_management/server/routes/find.ts index 44c80201e98fe..3a3ac5db63288 100644 --- a/src/plugins/so_management/server/routes/find.ts +++ b/src/plugins/so_management/server/routes/find.ts @@ -20,11 +20,11 @@ import { schema } from '@kbn/config-schema'; import { IRouter } from 'src/core/server'; import { injectMetaAttributes } from '../lib'; -import { SavedObjectsManagement } from '../services'; +import { ISavedObjectsManagement } from '../services'; export const registerFindRoute = ( router: IRouter, - managementServicePromise: Promise + managementServicePromise: Promise ) => { router.get( { diff --git a/src/plugins/so_management/server/routes/index.test.ts b/src/plugins/so_management/server/routes/index.test.ts new file mode 100644 index 0000000000000..f183972953dce --- /dev/null +++ b/src/plugins/so_management/server/routes/index.test.ts @@ -0,0 +1,65 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { registerRoutes } from './index'; +import { ISavedObjectsManagement } from '../services'; +import { coreMock, httpServiceMock } from '../../../../core/server/mocks'; + +describe('registerRoutes', () => { + it('registers the management routes', () => { + const router = httpServiceMock.createRouter(); + const httpSetup = coreMock.createSetup().http; + httpSetup.createRouter.mockReturnValue(router); + const managementPromise = Promise.resolve({} as ISavedObjectsManagement); + + registerRoutes({ + http: httpSetup, + managementServicePromise: managementPromise, + }); + + expect(httpSetup.createRouter).toHaveBeenCalledTimes(1); + expect(router.get).toHaveBeenCalledTimes(2); + expect(router.post).toHaveBeenCalledTimes(2); + + expect(router.get).toHaveBeenCalledWith( + expect.objectContaining({ + path: '/api/kibana/management/saved_objects/_find', + }), + expect.any(Function) + ); + expect(router.get).toHaveBeenCalledWith( + expect.objectContaining({ + path: '/api/kibana/management/saved_objects/relationships/{type}/{id}', + }), + expect.any(Function) + ); + expect(router.post).toHaveBeenCalledWith( + expect.objectContaining({ + path: '/api/kibana/management/saved_objects/scroll/counts', + }), + expect.any(Function) + ); + expect(router.post).toHaveBeenCalledWith( + expect.objectContaining({ + path: '/api/kibana/management/saved_objects/scroll/export', + }), + expect.any(Function) + ); + }); +}); diff --git a/src/plugins/so_management/server/routes/index.ts b/src/plugins/so_management/server/routes/index.ts index c952838748afe..2c6adb71ed3ce 100644 --- a/src/plugins/so_management/server/routes/index.ts +++ b/src/plugins/so_management/server/routes/index.ts @@ -18,7 +18,7 @@ */ import { HttpServiceSetup } from 'src/core/server'; -import { SavedObjectsManagement } from '../services'; +import { ISavedObjectsManagement } from '../services'; import { registerFindRoute } from './find'; import { registerScrollForCountRoute } from './scroll_count'; import { registerScrollForExportRoute } from './scroll_export'; @@ -26,7 +26,7 @@ import { registerRelationshipsRoute } from './relationships'; interface RegisterRouteOptions { http: HttpServiceSetup; - managementServicePromise: Promise; + managementServicePromise: Promise; } export function registerRoutes({ http, managementServicePromise }: RegisterRouteOptions) { diff --git a/src/plugins/so_management/server/routes/relationships.ts b/src/plugins/so_management/server/routes/relationships.ts index 54818012d89a0..07a928b062dae 100644 --- a/src/plugins/so_management/server/routes/relationships.ts +++ b/src/plugins/so_management/server/routes/relationships.ts @@ -20,11 +20,11 @@ import { schema } from '@kbn/config-schema'; import { IRouter } from 'src/core/server'; import { findRelationships } from '../lib'; -import { SavedObjectsManagement } from '../services'; +import { ISavedObjectsManagement } from '../services'; export const registerRelationshipsRoute = ( router: IRouter, - managementServicePromise: Promise + managementServicePromise: Promise ) => { router.get( { diff --git a/src/plugins/so_management/server/types.ts b/src/plugins/so_management/server/types.ts index 5d1495ba0506e..6aaf51d53815a 100644 --- a/src/plugins/so_management/server/types.ts +++ b/src/plugins/so_management/server/types.ts @@ -18,7 +18,19 @@ */ import { SavedObject } from 'src/core/server'; +import { ISavedObjectsManagement } from './services'; +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface SavedObjectsManagementPluginSetup {} + +export interface SavedObjectsManagementPluginStart { + management: ISavedObjectsManagement; +} + +/** + * The metadata injected into a {@link SavedObject | saved object} when returning + * {@link SavedObjectWithMetadata | enhanced objects} from the plugin API endpoints. + */ export interface SavedObjectMetadata { icon?: string; title?: string; @@ -26,6 +38,9 @@ export interface SavedObjectMetadata { inAppUrl?: { path: string; uiCapabilitiesPath: string }; } +/** + * A {@link SavedObject | saved object} enhanced with meta properties used by the client-side plugin. + */ export type SavedObjectWithMetadata = SavedObject & { meta: SavedObjectMetadata; }; From 07bfe521b10bf393f2d772fd3160691968c94e87 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 11 Mar 2020 10:56:30 +0100 Subject: [PATCH 11/20] add injectMetaAttributes tests --- .../server/lib/inject_meta_attributes.test.ts | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 src/plugins/so_management/server/lib/inject_meta_attributes.test.ts diff --git a/src/plugins/so_management/server/lib/inject_meta_attributes.test.ts b/src/plugins/so_management/server/lib/inject_meta_attributes.test.ts new file mode 100644 index 0000000000000..0c0f9d8feb506 --- /dev/null +++ b/src/plugins/so_management/server/lib/inject_meta_attributes.test.ts @@ -0,0 +1,82 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObject } from 'src/core/server'; +import { injectMetaAttributes } from './inject_meta_attributes'; +import { managementMock } from '../services/management.mock'; + +describe('injectMetaAttributes', () => { + let managementService: ReturnType; + + beforeEach(() => { + managementService = managementMock.create(); + + managementService.getIcon.mockReturnValue('icon'); + managementService.getTitle.mockReturnValue('title'); + managementService.getEditUrl.mockReturnValue('editUrl'); + managementService.getInAppUrl.mockReturnValue({ + path: 'path', + uiCapabilitiesPath: 'uiCapabilitiesPath', + }); + }); + + it('inject the metadata to the obj', () => { + const obj: SavedObject = { + id: 'id', + type: 'config', + attributes: { some: 'value' }, + references: [], + }; + + const objWithMeta = injectMetaAttributes(obj, managementService); + expect(objWithMeta).toStrictEqual({ + id: 'id', + type: 'config', + attributes: { some: 'value' }, + references: [], + meta: { + icon: 'icon', + title: 'title', + editUrl: 'editUrl', + inAppUrl: { + path: 'path', + uiCapabilitiesPath: 'uiCapabilitiesPath', + }, + }, + }); + }); + + it('does not alter the original object', () => { + const obj: SavedObject = { + id: 'id', + type: 'config', + attributes: { some: 'value' }, + references: [], + }; + + injectMetaAttributes(obj, managementService); + + expect(obj).toStrictEqual({ + id: 'id', + type: 'config', + attributes: { some: 'value' }, + references: [], + }); + }); +}); From 5a9d9f1223f548874e9255ac3ea180a262632aaa Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 11 Mar 2020 11:23:48 +0100 Subject: [PATCH 12/20] extract relation type --- .../so_management/server/lib/find_relationships.ts | 9 +-------- src/plugins/so_management/server/types.ts | 10 ++++++++++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/plugins/so_management/server/lib/find_relationships.ts b/src/plugins/so_management/server/lib/find_relationships.ts index 2031ad708ea14..7c01ee652155b 100644 --- a/src/plugins/so_management/server/lib/find_relationships.ts +++ b/src/plugins/so_management/server/lib/find_relationships.ts @@ -20,14 +20,7 @@ import { SavedObjectsClientContract } from 'src/core/server'; import { injectMetaAttributes } from './inject_meta_attributes'; import { ISavedObjectsManagement } from '../services'; -import { SavedObjectMetadata, SavedObjectWithMetadata } from '../types'; - -interface SavedObjectRelation { - id: string; - type: string; - relationship: 'child' | 'parent'; - meta: SavedObjectMetadata; -} +import { SavedObjectRelation, SavedObjectWithMetadata } from '../types'; export async function findRelationships({ type, diff --git a/src/plugins/so_management/server/types.ts b/src/plugins/so_management/server/types.ts index 6aaf51d53815a..3953946b12468 100644 --- a/src/plugins/so_management/server/types.ts +++ b/src/plugins/so_management/server/types.ts @@ -44,3 +44,13 @@ export interface SavedObjectMetadata { export type SavedObjectWithMetadata = SavedObject & { meta: SavedObjectMetadata; }; + +/** + * Represents a relation between two {@link SavedObject | saved object} + */ +export interface SavedObjectRelation { + id: string; + type: string; + relationship: 'child' | 'parent'; + meta: SavedObjectMetadata; +} From fd70c8bec80a8a8351f18f476fc37ccd964fff3a Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 11 Mar 2020 12:13:31 +0100 Subject: [PATCH 13/20] add find_relationships tests --- .../server/lib/find_relationships.test.ts | 213 ++++++++++++++++++ .../server/lib/find_relationships.ts | 12 +- .../server/routes/relationships.ts | 2 +- 3 files changed, 220 insertions(+), 7 deletions(-) create mode 100644 src/plugins/so_management/server/lib/find_relationships.test.ts diff --git a/src/plugins/so_management/server/lib/find_relationships.test.ts b/src/plugins/so_management/server/lib/find_relationships.test.ts new file mode 100644 index 0000000000000..18846eeebdfb7 --- /dev/null +++ b/src/plugins/so_management/server/lib/find_relationships.test.ts @@ -0,0 +1,213 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { findRelationships } from './find_relationships'; +import { managementMock } from '../services/management.mock'; +import { savedObjectsClientMock } from '../../../../core/server/mocks'; + +describe('findRelationships', () => { + let savedObjectsClient: ReturnType; + let managementService: ReturnType; + + beforeEach(() => { + savedObjectsClient = savedObjectsClientMock.create(); + managementService = managementMock.create(); + }); + + it('returns the child and parent references of the object', async () => { + const type = 'dashboard'; + const id = 'some-id'; + const references = [ + { + type: 'some-type', + id: 'ref-1', + name: 'ref 1', + }, + { + type: 'another-type', + id: 'ref-2', + name: 'ref 2', + }, + ]; + const referenceTypes = ['some-type', 'another-type']; + + savedObjectsClient.get.mockResolvedValue({ + id, + type, + attributes: {}, + references, + }); + + savedObjectsClient.bulkGet.mockResolvedValue({ + saved_objects: [ + { + type: 'some-type', + id: 'ref-1', + attributes: {}, + references: [], + }, + { + type: 'another-type', + id: 'ref-2', + attributes: {}, + references: [], + }, + ], + }); + + savedObjectsClient.find.mockResolvedValue({ + saved_objects: [ + { + type: 'parent-type', + id: 'parent-id', + attributes: {}, + references: [], + }, + ], + total: 1, + per_page: 20, + page: 1, + }); + + const relationships = await findRelationships({ + type, + id, + size: 20, + client: savedObjectsClient, + referenceTypes, + savedObjectsManagement: managementService, + }); + + expect(savedObjectsClient.get).toHaveBeenCalledTimes(1); + expect(savedObjectsClient.get).toHaveBeenCalledWith(type, id); + + expect(savedObjectsClient.bulkGet).toHaveBeenCalledTimes(1); + expect(savedObjectsClient.bulkGet).toHaveBeenCalledWith( + references.map(ref => ({ + id: ref.id, + type: ref.type, + })) + ); + + expect(savedObjectsClient.find).toHaveBeenCalledTimes(1); + expect(savedObjectsClient.find).toHaveBeenCalledWith({ + hasReference: { type, id }, + perPage: 20, + type: referenceTypes, + }); + + expect(relationships).toEqual([ + { + id: 'ref-1', + relationship: 'child', + type: 'some-type', + meta: expect.any(Object), + }, + { + id: 'ref-2', + relationship: 'child', + type: 'another-type', + meta: expect.any(Object), + }, + { + id: 'parent-id', + relationship: 'parent', + type: 'parent-type', + meta: expect.any(Object), + }, + ]); + }); + + it('uses the management service to consolidate the relationship objects', async () => { + const type = 'dashboard'; + const id = 'some-id'; + const references = [ + { + type: 'some-type', + id: 'ref-1', + name: 'ref 1', + }, + ]; + const referenceTypes = ['some-type', 'another-type']; + + managementService.getIcon.mockReturnValue('icon'); + managementService.getTitle.mockReturnValue('title'); + managementService.getEditUrl.mockReturnValue('editUrl'); + managementService.getInAppUrl.mockReturnValue({ + path: 'path', + uiCapabilitiesPath: 'uiCapabilitiesPath', + }); + + savedObjectsClient.get.mockResolvedValue({ + id, + type, + attributes: {}, + references, + }); + + savedObjectsClient.bulkGet.mockResolvedValue({ + saved_objects: [ + { + type: 'some-type', + id: 'ref-1', + attributes: {}, + references: [], + }, + ], + }); + + savedObjectsClient.find.mockResolvedValue({ + saved_objects: [], + total: 0, + per_page: 20, + page: 1, + }); + + const relationships = await findRelationships({ + type, + id, + size: 20, + client: savedObjectsClient, + referenceTypes, + savedObjectsManagement: managementService, + }); + + expect(managementService.getIcon).toHaveBeenCalledTimes(1); + expect(managementService.getTitle).toHaveBeenCalledTimes(1); + expect(managementService.getEditUrl).toHaveBeenCalledTimes(1); + expect(managementService.getInAppUrl).toHaveBeenCalledTimes(1); + + expect(relationships).toEqual([ + { + id: 'ref-1', + relationship: 'child', + type: 'some-type', + meta: { + title: 'title', + icon: 'icon', + editUrl: 'editUrl', + inAppUrl: { + path: 'path', + uiCapabilitiesPath: 'uiCapabilitiesPath', + }, + }, + }, + ]); + }); +}); diff --git a/src/plugins/so_management/server/lib/find_relationships.ts b/src/plugins/so_management/server/lib/find_relationships.ts index 7c01ee652155b..cca8831afd16c 100644 --- a/src/plugins/so_management/server/lib/find_relationships.ts +++ b/src/plugins/so_management/server/lib/find_relationships.ts @@ -27,14 +27,14 @@ export async function findRelationships({ id, size, client, - savedObjectTypes, + referenceTypes, savedObjectsManagement, }: { type: string; id: string; size: number; client: SavedObjectsClientContract; - savedObjectTypes: string[]; + referenceTypes: string[]; savedObjectsManagement: ISavedObjectsManagement; }): Promise { const { references = [] } = await client.get(type, id); @@ -44,18 +44,18 @@ export async function findRelationships({ references.map(ref => [`${ref.type}:${ref.id}`, { id: ref.id, type: ref.type }]) ); - const [referencedObjects, referencedResponse] = await Promise.all([ + const [childReferencesResponse, parentReferencesResponse] = await Promise.all([ referencedToBulkGetOpts.size > 0 ? client.bulkGet([...referencedToBulkGetOpts.values()]) : Promise.resolve({ saved_objects: [] }), client.find({ hasReference: { type, id }, perPage: size, - type: savedObjectTypes, + type: referenceTypes, }), ]); - return referencedObjects.saved_objects + return childReferencesResponse.saved_objects .map(obj => injectMetaAttributes(obj, savedObjectsManagement)) .map(extractCommonProperties) .map( @@ -66,7 +66,7 @@ export async function findRelationships({ } as SavedObjectRelation) ) .concat( - referencedResponse.saved_objects + parentReferencesResponse.saved_objects .map(obj => injectMetaAttributes(obj, savedObjectsManagement)) .map(extractCommonProperties) .map( diff --git a/src/plugins/so_management/server/routes/relationships.ts b/src/plugins/so_management/server/routes/relationships.ts index 07a928b062dae..c9001deb91237 100644 --- a/src/plugins/so_management/server/routes/relationships.ts +++ b/src/plugins/so_management/server/routes/relationships.ts @@ -54,7 +54,7 @@ export const registerRelationshipsRoute = ( id, client, size, - savedObjectTypes, + referenceTypes: savedObjectTypes, savedObjectsManagement: managementService, }); From b24b37f40b425277ee444e84a7dfdd35bdf85fe4 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 11 Mar 2020 12:31:17 +0100 Subject: [PATCH 14/20] add find_all tests --- .../so_management/server/lib/find_all.test.ts | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/plugins/so_management/server/lib/find_all.test.ts diff --git a/src/plugins/so_management/server/lib/find_all.test.ts b/src/plugins/so_management/server/lib/find_all.test.ts new file mode 100644 index 0000000000000..98e669c093178 --- /dev/null +++ b/src/plugins/so_management/server/lib/find_all.test.ts @@ -0,0 +1,95 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { times } from 'lodash'; +import { SavedObjectsFindOptions, SavedObject } from 'src/core/server'; +import { savedObjectsClientMock } from '../../../../core/server/mocks'; +import { findAll } from './find_all'; + +describe('findAll', () => { + let savedObjectsClient: ReturnType; + + const createObj = (id: number): SavedObject => ({ + type: 'type', + id: `id-${id}`, + attributes: {}, + references: [], + }); + + beforeEach(() => { + savedObjectsClient = savedObjectsClientMock.create(); + }); + + it('calls the saved object client with the correct parameters', async () => { + const query: SavedObjectsFindOptions = { + type: ['some-type', 'another-type'], + }; + + savedObjectsClient.find.mockResolvedValue({ + saved_objects: [createObj(1), createObj(2)], + total: 1, + per_page: 20, + page: 1, + }); + + const results = await findAll(savedObjectsClient, query); + + expect(savedObjectsClient.find).toHaveBeenCalledTimes(1); + expect(savedObjectsClient.find).toHaveBeenCalledWith({ + ...query, + page: 1, + }); + + expect(results).toEqual([createObj(1), createObj(2)]); + }); + + it('recursively call find until all objects are fetched', async () => { + const query: SavedObjectsFindOptions = { + type: ['some-type', 'another-type'], + }; + const objPerPage = 2; + + savedObjectsClient.find.mockImplementation(({ page }) => { + const firstInPage = (page! - 1) * objPerPage + 1; + return Promise.resolve({ + saved_objects: [createObj(firstInPage), createObj(firstInPage + 1)], + total: objPerPage * 3, + per_page: objPerPage, + page: page!, + }); + }); + + const results = await findAll(savedObjectsClient, query); + expect(savedObjectsClient.find).toHaveBeenCalledTimes(3); + expect(savedObjectsClient.find).toHaveBeenCalledWith({ + ...query, + page: 1, + }); + expect(savedObjectsClient.find).toHaveBeenCalledWith({ + ...query, + page: 2, + }); + expect(savedObjectsClient.find).toHaveBeenCalledWith({ + ...query, + page: 3, + }); + + expect(results).toEqual(times(6, num => createObj(num + 1))); + }); +}); From 23581cd43427c4a747aeb00471eea015966886c5 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Mon, 16 Mar 2020 09:58:57 +0100 Subject: [PATCH 15/20] do not complete migrator$ to avoid unhandled promise rejection --- src/core/server/saved_objects/saved_objects_service.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/server/saved_objects/saved_objects_service.ts b/src/core/server/saved_objects/saved_objects_service.ts index 943be5b7c5c22..175eac3c1bd95 100644 --- a/src/core/server/saved_objects/saved_objects_service.ts +++ b/src/core/server/saved_objects/saved_objects_service.ts @@ -450,9 +450,7 @@ export class SavedObjectsService }; } - public async stop() { - this.migrator$.complete(); - } + public async stop() {} private createMigrator( kibanaConfig: KibanaConfigType, From 9f5df694b1b791b81408064133e059b8627b6e80 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Mon, 16 Mar 2020 12:36:38 +0100 Subject: [PATCH 16/20] fix data for search endpoint integration tests --- .../apis/saved_objects_management/find.ts | 4 +- .../saved_objects/search/data.json.gz | Bin 0 -> 1385 bytes .../saved_objects/search/mappings.json | 283 ++++++++++++++++++ 3 files changed, 285 insertions(+), 2 deletions(-) create mode 100644 test/api_integration/fixtures/es_archiver/management/saved_objects/search/data.json.gz create mode 100644 test/api_integration/fixtures/es_archiver/management/saved_objects/search/mappings.json diff --git a/test/api_integration/apis/saved_objects_management/find.ts b/test/api_integration/apis/saved_objects_management/find.ts index 350cfb3dd987b..9b3eda4c0664b 100644 --- a/test/api_integration/apis/saved_objects_management/find.ts +++ b/test/api_integration/apis/saved_objects_management/find.ts @@ -205,8 +205,8 @@ export default function({ getService }: FtrProviderContext) { }); describe('meta attributes injected properly', () => { - before(() => esArchiver.load('management/saved_objects')); - after(() => esArchiver.unload('management/saved_objects')); + before(() => esArchiver.load('management/saved_objects/search')); + after(() => esArchiver.unload('management/saved_objects/search')); it('should inject meta attributes for searches', async () => await supertest diff --git a/test/api_integration/fixtures/es_archiver/management/saved_objects/search/data.json.gz b/test/api_integration/fixtures/es_archiver/management/saved_objects/search/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..0834567abb66b663079894089ed4edd91f1cf0b3 GIT binary patch literal 1385 zcmV-v1(y0BiwFP!000026V+JVZ`(Eyf6rfGXfIn48~X5vthb^?hYW2R#6}+$2LUCX zWv;U5QB=~@(Eq+8C0mrECGvtGnI97Qcs$m8BGZD22gy7Lt@`B_*dyDA^hk#?yYb0+4|-wU-`D?Y;|<*LNK7`ym-mNf3G{|YrRCa=-?zQK>&=}>F!BP=9{3aY&szV$ zPJNPIlZig;9PWB^RQ!yJy;cWFiU@hL0v4~-Deh#{s>PFhohtX;wq?QZ4%co$WMx=R zB`i*Me~Xji3UJ=YzUfFYxa+g~mtVvi|tywT)oz%*<= zi5GuvJAv&7-f-YfZ38b&GwpE6$Sqpr;a?ER?46mNC4+>j8?~;s3o9jSSXjZrx?yx- zoi4PmT98S>(pbwPo~IIpHa?f20#pu`B*{RDfCx-=n5d0XmXn12Br5R%8M=`@ z@}CLbhRu!`o(7ITn0jLa!%Z{oQ2u7>C=%5$m^G`Xv^A4>dX;v)U*G*>2Ab4gu{Me} zM38k>=5_<(qRgYC8^Ma-UEr+BNOFnerr8g01%b(;?7c$HXSju=v5wVInk;MUtU_j* zCkZZ7CJ@;r!j!0}OwPF^iD5>n@1OECDm!PsE@6e8M;)dHHPzB^$?- z?M*kg6|9LCDn4e>!6g(0Le;qIoaw7Jstj+xx-H}8jtv+;9r-G&Q+TF9egr4K5YHH8 z{cqgx2rs+_6Hw|qcK9kx;9)l#d(UBl<4eC;>#aD)Dx!4Gc_P`ynCU3}3^AnU?AK;z z_gIkzW>#XJzi?{K$aw~rhyXB&0jqKX zJa<^$+w06QBYQBm%~_*1(atU(9~^P?Z)F>jVs-73tAHE}MnB@)V3{9PZthSGn5rm8 z`0%4D)BEZ_+u^=w44ezge2uHHjc1+h!Q(X9?e+ojRVCGh^vkNlw_r+D<$ciabY&Ht zc8p0&nnAh82jzARs>4kCNKn^i4!O>3W>hF8;`(&bg?DMtAR@76`}lotR1KQp@| literal 0 HcmV?d00001 diff --git a/test/api_integration/fixtures/es_archiver/management/saved_objects/search/mappings.json b/test/api_integration/fixtures/es_archiver/management/saved_objects/search/mappings.json new file mode 100644 index 0000000000000..c670508247b1a --- /dev/null +++ b/test/api_integration/fixtures/es_archiver/management/saved_objects/search/mappings.json @@ -0,0 +1,283 @@ +{ + "type": "index", + "value": { + "index": ".kibana", + "settings": { + "index": { + "number_of_shards": "1", + "auto_expand_replicas": "0-1", + "number_of_replicas": "0" + } + }, + "mappings": { + "dynamic": "strict", + "properties": { + "config": { + "dynamic": "true", + "properties": { + "buildNum": { + "type": "keyword" + }, + "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" + } + } + }, + "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" + } + } + }, + "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" + } + } + }, + "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" + } + } + } + } + } + } +} \ No newline at end of file From b1ef936f88438729d1ffa9dc195a53e97f22dc74 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Mon, 16 Mar 2020 13:05:38 +0100 Subject: [PATCH 17/20] remove falsy comment --- src/legacy/core_plugins/kibana/inject_vars.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/legacy/core_plugins/kibana/inject_vars.js b/src/legacy/core_plugins/kibana/inject_vars.js index c26c1966f8e66..76d1704907ab5 100644 --- a/src/legacy/core_plugins/kibana/inject_vars.js +++ b/src/legacy/core_plugins/kibana/inject_vars.js @@ -20,7 +20,6 @@ export function injectVars(server) { const serverConfig = server.config(); - // Get types that are import and exportable, by default yes unless isImportableAndExportable is set to false const { importAndExportableTypes } = server.savedObjects; return { From dc4e2bd678892f6e2d9cafcc151f100898288141 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 18 Mar 2020 18:30:04 +0100 Subject: [PATCH 18/20] rename plugin folder to match plugin id --- .../{so_management => saved_objects_management}/kibana.json | 0 .../{so_management => saved_objects_management}/server/index.ts | 0 .../server/lib/find_all.test.ts | 0 .../server/lib/find_all.ts | 0 .../server/lib/find_relationships.test.ts | 0 .../server/lib/find_relationships.ts | 0 .../server/lib/index.ts | 0 .../server/lib/inject_meta_attributes.test.ts | 0 .../server/lib/inject_meta_attributes.ts | 0 .../server/plugin.test.mocks.ts | 0 .../server/plugin.test.ts | 0 .../{so_management => saved_objects_management}/server/plugin.ts | 0 .../server/routes/find.ts | 0 .../server/routes/index.test.ts | 0 .../server/routes/index.ts | 0 .../server/routes/relationships.ts | 0 .../server/routes/scroll_count.ts | 0 .../server/routes/scroll_export.ts | 0 .../server/services/index.ts | 0 .../server/services/management.mock.ts | 0 .../server/services/management.test.ts | 0 .../server/services/management.ts | 0 .../{so_management => saved_objects_management}/server/types.ts | 0 23 files changed, 0 insertions(+), 0 deletions(-) rename src/plugins/{so_management => saved_objects_management}/kibana.json (100%) rename src/plugins/{so_management => saved_objects_management}/server/index.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/lib/find_all.test.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/lib/find_all.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/lib/find_relationships.test.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/lib/find_relationships.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/lib/index.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/lib/inject_meta_attributes.test.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/lib/inject_meta_attributes.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/plugin.test.mocks.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/plugin.test.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/plugin.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/routes/find.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/routes/index.test.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/routes/index.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/routes/relationships.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/routes/scroll_count.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/routes/scroll_export.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/services/index.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/services/management.mock.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/services/management.test.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/services/management.ts (100%) rename src/plugins/{so_management => saved_objects_management}/server/types.ts (100%) diff --git a/src/plugins/so_management/kibana.json b/src/plugins/saved_objects_management/kibana.json similarity index 100% rename from src/plugins/so_management/kibana.json rename to src/plugins/saved_objects_management/kibana.json diff --git a/src/plugins/so_management/server/index.ts b/src/plugins/saved_objects_management/server/index.ts similarity index 100% rename from src/plugins/so_management/server/index.ts rename to src/plugins/saved_objects_management/server/index.ts diff --git a/src/plugins/so_management/server/lib/find_all.test.ts b/src/plugins/saved_objects_management/server/lib/find_all.test.ts similarity index 100% rename from src/plugins/so_management/server/lib/find_all.test.ts rename to src/plugins/saved_objects_management/server/lib/find_all.test.ts diff --git a/src/plugins/so_management/server/lib/find_all.ts b/src/plugins/saved_objects_management/server/lib/find_all.ts similarity index 100% rename from src/plugins/so_management/server/lib/find_all.ts rename to src/plugins/saved_objects_management/server/lib/find_all.ts diff --git a/src/plugins/so_management/server/lib/find_relationships.test.ts b/src/plugins/saved_objects_management/server/lib/find_relationships.test.ts similarity index 100% rename from src/plugins/so_management/server/lib/find_relationships.test.ts rename to src/plugins/saved_objects_management/server/lib/find_relationships.test.ts diff --git a/src/plugins/so_management/server/lib/find_relationships.ts b/src/plugins/saved_objects_management/server/lib/find_relationships.ts similarity index 100% rename from src/plugins/so_management/server/lib/find_relationships.ts rename to src/plugins/saved_objects_management/server/lib/find_relationships.ts diff --git a/src/plugins/so_management/server/lib/index.ts b/src/plugins/saved_objects_management/server/lib/index.ts similarity index 100% rename from src/plugins/so_management/server/lib/index.ts rename to src/plugins/saved_objects_management/server/lib/index.ts diff --git a/src/plugins/so_management/server/lib/inject_meta_attributes.test.ts b/src/plugins/saved_objects_management/server/lib/inject_meta_attributes.test.ts similarity index 100% rename from src/plugins/so_management/server/lib/inject_meta_attributes.test.ts rename to src/plugins/saved_objects_management/server/lib/inject_meta_attributes.test.ts diff --git a/src/plugins/so_management/server/lib/inject_meta_attributes.ts b/src/plugins/saved_objects_management/server/lib/inject_meta_attributes.ts similarity index 100% rename from src/plugins/so_management/server/lib/inject_meta_attributes.ts rename to src/plugins/saved_objects_management/server/lib/inject_meta_attributes.ts diff --git a/src/plugins/so_management/server/plugin.test.mocks.ts b/src/plugins/saved_objects_management/server/plugin.test.mocks.ts similarity index 100% rename from src/plugins/so_management/server/plugin.test.mocks.ts rename to src/plugins/saved_objects_management/server/plugin.test.mocks.ts diff --git a/src/plugins/so_management/server/plugin.test.ts b/src/plugins/saved_objects_management/server/plugin.test.ts similarity index 100% rename from src/plugins/so_management/server/plugin.test.ts rename to src/plugins/saved_objects_management/server/plugin.test.ts diff --git a/src/plugins/so_management/server/plugin.ts b/src/plugins/saved_objects_management/server/plugin.ts similarity index 100% rename from src/plugins/so_management/server/plugin.ts rename to src/plugins/saved_objects_management/server/plugin.ts diff --git a/src/plugins/so_management/server/routes/find.ts b/src/plugins/saved_objects_management/server/routes/find.ts similarity index 100% rename from src/plugins/so_management/server/routes/find.ts rename to src/plugins/saved_objects_management/server/routes/find.ts diff --git a/src/plugins/so_management/server/routes/index.test.ts b/src/plugins/saved_objects_management/server/routes/index.test.ts similarity index 100% rename from src/plugins/so_management/server/routes/index.test.ts rename to src/plugins/saved_objects_management/server/routes/index.test.ts diff --git a/src/plugins/so_management/server/routes/index.ts b/src/plugins/saved_objects_management/server/routes/index.ts similarity index 100% rename from src/plugins/so_management/server/routes/index.ts rename to src/plugins/saved_objects_management/server/routes/index.ts diff --git a/src/plugins/so_management/server/routes/relationships.ts b/src/plugins/saved_objects_management/server/routes/relationships.ts similarity index 100% rename from src/plugins/so_management/server/routes/relationships.ts rename to src/plugins/saved_objects_management/server/routes/relationships.ts diff --git a/src/plugins/so_management/server/routes/scroll_count.ts b/src/plugins/saved_objects_management/server/routes/scroll_count.ts similarity index 100% rename from src/plugins/so_management/server/routes/scroll_count.ts rename to src/plugins/saved_objects_management/server/routes/scroll_count.ts diff --git a/src/plugins/so_management/server/routes/scroll_export.ts b/src/plugins/saved_objects_management/server/routes/scroll_export.ts similarity index 100% rename from src/plugins/so_management/server/routes/scroll_export.ts rename to src/plugins/saved_objects_management/server/routes/scroll_export.ts diff --git a/src/plugins/so_management/server/services/index.ts b/src/plugins/saved_objects_management/server/services/index.ts similarity index 100% rename from src/plugins/so_management/server/services/index.ts rename to src/plugins/saved_objects_management/server/services/index.ts diff --git a/src/plugins/so_management/server/services/management.mock.ts b/src/plugins/saved_objects_management/server/services/management.mock.ts similarity index 100% rename from src/plugins/so_management/server/services/management.mock.ts rename to src/plugins/saved_objects_management/server/services/management.mock.ts diff --git a/src/plugins/so_management/server/services/management.test.ts b/src/plugins/saved_objects_management/server/services/management.test.ts similarity index 100% rename from src/plugins/so_management/server/services/management.test.ts rename to src/plugins/saved_objects_management/server/services/management.test.ts diff --git a/src/plugins/so_management/server/services/management.ts b/src/plugins/saved_objects_management/server/services/management.ts similarity index 100% rename from src/plugins/so_management/server/services/management.ts rename to src/plugins/saved_objects_management/server/services/management.ts diff --git a/src/plugins/so_management/server/types.ts b/src/plugins/saved_objects_management/server/types.ts similarity index 100% rename from src/plugins/so_management/server/types.ts rename to src/plugins/saved_objects_management/server/types.ts From 703bc809c250d5d94ec088ba2bbbfbd481ce843e Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 18 Mar 2020 18:39:31 +0100 Subject: [PATCH 19/20] address review comments --- .../saved_objects_management/server/plugin.ts | 4 +--- .../server/routes/find.ts | 2 +- .../saved_objects_management/server/types.ts | 6 ++---- .../saved_objects_management/scroll_count.ts | 16 ++++++++++++++++ 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/plugins/saved_objects_management/server/plugin.ts b/src/plugins/saved_objects_management/server/plugin.ts index 562e9e5b07d44..b72644b500967 100644 --- a/src/plugins/saved_objects_management/server/plugin.ts +++ b/src/plugins/saved_objects_management/server/plugin.ts @@ -48,8 +48,6 @@ export class SavedObjectsManagementPlugin const managementService = new SavedObjectsManagement(core.savedObjects.getTypeRegistry()); this.managementService$.next(managementService); - return { - management: managementService, - }; + return {}; } } diff --git a/src/plugins/saved_objects_management/server/routes/find.ts b/src/plugins/saved_objects_management/server/routes/find.ts index 3a3ac5db63288..a74c92ba6161f 100644 --- a/src/plugins/saved_objects_management/server/routes/find.ts +++ b/src/plugins/saved_objects_management/server/routes/find.ts @@ -80,7 +80,7 @@ export const registerFindRoute = ( .map(so => injectMetaAttributes(so, managementService)) .map(obj => { const result = { ...obj, attributes: {} as Record }; - for (const field of includedFields || []) { + for (const field of includedFields) { result.attributes[field] = obj.attributes[field]; } return result; diff --git a/src/plugins/saved_objects_management/server/types.ts b/src/plugins/saved_objects_management/server/types.ts index 3953946b12468..5c4763d357e87 100644 --- a/src/plugins/saved_objects_management/server/types.ts +++ b/src/plugins/saved_objects_management/server/types.ts @@ -18,14 +18,12 @@ */ import { SavedObject } from 'src/core/server'; -import { ISavedObjectsManagement } from './services'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface SavedObjectsManagementPluginSetup {} -export interface SavedObjectsManagementPluginStart { - management: ISavedObjectsManagement; -} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface SavedObjectsManagementPluginStart {} /** * The metadata injected into a {@link SavedObject | saved object} when returning diff --git a/test/api_integration/apis/saved_objects_management/scroll_count.ts b/test/api_integration/apis/saved_objects_management/scroll_count.ts index 8dd3281efb98c..3c29d45244dec 100644 --- a/test/api_integration/apis/saved_objects_management/scroll_count.ts +++ b/test/api_integration/apis/saved_objects_management/scroll_count.ts @@ -82,5 +82,21 @@ export default function({ getService }: FtrProviderContext) { search: 0, }); }); + + it('includes all requested types even when none match the search', async () => { + const res = await supertest + .post(apiUrl) + .send({ + typesToInclude: ['dashboard', 'search', 'visualization'], + searchString: 'nothing-will-match', + }) + .expect(200); + + expect(res.body).to.eql({ + dashboard: 0, + visualization: 0, + search: 0, + }); + }); }); } From 4ac3e3f2442f065575bdb01471020526ada30b63 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 18 Mar 2020 18:40:10 +0100 Subject: [PATCH 20/20] update CODEOWNERS --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index df3a56dd35130..da78384b76ff2 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -131,6 +131,7 @@ /src/legacy/server/saved_objects/ @elastic/kibana-platform /src/legacy/server/status/ @elastic/kibana-platform /src/plugins/status_page/ @elastic/kibana-platform +/src/plugins/saved_objects_management/ @elastic/kibana-platform /src/dev/run_check_published_api_changes.ts @elastic/kibana-platform # Security