From 0816ade5a521dfe7cdb403d3c9122852d1d863f4 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Thu, 14 May 2020 10:06:38 +0200 Subject: [PATCH 1/9] register OSS features with KP SO types only --- x-pack/legacy/plugins/xpack_main/index.js | 5 +--- x-pack/plugins/features/server/plugin.ts | 32 ++++++++++++++++------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/x-pack/legacy/plugins/xpack_main/index.js b/x-pack/legacy/plugins/xpack_main/index.js index 6ce457ffbec05..1f8a4a62ea156 100644 --- a/x-pack/legacy/plugins/xpack_main/index.js +++ b/x-pack/legacy/plugins/xpack_main/index.js @@ -64,10 +64,7 @@ export const xpackMain = kibana => { mirrorPluginStatus(server.plugins.elasticsearch, this, 'yellow', 'red'); - featuresPlugin.registerLegacyAPI({ - xpackInfo: setupXPackMain(server), - savedObjectTypes: server.savedObjects.types, - }); + setupXPackMain(server); // register routes xpackInfoRoute(server); diff --git a/x-pack/plugins/features/server/plugin.ts b/x-pack/plugins/features/server/plugin.ts index 2405f05768a2f..57d273e9e52f0 100644 --- a/x-pack/plugins/features/server/plugin.ts +++ b/x-pack/plugins/features/server/plugin.ts @@ -6,6 +6,8 @@ import { CoreSetup, + CoreStart, + SavedObjectsServiceStart, Logger, PluginInitializerContext, RecursiveReadonly, @@ -52,6 +54,8 @@ export class Plugin { private readonly featureRegistry: FeatureRegistry = new FeatureRegistry(); private legacyAPI?: LegacyAPI; + private isTimelionEnabled: boolean = false; + private readonly getLegacyAPI = () => { if (!this.legacyAPI) { throw new Error('Legacy API is not registered!'); @@ -67,6 +71,8 @@ export class Plugin { core: CoreSetup, { visTypeTimelion }: { visTypeTimelion?: TimelionSetupContract } ): Promise> { + this.isTimelionEnabled = visTypeTimelion !== undefined && visTypeTimelion.uiEnabled; + defineRoutes({ router: core.http.createRouter(), featureRegistry: this.featureRegistry, @@ -80,20 +86,13 @@ export class Plugin { registerLegacyAPI: (legacyAPI: LegacyAPI) => { this.legacyAPI = legacyAPI; - - // Register OSS features. - for (const feature of buildOSSFeatures({ - savedObjectTypes: this.legacyAPI.savedObjectTypes, - includeTimelion: visTypeTimelion !== undefined && visTypeTimelion.uiEnabled, - })) { - this.featureRegistry.register(feature); - } }, }); } - public start(): RecursiveReadonly { - this.logger.debug('Starting plugin'); + public start(core: CoreStart): RecursiveReadonly { + this.registerOssFeatures(core.savedObjects); + return deepFreeze({ getFeatures: this.featureRegistry.getAll.bind(this.featureRegistry), }); @@ -102,4 +101,17 @@ export class Plugin { public stop() { this.logger.debug('Stopping plugin'); } + + private registerOssFeatures(savedObjects: SavedObjectsServiceStart) { + const registry = savedObjects.getTypeRegistry(); + const savedObjectsTypes = registry.getAllTypes().map(t => t.name); + const features = buildOSSFeatures({ + savedObjectTypes: savedObjectsTypes, + includeTimelion: this.isTimelionEnabled, + }); + + for (const feature of features) { + this.featureRegistry.register(feature); + } + } } From c9ebc3a683234f7ce768a4d741ba02cf70f6151b Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Thu, 14 May 2020 10:42:54 +0200 Subject: [PATCH 2/9] use Licensing plugin API in features plugin --- x-pack/plugins/endpoint/server/plugin.test.ts | 1 - x-pack/plugins/features/kibana.json | 1 + x-pack/plugins/features/server/mocks.ts | 1 - x-pack/plugins/features/server/plugin.ts | 26 ----------- .../features/server/routes/index.test.ts | 46 +++++++++---------- .../plugins/features/server/routes/index.ts | 7 ++- 6 files changed, 27 insertions(+), 55 deletions(-) diff --git a/x-pack/plugins/endpoint/server/plugin.test.ts b/x-pack/plugins/endpoint/server/plugin.test.ts index 45e9591a14975..215b26942bcdb 100644 --- a/x-pack/plugins/endpoint/server/plugin.test.ts +++ b/x-pack/plugins/endpoint/server/plugin.test.ts @@ -34,7 +34,6 @@ describe('test endpoint plugin', () => { registerFeature: jest.fn(), getFeatures: jest.fn(), getFeaturesUICapabilities: jest.fn(), - registerLegacyAPI: jest.fn(), }; }); diff --git a/x-pack/plugins/features/kibana.json b/x-pack/plugins/features/kibana.json index 6e51f3b650710..1cab1821b1bf5 100644 --- a/x-pack/plugins/features/kibana.json +++ b/x-pack/plugins/features/kibana.json @@ -2,6 +2,7 @@ "id": "features", "version": "8.0.0", "kibanaVersion": "kibana", + "requiredPlugins": ["licensing"], "optionalPlugins": ["visTypeTimelion"], "configPath": ["xpack", "features"], "server": true, diff --git a/x-pack/plugins/features/server/mocks.ts b/x-pack/plugins/features/server/mocks.ts index ebaa5f1a504ca..d9437169a7453 100644 --- a/x-pack/plugins/features/server/mocks.ts +++ b/x-pack/plugins/features/server/mocks.ts @@ -11,7 +11,6 @@ const createSetup = (): jest.Mocked => { getFeatures: jest.fn(), getFeaturesUICapabilities: jest.fn(), registerFeature: jest.fn(), - registerLegacyAPI: jest.fn(), }; }; diff --git a/x-pack/plugins/features/server/plugin.ts b/x-pack/plugins/features/server/plugin.ts index 57d273e9e52f0..acbbf88da44b0 100644 --- a/x-pack/plugins/features/server/plugin.ts +++ b/x-pack/plugins/features/server/plugin.ts @@ -14,7 +14,6 @@ import { } from '../../../../src/core/server'; import { Capabilities as UICapabilities } from '../../../../src/core/server'; import { deepFreeze } from '../../../../src/core/server'; -import { XPackInfo } from '../../../legacy/plugins/xpack_main/server/lib/xpack_info'; import { PluginSetupContract as TimelionSetupContract } from '../../../../src/plugins/vis_type_timelion/server'; import { FeatureRegistry } from './feature_registry'; import { Feature, FeatureConfig } from '../common/feature'; @@ -29,40 +28,20 @@ export interface PluginSetupContract { registerFeature(feature: FeatureConfig): void; getFeatures(): Feature[]; getFeaturesUICapabilities(): UICapabilities; - registerLegacyAPI: (legacyAPI: LegacyAPI) => void; } export interface PluginStartContract { getFeatures(): Feature[]; } -/** - * Describes a set of APIs that are available in the legacy platform only and required by this plugin - * to function properly. - */ -export interface LegacyAPI { - xpackInfo: Pick; - savedObjectTypes: string[]; -} - /** * Represents Features Plugin instance that will be managed by the Kibana plugin system. */ export class Plugin { private readonly logger: Logger; - private readonly featureRegistry: FeatureRegistry = new FeatureRegistry(); - - private legacyAPI?: LegacyAPI; private isTimelionEnabled: boolean = false; - private readonly getLegacyAPI = () => { - if (!this.legacyAPI) { - throw new Error('Legacy API is not registered!'); - } - return this.legacyAPI; - }; - constructor(private readonly initializerContext: PluginInitializerContext) { this.logger = this.initializerContext.logger.get(); } @@ -76,17 +55,12 @@ export class Plugin { defineRoutes({ router: core.http.createRouter(), featureRegistry: this.featureRegistry, - getLegacyAPI: this.getLegacyAPI, }); return deepFreeze({ registerFeature: this.featureRegistry.register.bind(this.featureRegistry), getFeatures: this.featureRegistry.getAll.bind(this.featureRegistry), getFeaturesUICapabilities: () => uiCapabilitiesForFeatures(this.featureRegistry.getAll()), - - registerLegacyAPI: (legacyAPI: LegacyAPI) => { - this.legacyAPI = legacyAPI; - }, }); } diff --git a/x-pack/plugins/features/server/routes/index.test.ts b/x-pack/plugins/features/server/routes/index.test.ts index c43e2a5195fe7..67b28b27f931f 100644 --- a/x-pack/plugins/features/server/routes/index.test.ts +++ b/x-pack/plugins/features/server/routes/index.test.ts @@ -7,12 +7,20 @@ import { FeatureRegistry } from '../feature_registry'; import { defineRoutes } from './index'; -import { httpServerMock, httpServiceMock } from '../../../../../src/core/server/mocks'; -import { XPackInfoLicense } from '../../../../legacy/plugins/xpack_main/server/lib/xpack_info_license'; +import { httpServerMock, httpServiceMock, coreMock } from '../../../../../src/core/server/mocks'; +import { LicenseType } from '../../../licensing/server/'; +import { licensingMock } from '../../../licensing/server/mocks'; import { RequestHandler } from '../../../../../src/core/server'; import { FeatureConfig } from '../../common'; -let currentLicenseLevel: string = 'gold'; +function createContextMock(licenseType: LicenseType = 'gold') { + return { + core: coreMock.createRequestHandlerContext(), + licensing: { + license: licensingMock.createLicense({ license: { type: licenseType } }), + }, + }; +} describe('GET /api/features', () => { let routeHandler: RequestHandler; @@ -53,16 +61,6 @@ describe('GET /api/features', () => { defineRoutes({ router: routerMock, featureRegistry, - getLegacyAPI: () => ({ - xpackInfo: { - license: { - isOneOf(candidateLicenses: string[]) { - return candidateLicenses.includes(currentLicenseLevel); - }, - } as XPackInfoLicense, - }, - savedObjectTypes: [], - }), }); routeHandler = routerMock.get.mock.calls[0][1]; @@ -70,7 +68,7 @@ describe('GET /api/features', () => { it('returns a list of available features, sorted by their configured order', async () => { const mockResponse = httpServerMock.createResponseFactory(); - routeHandler(undefined as any, { query: {} } as any, mockResponse); + routeHandler(createContextMock(), { query: {} } as any, mockResponse); expect(mockResponse.ok).toHaveBeenCalledTimes(1); const [call] = mockResponse.ok.mock.calls; @@ -98,10 +96,8 @@ describe('GET /api/features', () => { }); it(`by default does not return features that arent allowed by current license`, async () => { - currentLicenseLevel = 'basic'; - const mockResponse = httpServerMock.createResponseFactory(); - routeHandler(undefined as any, { query: {} } as any, mockResponse); + routeHandler(createContextMock('basic'), { query: {} } as any, mockResponse); expect(mockResponse.ok).toHaveBeenCalledTimes(1); const [call] = mockResponse.ok.mock.calls; @@ -126,10 +122,12 @@ describe('GET /api/features', () => { }); it(`ignoreValidLicenses=false does not return features that arent allowed by current license`, async () => { - currentLicenseLevel = 'basic'; - const mockResponse = httpServerMock.createResponseFactory(); - routeHandler(undefined as any, { query: { ignoreValidLicenses: false } } as any, mockResponse); + routeHandler( + createContextMock('basic'), + { query: { ignoreValidLicenses: false } } as any, + mockResponse + ); expect(mockResponse.ok).toHaveBeenCalledTimes(1); const [call] = mockResponse.ok.mock.calls; @@ -154,10 +152,12 @@ describe('GET /api/features', () => { }); it(`ignoreValidLicenses=true returns features that arent allowed by current license`, async () => { - currentLicenseLevel = 'basic'; - const mockResponse = httpServerMock.createResponseFactory(); - routeHandler(undefined as any, { query: { ignoreValidLicenses: true } } as any, mockResponse); + routeHandler( + createContextMock('basic'), + { query: { ignoreValidLicenses: true } } as any, + mockResponse + ); expect(mockResponse.ok).toHaveBeenCalledTimes(1); const [call] = mockResponse.ok.mock.calls; diff --git a/x-pack/plugins/features/server/routes/index.ts b/x-pack/plugins/features/server/routes/index.ts index 428500c3daa88..d07b488693091 100644 --- a/x-pack/plugins/features/server/routes/index.ts +++ b/x-pack/plugins/features/server/routes/index.ts @@ -6,7 +6,6 @@ import { schema } from '@kbn/config-schema'; import { IRouter } from '../../../../../src/core/server'; -import { LegacyAPI } from '../plugin'; import { FeatureRegistry } from '../feature_registry'; /** @@ -15,10 +14,9 @@ import { FeatureRegistry } from '../feature_registry'; export interface RouteDefinitionParams { router: IRouter; featureRegistry: FeatureRegistry; - getLegacyAPI: () => LegacyAPI; } -export function defineRoutes({ router, featureRegistry, getLegacyAPI }: RouteDefinitionParams) { +export function defineRoutes({ router, featureRegistry }: RouteDefinitionParams) { router.get( { path: '/api/features', @@ -37,7 +35,8 @@ export function defineRoutes({ router, featureRegistry, getLegacyAPI }: RouteDef request.query.ignoreValidLicenses || !feature.validLicenses || !feature.validLicenses.length || - getLegacyAPI().xpackInfo.license.isOneOf(feature.validLicenses) + (context.licensing!.license.type && + feature.validLicenses.includes(context.licensing!.license.type)) ) .sort( (f1, f2) => From a8af99a9dfa08b5ec425d19efff9c22f5189ea3b Mon Sep 17 00:00:00 2001 From: restrry Date: Thu, 14 May 2020 12:13:27 +0200 Subject: [PATCH 3/9] add plugin tests --- .../saved_objects_service.mock.ts | 1 + x-pack/plugins/features/server/plugin.test.ts | 87 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 x-pack/plugins/features/server/plugin.test.ts diff --git a/src/core/server/saved_objects/saved_objects_service.mock.ts b/src/core/server/saved_objects/saved_objects_service.mock.ts index 4e1f5981d6a41..6f5ecb1eb464b 100644 --- a/src/core/server/saved_objects/saved_objects_service.mock.ts +++ b/src/core/server/saved_objects/saved_objects_service.mock.ts @@ -107,4 +107,5 @@ export const savedObjectsServiceMock = { createInternalStartContract: createInternalStartContractMock, createStartContract: createStartContractMock, createMigrationContext: migrationMocks.createContext, + createTypeRegistryMock: typeRegistryMock.create, }; diff --git a/x-pack/plugins/features/server/plugin.test.ts b/x-pack/plugins/features/server/plugin.test.ts new file mode 100644 index 0000000000000..daf30851d8c29 --- /dev/null +++ b/x-pack/plugins/features/server/plugin.test.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { coreMock, savedObjectsServiceMock } from 'src/core/server/mocks'; + +import { Plugin } from './plugin'; +const initContext = coreMock.createPluginInitializerContext(); +const coreSetup = coreMock.createSetup(); +const coreStart = coreMock.createStart(); +const typeRegistry = savedObjectsServiceMock.createTypeRegistryMock(); +typeRegistry.getAllTypes.mockReturnValue([ + { + name: 'foo', + hidden: false, + mappings: { properties: {} }, + namespaceType: 'single' as 'single', + }, + { + name: 'bar', + hidden: true, + mappings: { properties: {} }, + namespaceType: 'agnostic' as 'agnostic', + }, +]); +coreStart.savedObjects.getTypeRegistry.mockReturnValue(typeRegistry); + +describe('Features Plugin', () => { + it('returns OSS + registered features', async () => { + const plugin = new Plugin(initContext); + const { registerFeature } = await plugin.setup(coreSetup, {}); + registerFeature({ + id: 'baz', + name: 'baz', + app: [], + privileges: null, + }); + + const { getFeatures } = await plugin.start(coreStart); + + const featureNames = getFeatures() + .map(f => f.id) + .sort(); + expect(featureNames).toEqual([ + 'advancedSettings', + 'baz', + 'dashboard', + 'dev_tools', + 'discover', + 'indexPatterns', + 'savedObjectsManagement', + 'visualize', + ]); + }); + + it('returns OSS + registered features with timielion when available', async () => { + const plugin = new Plugin(initContext); + const { registerFeature } = await plugin.setup(coreSetup, { + visTypeTimelion: { uiEnabled: true }, + }); + registerFeature({ + id: 'baz', + name: 'baz', + app: [], + privileges: null, + }); + + const { getFeatures } = await plugin.start(coreStart); + + const featureNames = getFeatures() + .map(f => f.id) + .sort(); + + expect(featureNames).toEqual([ + 'advancedSettings', + 'baz', + 'dashboard', + 'dev_tools', + 'discover', + 'indexPatterns', + 'savedObjectsManagement', + 'timelion', + 'visualize', + ]); + }); +}); From ee39ebd00bd3e114d4341cc511c75c298d713d13 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Thu, 14 May 2020 15:43:31 +0200 Subject: [PATCH 4/9] filter hidden types out --- .../server/__snapshots__/plugin.test.ts.snap | 1438 +++++++++++++++++ x-pack/plugins/features/server/plugin.test.ts | 30 +- x-pack/plugins/features/server/plugin.ts | 6 +- 3 files changed, 1445 insertions(+), 29 deletions(-) create mode 100644 x-pack/plugins/features/server/__snapshots__/plugin.test.ts.snap diff --git a/x-pack/plugins/features/server/__snapshots__/plugin.test.ts.snap b/x-pack/plugins/features/server/__snapshots__/plugin.test.ts.snap new file mode 100644 index 0000000000000..b96f4bd292e75 --- /dev/null +++ b/x-pack/plugins/features/server/__snapshots__/plugin.test.ts.snap @@ -0,0 +1,1438 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Features Plugin returns OSS + registered features 1`] = ` +Array [ + Feature { + "config": Object { + "app": Array [], + "id": "baz", + "name": "baz", + "privileges": null, + }, + "subFeatures": Array [], + }, + Feature { + "config": Object { + "app": Array [ + "discover", + "kibana", + ], + "catalogue": Array [ + "discover", + ], + "icon": "discoverApp", + "id": "discover", + "name": "Discover", + "navLinkId": "discover", + "order": 100, + "privileges": Object { + "all": Object { + "app": Array [ + "discover", + "kibana", + ], + "catalogue": Array [ + "discover", + ], + "savedObject": Object { + "all": Array [ + "search", + "query", + "index-pattern", + "telemetry", + ], + "read": Array [ + "config", + "url", + ], + }, + "ui": Array [ + "show", + "save", + "saveQuery", + ], + }, + "read": Object { + "app": Array [ + "discover", + "kibana", + ], + "catalogue": Array [ + "discover", + ], + "savedObject": Object { + "all": Array [], + "read": Array [ + "index-pattern", + "search", + "query", + "config", + "url", + ], + }, + "ui": Array [ + "show", + ], + }, + }, + "subFeatures": Array [ + Object { + "name": "Short URLs", + "privilegeGroups": Array [ + Object { + "groupType": "independent", + "privileges": Array [ + Object { + "id": "url_create", + "includeIn": "all", + "name": "Create Short URLs", + "savedObject": Object { + "all": Array [ + "url", + ], + "read": Array [], + }, + "ui": Array [ + "createShortUrl", + ], + }, + ], + }, + ], + }, + ], + }, + "subFeatures": Array [ + SubFeature { + "config": Object { + "name": "Short URLs", + "privilegeGroups": Array [ + Object { + "groupType": "independent", + "privileges": Array [ + Object { + "id": "url_create", + "includeIn": "all", + "name": "Create Short URLs", + "savedObject": Object { + "all": Array [ + "url", + ], + "read": Array [], + }, + "ui": Array [ + "createShortUrl", + ], + }, + ], + }, + ], + }, + }, + ], + }, + Feature { + "config": Object { + "app": Array [ + "visualize", + "lens", + "kibana", + ], + "catalogue": Array [ + "visualize", + ], + "icon": "visualizeApp", + "id": "visualize", + "name": "Visualize", + "navLinkId": "visualize", + "order": 200, + "privileges": Object { + "all": Object { + "app": Array [ + "visualize", + "lens", + "kibana", + ], + "catalogue": Array [ + "visualize", + ], + "savedObject": Object { + "all": Array [ + "visualization", + "query", + "lens", + "telemetry", + ], + "read": Array [ + "index-pattern", + "search", + "config", + "url", + ], + }, + "ui": Array [ + "show", + "delete", + "save", + "saveQuery", + ], + }, + "read": Object { + "app": Array [ + "visualize", + "lens", + "kibana", + ], + "catalogue": Array [ + "visualize", + ], + "savedObject": Object { + "all": Array [], + "read": Array [ + "index-pattern", + "search", + "visualization", + "query", + "lens", + "config", + "url", + ], + }, + "ui": Array [ + "show", + ], + }, + }, + "subFeatures": Array [ + Object { + "name": "Short URLs", + "privilegeGroups": Array [ + Object { + "groupType": "independent", + "privileges": Array [ + Object { + "id": "url_create", + "includeIn": "all", + "name": "Create Short URLs", + "savedObject": Object { + "all": Array [ + "url", + ], + "read": Array [], + }, + "ui": Array [ + "createShortUrl", + ], + }, + ], + }, + ], + }, + ], + }, + "subFeatures": Array [ + SubFeature { + "config": Object { + "name": "Short URLs", + "privilegeGroups": Array [ + Object { + "groupType": "independent", + "privileges": Array [ + Object { + "id": "url_create", + "includeIn": "all", + "name": "Create Short URLs", + "savedObject": Object { + "all": Array [ + "url", + ], + "read": Array [], + }, + "ui": Array [ + "createShortUrl", + ], + }, + ], + }, + ], + }, + }, + ], + }, + Feature { + "config": Object { + "app": Array [ + "dashboards", + "kibana", + ], + "catalogue": Array [ + "dashboard", + ], + "icon": "dashboardApp", + "id": "dashboard", + "name": "Dashboard", + "navLinkId": "dashboards", + "order": 300, + "privileges": Object { + "all": Object { + "app": Array [ + "dashboards", + "kibana", + ], + "catalogue": Array [ + "dashboard", + ], + "savedObject": Object { + "all": Array [ + "dashboard", + "url", + "query", + "telemetry", + ], + "read": Array [ + "index-pattern", + "search", + "visualization", + "timelion-sheet", + "canvas-workpad", + "lens", + "map", + "config", + "url", + ], + }, + "ui": Array [ + "createNew", + "show", + "showWriteControls", + "saveQuery", + ], + }, + "read": Object { + "app": Array [ + "dashboards", + "kibana", + ], + "catalogue": Array [ + "dashboard", + ], + "savedObject": Object { + "all": Array [], + "read": Array [ + "index-pattern", + "search", + "visualization", + "timelion-sheet", + "canvas-workpad", + "map", + "dashboard", + "query", + "config", + "url", + ], + }, + "ui": Array [ + "show", + ], + }, + }, + "subFeatures": Array [ + Object { + "name": "Short URLs", + "privilegeGroups": Array [ + Object { + "groupType": "independent", + "privileges": Array [ + Object { + "id": "url_create", + "includeIn": "all", + "name": "Create Short URLs", + "savedObject": Object { + "all": Array [ + "url", + ], + "read": Array [], + }, + "ui": Array [ + "createShortUrl", + ], + }, + ], + }, + ], + }, + ], + }, + "subFeatures": Array [ + SubFeature { + "config": Object { + "name": "Short URLs", + "privilegeGroups": Array [ + Object { + "groupType": "independent", + "privileges": Array [ + Object { + "id": "url_create", + "includeIn": "all", + "name": "Create Short URLs", + "savedObject": Object { + "all": Array [ + "url", + ], + "read": Array [], + }, + "ui": Array [ + "createShortUrl", + ], + }, + ], + }, + ], + }, + }, + ], + }, + Feature { + "config": Object { + "app": Array [ + "dev_tools", + "kibana", + ], + "catalogue": Array [ + "console", + "searchprofiler", + "grokdebugger", + ], + "icon": "devToolsApp", + "id": "dev_tools", + "name": "Dev Tools", + "navLinkId": "dev_tools", + "order": 1300, + "privileges": Object { + "all": Object { + "api": Array [ + "console", + ], + "app": Array [ + "dev_tools", + "kibana", + ], + "catalogue": Array [ + "console", + "searchprofiler", + "grokdebugger", + ], + "savedObject": Object { + "all": Array [ + "telemetry", + ], + "read": Array [ + "config", + "url", + ], + }, + "ui": Array [ + "show", + "save", + ], + }, + "read": Object { + "api": Array [ + "console", + ], + "app": Array [ + "dev_tools", + "kibana", + ], + "catalogue": Array [ + "console", + "searchprofiler", + "grokdebugger", + ], + "savedObject": Object { + "all": Array [], + "read": Array [ + "config", + "url", + ], + }, + "ui": Array [ + "show", + ], + }, + }, + "privilegesTooltip": "User should also be granted the appropriate Elasticsearch cluster and index privileges", + }, + "subFeatures": Array [], + }, + Feature { + "config": Object { + "app": Array [ + "kibana", + ], + "catalogue": Array [ + "advanced_settings", + ], + "icon": "advancedSettingsApp", + "id": "advancedSettings", + "management": Object { + "kibana": Array [ + "settings", + ], + }, + "name": "Advanced Settings", + "order": 1500, + "privileges": Object { + "all": Object { + "app": Array [ + "kibana", + ], + "catalogue": Array [ + "advanced_settings", + ], + "management": Object { + "kibana": Array [ + "settings", + ], + }, + "savedObject": Object { + "all": Array [ + "config", + "telemetry", + ], + "read": Array [ + "config", + "url", + ], + }, + "ui": Array [ + "save", + ], + }, + "read": Object { + "app": Array [ + "kibana", + ], + "catalogue": Array [ + "advanced_settings", + ], + "management": Object { + "kibana": Array [ + "settings", + ], + }, + "savedObject": Object { + "all": Array [], + "read": Array [ + "config", + "url", + ], + }, + "ui": Array [], + }, + }, + }, + "subFeatures": Array [], + }, + Feature { + "config": Object { + "app": Array [ + "kibana", + ], + "catalogue": Array [ + "index_patterns", + ], + "icon": "indexPatternApp", + "id": "indexPatterns", + "management": Object { + "kibana": Array [ + "index_patterns", + ], + }, + "name": "Index Pattern Management", + "order": 1600, + "privileges": Object { + "all": Object { + "app": Array [ + "kibana", + ], + "catalogue": Array [ + "index_patterns", + ], + "management": Object { + "kibana": Array [ + "index_patterns", + ], + }, + "savedObject": Object { + "all": Array [ + "index-pattern", + "telemetry", + ], + "read": Array [ + "config", + "url", + ], + }, + "ui": Array [ + "save", + ], + }, + "read": Object { + "app": Array [ + "kibana", + ], + "catalogue": Array [ + "index_patterns", + ], + "management": Object { + "kibana": Array [ + "index_patterns", + ], + }, + "savedObject": Object { + "all": Array [], + "read": Array [ + "index-pattern", + "config", + "url", + ], + }, + "ui": Array [], + }, + }, + }, + "subFeatures": Array [], + }, + Feature { + "config": Object { + "app": Array [ + "kibana", + ], + "catalogue": Array [ + "saved_objects", + ], + "icon": "savedObjectsApp", + "id": "savedObjectsManagement", + "management": Object { + "kibana": Array [ + "objects", + ], + }, + "name": "Saved Objects Management", + "order": 1700, + "privileges": Object { + "all": Object { + "api": Array [ + "copySavedObjectsToSpaces", + ], + "app": Array [ + "kibana", + ], + "catalogue": Array [ + "saved_objects", + ], + "management": Object { + "kibana": Array [ + "objects", + ], + }, + "savedObject": Object { + "all": Array [ + "foo", + "telemetry", + ], + "read": Array [ + "config", + "url", + ], + }, + "ui": Array [ + "read", + "edit", + "delete", + "copyIntoSpace", + ], + }, + "read": Object { + "api": Array [ + "copySavedObjectsToSpaces", + ], + "app": Array [ + "kibana", + ], + "catalogue": Array [ + "saved_objects", + ], + "management": Object { + "kibana": Array [ + "objects", + ], + }, + "savedObject": Object { + "all": Array [], + "read": Array [ + "foo", + "config", + "url", + ], + }, + "ui": Array [ + "read", + ], + }, + }, + }, + "subFeatures": Array [], + }, +] +`; + +exports[`Features Plugin returns OSS + registered features with timielion when available 1`] = ` +Array [ + Feature { + "config": Object { + "app": Array [], + "id": "baz", + "name": "baz", + "privileges": null, + }, + "subFeatures": Array [], + }, + Feature { + "config": Object { + "app": Array [ + "discover", + "kibana", + ], + "catalogue": Array [ + "discover", + ], + "icon": "discoverApp", + "id": "discover", + "name": "Discover", + "navLinkId": "discover", + "order": 100, + "privileges": Object { + "all": Object { + "app": Array [ + "discover", + "kibana", + ], + "catalogue": Array [ + "discover", + ], + "savedObject": Object { + "all": Array [ + "search", + "query", + "index-pattern", + "telemetry", + ], + "read": Array [ + "config", + "url", + ], + }, + "ui": Array [ + "show", + "save", + "saveQuery", + ], + }, + "read": Object { + "app": Array [ + "discover", + "kibana", + ], + "catalogue": Array [ + "discover", + ], + "savedObject": Object { + "all": Array [], + "read": Array [ + "index-pattern", + "search", + "query", + "config", + "url", + ], + }, + "ui": Array [ + "show", + ], + }, + }, + "subFeatures": Array [ + Object { + "name": "Short URLs", + "privilegeGroups": Array [ + Object { + "groupType": "independent", + "privileges": Array [ + Object { + "id": "url_create", + "includeIn": "all", + "name": "Create Short URLs", + "savedObject": Object { + "all": Array [ + "url", + ], + "read": Array [], + }, + "ui": Array [ + "createShortUrl", + ], + }, + ], + }, + ], + }, + ], + }, + "subFeatures": Array [ + SubFeature { + "config": Object { + "name": "Short URLs", + "privilegeGroups": Array [ + Object { + "groupType": "independent", + "privileges": Array [ + Object { + "id": "url_create", + "includeIn": "all", + "name": "Create Short URLs", + "savedObject": Object { + "all": Array [ + "url", + ], + "read": Array [], + }, + "ui": Array [ + "createShortUrl", + ], + }, + ], + }, + ], + }, + }, + ], + }, + Feature { + "config": Object { + "app": Array [ + "visualize", + "lens", + "kibana", + ], + "catalogue": Array [ + "visualize", + ], + "icon": "visualizeApp", + "id": "visualize", + "name": "Visualize", + "navLinkId": "visualize", + "order": 200, + "privileges": Object { + "all": Object { + "app": Array [ + "visualize", + "lens", + "kibana", + ], + "catalogue": Array [ + "visualize", + ], + "savedObject": Object { + "all": Array [ + "visualization", + "query", + "lens", + "telemetry", + ], + "read": Array [ + "index-pattern", + "search", + "config", + "url", + ], + }, + "ui": Array [ + "show", + "delete", + "save", + "saveQuery", + ], + }, + "read": Object { + "app": Array [ + "visualize", + "lens", + "kibana", + ], + "catalogue": Array [ + "visualize", + ], + "savedObject": Object { + "all": Array [], + "read": Array [ + "index-pattern", + "search", + "visualization", + "query", + "lens", + "config", + "url", + ], + }, + "ui": Array [ + "show", + ], + }, + }, + "subFeatures": Array [ + Object { + "name": "Short URLs", + "privilegeGroups": Array [ + Object { + "groupType": "independent", + "privileges": Array [ + Object { + "id": "url_create", + "includeIn": "all", + "name": "Create Short URLs", + "savedObject": Object { + "all": Array [ + "url", + ], + "read": Array [], + }, + "ui": Array [ + "createShortUrl", + ], + }, + ], + }, + ], + }, + ], + }, + "subFeatures": Array [ + SubFeature { + "config": Object { + "name": "Short URLs", + "privilegeGroups": Array [ + Object { + "groupType": "independent", + "privileges": Array [ + Object { + "id": "url_create", + "includeIn": "all", + "name": "Create Short URLs", + "savedObject": Object { + "all": Array [ + "url", + ], + "read": Array [], + }, + "ui": Array [ + "createShortUrl", + ], + }, + ], + }, + ], + }, + }, + ], + }, + Feature { + "config": Object { + "app": Array [ + "dashboards", + "kibana", + ], + "catalogue": Array [ + "dashboard", + ], + "icon": "dashboardApp", + "id": "dashboard", + "name": "Dashboard", + "navLinkId": "dashboards", + "order": 300, + "privileges": Object { + "all": Object { + "app": Array [ + "dashboards", + "kibana", + ], + "catalogue": Array [ + "dashboard", + ], + "savedObject": Object { + "all": Array [ + "dashboard", + "url", + "query", + "telemetry", + ], + "read": Array [ + "index-pattern", + "search", + "visualization", + "timelion-sheet", + "canvas-workpad", + "lens", + "map", + "config", + "url", + ], + }, + "ui": Array [ + "createNew", + "show", + "showWriteControls", + "saveQuery", + ], + }, + "read": Object { + "app": Array [ + "dashboards", + "kibana", + ], + "catalogue": Array [ + "dashboard", + ], + "savedObject": Object { + "all": Array [], + "read": Array [ + "index-pattern", + "search", + "visualization", + "timelion-sheet", + "canvas-workpad", + "map", + "dashboard", + "query", + "config", + "url", + ], + }, + "ui": Array [ + "show", + ], + }, + }, + "subFeatures": Array [ + Object { + "name": "Short URLs", + "privilegeGroups": Array [ + Object { + "groupType": "independent", + "privileges": Array [ + Object { + "id": "url_create", + "includeIn": "all", + "name": "Create Short URLs", + "savedObject": Object { + "all": Array [ + "url", + ], + "read": Array [], + }, + "ui": Array [ + "createShortUrl", + ], + }, + ], + }, + ], + }, + ], + }, + "subFeatures": Array [ + SubFeature { + "config": Object { + "name": "Short URLs", + "privilegeGroups": Array [ + Object { + "groupType": "independent", + "privileges": Array [ + Object { + "id": "url_create", + "includeIn": "all", + "name": "Create Short URLs", + "savedObject": Object { + "all": Array [ + "url", + ], + "read": Array [], + }, + "ui": Array [ + "createShortUrl", + ], + }, + ], + }, + ], + }, + }, + ], + }, + Feature { + "config": Object { + "app": Array [ + "dev_tools", + "kibana", + ], + "catalogue": Array [ + "console", + "searchprofiler", + "grokdebugger", + ], + "icon": "devToolsApp", + "id": "dev_tools", + "name": "Dev Tools", + "navLinkId": "dev_tools", + "order": 1300, + "privileges": Object { + "all": Object { + "api": Array [ + "console", + ], + "app": Array [ + "dev_tools", + "kibana", + ], + "catalogue": Array [ + "console", + "searchprofiler", + "grokdebugger", + ], + "savedObject": Object { + "all": Array [ + "telemetry", + ], + "read": Array [ + "config", + "url", + ], + }, + "ui": Array [ + "show", + "save", + ], + }, + "read": Object { + "api": Array [ + "console", + ], + "app": Array [ + "dev_tools", + "kibana", + ], + "catalogue": Array [ + "console", + "searchprofiler", + "grokdebugger", + ], + "savedObject": Object { + "all": Array [], + "read": Array [ + "config", + "url", + ], + }, + "ui": Array [ + "show", + ], + }, + }, + "privilegesTooltip": "User should also be granted the appropriate Elasticsearch cluster and index privileges", + }, + "subFeatures": Array [], + }, + Feature { + "config": Object { + "app": Array [ + "kibana", + ], + "catalogue": Array [ + "advanced_settings", + ], + "icon": "advancedSettingsApp", + "id": "advancedSettings", + "management": Object { + "kibana": Array [ + "settings", + ], + }, + "name": "Advanced Settings", + "order": 1500, + "privileges": Object { + "all": Object { + "app": Array [ + "kibana", + ], + "catalogue": Array [ + "advanced_settings", + ], + "management": Object { + "kibana": Array [ + "settings", + ], + }, + "savedObject": Object { + "all": Array [ + "config", + "telemetry", + ], + "read": Array [ + "config", + "url", + ], + }, + "ui": Array [ + "save", + ], + }, + "read": Object { + "app": Array [ + "kibana", + ], + "catalogue": Array [ + "advanced_settings", + ], + "management": Object { + "kibana": Array [ + "settings", + ], + }, + "savedObject": Object { + "all": Array [], + "read": Array [ + "config", + "url", + ], + }, + "ui": Array [], + }, + }, + }, + "subFeatures": Array [], + }, + Feature { + "config": Object { + "app": Array [ + "kibana", + ], + "catalogue": Array [ + "index_patterns", + ], + "icon": "indexPatternApp", + "id": "indexPatterns", + "management": Object { + "kibana": Array [ + "index_patterns", + ], + }, + "name": "Index Pattern Management", + "order": 1600, + "privileges": Object { + "all": Object { + "app": Array [ + "kibana", + ], + "catalogue": Array [ + "index_patterns", + ], + "management": Object { + "kibana": Array [ + "index_patterns", + ], + }, + "savedObject": Object { + "all": Array [ + "index-pattern", + "telemetry", + ], + "read": Array [ + "config", + "url", + ], + }, + "ui": Array [ + "save", + ], + }, + "read": Object { + "app": Array [ + "kibana", + ], + "catalogue": Array [ + "index_patterns", + ], + "management": Object { + "kibana": Array [ + "index_patterns", + ], + }, + "savedObject": Object { + "all": Array [], + "read": Array [ + "index-pattern", + "config", + "url", + ], + }, + "ui": Array [], + }, + }, + }, + "subFeatures": Array [], + }, + Feature { + "config": Object { + "app": Array [ + "kibana", + ], + "catalogue": Array [ + "saved_objects", + ], + "icon": "savedObjectsApp", + "id": "savedObjectsManagement", + "management": Object { + "kibana": Array [ + "objects", + ], + }, + "name": "Saved Objects Management", + "order": 1700, + "privileges": Object { + "all": Object { + "api": Array [ + "copySavedObjectsToSpaces", + ], + "app": Array [ + "kibana", + ], + "catalogue": Array [ + "saved_objects", + ], + "management": Object { + "kibana": Array [ + "objects", + ], + }, + "savedObject": Object { + "all": Array [ + "foo", + "telemetry", + ], + "read": Array [ + "config", + "url", + ], + }, + "ui": Array [ + "read", + "edit", + "delete", + "copyIntoSpace", + ], + }, + "read": Object { + "api": Array [ + "copySavedObjectsToSpaces", + ], + "app": Array [ + "kibana", + ], + "catalogue": Array [ + "saved_objects", + ], + "management": Object { + "kibana": Array [ + "objects", + ], + }, + "savedObject": Object { + "all": Array [], + "read": Array [ + "foo", + "config", + "url", + ], + }, + "ui": Array [ + "read", + ], + }, + }, + }, + "subFeatures": Array [], + }, + Feature { + "config": Object { + "app": Array [ + "timelion", + "kibana", + ], + "catalogue": Array [ + "timelion", + ], + "icon": "timelionApp", + "id": "timelion", + "name": "Timelion", + "navLinkId": "timelion", + "order": 350, + "privileges": Object { + "all": Object { + "app": Array [ + "timelion", + "kibana", + ], + "catalogue": Array [ + "timelion", + ], + "savedObject": Object { + "all": Array [ + "timelion-sheet", + "telemetry", + ], + "read": Array [ + "index-pattern", + "config", + "url", + ], + }, + "ui": Array [ + "save", + ], + }, + "read": Object { + "app": Array [ + "timelion", + "kibana", + ], + "catalogue": Array [ + "timelion", + ], + "savedObject": Object { + "all": Array [], + "read": Array [ + "index-pattern", + "timelion-sheet", + "config", + "url", + ], + }, + "ui": Array [], + }, + }, + }, + "subFeatures": Array [], + }, +] +`; diff --git a/x-pack/plugins/features/server/plugin.test.ts b/x-pack/plugins/features/server/plugin.test.ts index daf30851d8c29..16cd3ba43829f 100644 --- a/x-pack/plugins/features/server/plugin.test.ts +++ b/x-pack/plugins/features/server/plugin.test.ts @@ -39,19 +39,7 @@ describe('Features Plugin', () => { const { getFeatures } = await plugin.start(coreStart); - const featureNames = getFeatures() - .map(f => f.id) - .sort(); - expect(featureNames).toEqual([ - 'advancedSettings', - 'baz', - 'dashboard', - 'dev_tools', - 'discover', - 'indexPatterns', - 'savedObjectsManagement', - 'visualize', - ]); + expect(getFeatures()).toMatchSnapshot(); }); it('returns OSS + registered features with timielion when available', async () => { @@ -68,20 +56,6 @@ describe('Features Plugin', () => { const { getFeatures } = await plugin.start(coreStart); - const featureNames = getFeatures() - .map(f => f.id) - .sort(); - - expect(featureNames).toEqual([ - 'advancedSettings', - 'baz', - 'dashboard', - 'dev_tools', - 'discover', - 'indexPatterns', - 'savedObjectsManagement', - 'timelion', - 'visualize', - ]); + expect(getFeatures()).toMatchSnapshot(); }); }); diff --git a/x-pack/plugins/features/server/plugin.ts b/x-pack/plugins/features/server/plugin.ts index acbbf88da44b0..a9db9f8db170e 100644 --- a/x-pack/plugins/features/server/plugin.ts +++ b/x-pack/plugins/features/server/plugin.ts @@ -78,7 +78,11 @@ export class Plugin { private registerOssFeatures(savedObjects: SavedObjectsServiceStart) { const registry = savedObjects.getTypeRegistry(); - const savedObjectsTypes = registry.getAllTypes().map(t => t.name); + const savedObjectsTypes = registry + .getAllTypes() + .filter(t => !t.hidden) + .map(t => t.name); + const features = buildOSSFeatures({ savedObjectTypes: savedObjectsTypes, includeTimelion: this.isTimelionEnabled, From 863d4884fbf5044b80cf765e2dc1660866450b43 Mon Sep 17 00:00:00 2001 From: restrry Date: Thu, 14 May 2020 16:04:52 +0200 Subject: [PATCH 5/9] cleanup tests --- .../server/__snapshots__/plugin.test.ts.snap | 1438 ----------------- x-pack/plugins/features/server/plugin.test.ts | 40 +- 2 files changed, 38 insertions(+), 1440 deletions(-) delete mode 100644 x-pack/plugins/features/server/__snapshots__/plugin.test.ts.snap diff --git a/x-pack/plugins/features/server/__snapshots__/plugin.test.ts.snap b/x-pack/plugins/features/server/__snapshots__/plugin.test.ts.snap deleted file mode 100644 index b96f4bd292e75..0000000000000 --- a/x-pack/plugins/features/server/__snapshots__/plugin.test.ts.snap +++ /dev/null @@ -1,1438 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Features Plugin returns OSS + registered features 1`] = ` -Array [ - Feature { - "config": Object { - "app": Array [], - "id": "baz", - "name": "baz", - "privileges": null, - }, - "subFeatures": Array [], - }, - Feature { - "config": Object { - "app": Array [ - "discover", - "kibana", - ], - "catalogue": Array [ - "discover", - ], - "icon": "discoverApp", - "id": "discover", - "name": "Discover", - "navLinkId": "discover", - "order": 100, - "privileges": Object { - "all": Object { - "app": Array [ - "discover", - "kibana", - ], - "catalogue": Array [ - "discover", - ], - "savedObject": Object { - "all": Array [ - "search", - "query", - "index-pattern", - "telemetry", - ], - "read": Array [ - "config", - "url", - ], - }, - "ui": Array [ - "show", - "save", - "saveQuery", - ], - }, - "read": Object { - "app": Array [ - "discover", - "kibana", - ], - "catalogue": Array [ - "discover", - ], - "savedObject": Object { - "all": Array [], - "read": Array [ - "index-pattern", - "search", - "query", - "config", - "url", - ], - }, - "ui": Array [ - "show", - ], - }, - }, - "subFeatures": Array [ - Object { - "name": "Short URLs", - "privilegeGroups": Array [ - Object { - "groupType": "independent", - "privileges": Array [ - Object { - "id": "url_create", - "includeIn": "all", - "name": "Create Short URLs", - "savedObject": Object { - "all": Array [ - "url", - ], - "read": Array [], - }, - "ui": Array [ - "createShortUrl", - ], - }, - ], - }, - ], - }, - ], - }, - "subFeatures": Array [ - SubFeature { - "config": Object { - "name": "Short URLs", - "privilegeGroups": Array [ - Object { - "groupType": "independent", - "privileges": Array [ - Object { - "id": "url_create", - "includeIn": "all", - "name": "Create Short URLs", - "savedObject": Object { - "all": Array [ - "url", - ], - "read": Array [], - }, - "ui": Array [ - "createShortUrl", - ], - }, - ], - }, - ], - }, - }, - ], - }, - Feature { - "config": Object { - "app": Array [ - "visualize", - "lens", - "kibana", - ], - "catalogue": Array [ - "visualize", - ], - "icon": "visualizeApp", - "id": "visualize", - "name": "Visualize", - "navLinkId": "visualize", - "order": 200, - "privileges": Object { - "all": Object { - "app": Array [ - "visualize", - "lens", - "kibana", - ], - "catalogue": Array [ - "visualize", - ], - "savedObject": Object { - "all": Array [ - "visualization", - "query", - "lens", - "telemetry", - ], - "read": Array [ - "index-pattern", - "search", - "config", - "url", - ], - }, - "ui": Array [ - "show", - "delete", - "save", - "saveQuery", - ], - }, - "read": Object { - "app": Array [ - "visualize", - "lens", - "kibana", - ], - "catalogue": Array [ - "visualize", - ], - "savedObject": Object { - "all": Array [], - "read": Array [ - "index-pattern", - "search", - "visualization", - "query", - "lens", - "config", - "url", - ], - }, - "ui": Array [ - "show", - ], - }, - }, - "subFeatures": Array [ - Object { - "name": "Short URLs", - "privilegeGroups": Array [ - Object { - "groupType": "independent", - "privileges": Array [ - Object { - "id": "url_create", - "includeIn": "all", - "name": "Create Short URLs", - "savedObject": Object { - "all": Array [ - "url", - ], - "read": Array [], - }, - "ui": Array [ - "createShortUrl", - ], - }, - ], - }, - ], - }, - ], - }, - "subFeatures": Array [ - SubFeature { - "config": Object { - "name": "Short URLs", - "privilegeGroups": Array [ - Object { - "groupType": "independent", - "privileges": Array [ - Object { - "id": "url_create", - "includeIn": "all", - "name": "Create Short URLs", - "savedObject": Object { - "all": Array [ - "url", - ], - "read": Array [], - }, - "ui": Array [ - "createShortUrl", - ], - }, - ], - }, - ], - }, - }, - ], - }, - Feature { - "config": Object { - "app": Array [ - "dashboards", - "kibana", - ], - "catalogue": Array [ - "dashboard", - ], - "icon": "dashboardApp", - "id": "dashboard", - "name": "Dashboard", - "navLinkId": "dashboards", - "order": 300, - "privileges": Object { - "all": Object { - "app": Array [ - "dashboards", - "kibana", - ], - "catalogue": Array [ - "dashboard", - ], - "savedObject": Object { - "all": Array [ - "dashboard", - "url", - "query", - "telemetry", - ], - "read": Array [ - "index-pattern", - "search", - "visualization", - "timelion-sheet", - "canvas-workpad", - "lens", - "map", - "config", - "url", - ], - }, - "ui": Array [ - "createNew", - "show", - "showWriteControls", - "saveQuery", - ], - }, - "read": Object { - "app": Array [ - "dashboards", - "kibana", - ], - "catalogue": Array [ - "dashboard", - ], - "savedObject": Object { - "all": Array [], - "read": Array [ - "index-pattern", - "search", - "visualization", - "timelion-sheet", - "canvas-workpad", - "map", - "dashboard", - "query", - "config", - "url", - ], - }, - "ui": Array [ - "show", - ], - }, - }, - "subFeatures": Array [ - Object { - "name": "Short URLs", - "privilegeGroups": Array [ - Object { - "groupType": "independent", - "privileges": Array [ - Object { - "id": "url_create", - "includeIn": "all", - "name": "Create Short URLs", - "savedObject": Object { - "all": Array [ - "url", - ], - "read": Array [], - }, - "ui": Array [ - "createShortUrl", - ], - }, - ], - }, - ], - }, - ], - }, - "subFeatures": Array [ - SubFeature { - "config": Object { - "name": "Short URLs", - "privilegeGroups": Array [ - Object { - "groupType": "independent", - "privileges": Array [ - Object { - "id": "url_create", - "includeIn": "all", - "name": "Create Short URLs", - "savedObject": Object { - "all": Array [ - "url", - ], - "read": Array [], - }, - "ui": Array [ - "createShortUrl", - ], - }, - ], - }, - ], - }, - }, - ], - }, - Feature { - "config": Object { - "app": Array [ - "dev_tools", - "kibana", - ], - "catalogue": Array [ - "console", - "searchprofiler", - "grokdebugger", - ], - "icon": "devToolsApp", - "id": "dev_tools", - "name": "Dev Tools", - "navLinkId": "dev_tools", - "order": 1300, - "privileges": Object { - "all": Object { - "api": Array [ - "console", - ], - "app": Array [ - "dev_tools", - "kibana", - ], - "catalogue": Array [ - "console", - "searchprofiler", - "grokdebugger", - ], - "savedObject": Object { - "all": Array [ - "telemetry", - ], - "read": Array [ - "config", - "url", - ], - }, - "ui": Array [ - "show", - "save", - ], - }, - "read": Object { - "api": Array [ - "console", - ], - "app": Array [ - "dev_tools", - "kibana", - ], - "catalogue": Array [ - "console", - "searchprofiler", - "grokdebugger", - ], - "savedObject": Object { - "all": Array [], - "read": Array [ - "config", - "url", - ], - }, - "ui": Array [ - "show", - ], - }, - }, - "privilegesTooltip": "User should also be granted the appropriate Elasticsearch cluster and index privileges", - }, - "subFeatures": Array [], - }, - Feature { - "config": Object { - "app": Array [ - "kibana", - ], - "catalogue": Array [ - "advanced_settings", - ], - "icon": "advancedSettingsApp", - "id": "advancedSettings", - "management": Object { - "kibana": Array [ - "settings", - ], - }, - "name": "Advanced Settings", - "order": 1500, - "privileges": Object { - "all": Object { - "app": Array [ - "kibana", - ], - "catalogue": Array [ - "advanced_settings", - ], - "management": Object { - "kibana": Array [ - "settings", - ], - }, - "savedObject": Object { - "all": Array [ - "config", - "telemetry", - ], - "read": Array [ - "config", - "url", - ], - }, - "ui": Array [ - "save", - ], - }, - "read": Object { - "app": Array [ - "kibana", - ], - "catalogue": Array [ - "advanced_settings", - ], - "management": Object { - "kibana": Array [ - "settings", - ], - }, - "savedObject": Object { - "all": Array [], - "read": Array [ - "config", - "url", - ], - }, - "ui": Array [], - }, - }, - }, - "subFeatures": Array [], - }, - Feature { - "config": Object { - "app": Array [ - "kibana", - ], - "catalogue": Array [ - "index_patterns", - ], - "icon": "indexPatternApp", - "id": "indexPatterns", - "management": Object { - "kibana": Array [ - "index_patterns", - ], - }, - "name": "Index Pattern Management", - "order": 1600, - "privileges": Object { - "all": Object { - "app": Array [ - "kibana", - ], - "catalogue": Array [ - "index_patterns", - ], - "management": Object { - "kibana": Array [ - "index_patterns", - ], - }, - "savedObject": Object { - "all": Array [ - "index-pattern", - "telemetry", - ], - "read": Array [ - "config", - "url", - ], - }, - "ui": Array [ - "save", - ], - }, - "read": Object { - "app": Array [ - "kibana", - ], - "catalogue": Array [ - "index_patterns", - ], - "management": Object { - "kibana": Array [ - "index_patterns", - ], - }, - "savedObject": Object { - "all": Array [], - "read": Array [ - "index-pattern", - "config", - "url", - ], - }, - "ui": Array [], - }, - }, - }, - "subFeatures": Array [], - }, - Feature { - "config": Object { - "app": Array [ - "kibana", - ], - "catalogue": Array [ - "saved_objects", - ], - "icon": "savedObjectsApp", - "id": "savedObjectsManagement", - "management": Object { - "kibana": Array [ - "objects", - ], - }, - "name": "Saved Objects Management", - "order": 1700, - "privileges": Object { - "all": Object { - "api": Array [ - "copySavedObjectsToSpaces", - ], - "app": Array [ - "kibana", - ], - "catalogue": Array [ - "saved_objects", - ], - "management": Object { - "kibana": Array [ - "objects", - ], - }, - "savedObject": Object { - "all": Array [ - "foo", - "telemetry", - ], - "read": Array [ - "config", - "url", - ], - }, - "ui": Array [ - "read", - "edit", - "delete", - "copyIntoSpace", - ], - }, - "read": Object { - "api": Array [ - "copySavedObjectsToSpaces", - ], - "app": Array [ - "kibana", - ], - "catalogue": Array [ - "saved_objects", - ], - "management": Object { - "kibana": Array [ - "objects", - ], - }, - "savedObject": Object { - "all": Array [], - "read": Array [ - "foo", - "config", - "url", - ], - }, - "ui": Array [ - "read", - ], - }, - }, - }, - "subFeatures": Array [], - }, -] -`; - -exports[`Features Plugin returns OSS + registered features with timielion when available 1`] = ` -Array [ - Feature { - "config": Object { - "app": Array [], - "id": "baz", - "name": "baz", - "privileges": null, - }, - "subFeatures": Array [], - }, - Feature { - "config": Object { - "app": Array [ - "discover", - "kibana", - ], - "catalogue": Array [ - "discover", - ], - "icon": "discoverApp", - "id": "discover", - "name": "Discover", - "navLinkId": "discover", - "order": 100, - "privileges": Object { - "all": Object { - "app": Array [ - "discover", - "kibana", - ], - "catalogue": Array [ - "discover", - ], - "savedObject": Object { - "all": Array [ - "search", - "query", - "index-pattern", - "telemetry", - ], - "read": Array [ - "config", - "url", - ], - }, - "ui": Array [ - "show", - "save", - "saveQuery", - ], - }, - "read": Object { - "app": Array [ - "discover", - "kibana", - ], - "catalogue": Array [ - "discover", - ], - "savedObject": Object { - "all": Array [], - "read": Array [ - "index-pattern", - "search", - "query", - "config", - "url", - ], - }, - "ui": Array [ - "show", - ], - }, - }, - "subFeatures": Array [ - Object { - "name": "Short URLs", - "privilegeGroups": Array [ - Object { - "groupType": "independent", - "privileges": Array [ - Object { - "id": "url_create", - "includeIn": "all", - "name": "Create Short URLs", - "savedObject": Object { - "all": Array [ - "url", - ], - "read": Array [], - }, - "ui": Array [ - "createShortUrl", - ], - }, - ], - }, - ], - }, - ], - }, - "subFeatures": Array [ - SubFeature { - "config": Object { - "name": "Short URLs", - "privilegeGroups": Array [ - Object { - "groupType": "independent", - "privileges": Array [ - Object { - "id": "url_create", - "includeIn": "all", - "name": "Create Short URLs", - "savedObject": Object { - "all": Array [ - "url", - ], - "read": Array [], - }, - "ui": Array [ - "createShortUrl", - ], - }, - ], - }, - ], - }, - }, - ], - }, - Feature { - "config": Object { - "app": Array [ - "visualize", - "lens", - "kibana", - ], - "catalogue": Array [ - "visualize", - ], - "icon": "visualizeApp", - "id": "visualize", - "name": "Visualize", - "navLinkId": "visualize", - "order": 200, - "privileges": Object { - "all": Object { - "app": Array [ - "visualize", - "lens", - "kibana", - ], - "catalogue": Array [ - "visualize", - ], - "savedObject": Object { - "all": Array [ - "visualization", - "query", - "lens", - "telemetry", - ], - "read": Array [ - "index-pattern", - "search", - "config", - "url", - ], - }, - "ui": Array [ - "show", - "delete", - "save", - "saveQuery", - ], - }, - "read": Object { - "app": Array [ - "visualize", - "lens", - "kibana", - ], - "catalogue": Array [ - "visualize", - ], - "savedObject": Object { - "all": Array [], - "read": Array [ - "index-pattern", - "search", - "visualization", - "query", - "lens", - "config", - "url", - ], - }, - "ui": Array [ - "show", - ], - }, - }, - "subFeatures": Array [ - Object { - "name": "Short URLs", - "privilegeGroups": Array [ - Object { - "groupType": "independent", - "privileges": Array [ - Object { - "id": "url_create", - "includeIn": "all", - "name": "Create Short URLs", - "savedObject": Object { - "all": Array [ - "url", - ], - "read": Array [], - }, - "ui": Array [ - "createShortUrl", - ], - }, - ], - }, - ], - }, - ], - }, - "subFeatures": Array [ - SubFeature { - "config": Object { - "name": "Short URLs", - "privilegeGroups": Array [ - Object { - "groupType": "independent", - "privileges": Array [ - Object { - "id": "url_create", - "includeIn": "all", - "name": "Create Short URLs", - "savedObject": Object { - "all": Array [ - "url", - ], - "read": Array [], - }, - "ui": Array [ - "createShortUrl", - ], - }, - ], - }, - ], - }, - }, - ], - }, - Feature { - "config": Object { - "app": Array [ - "dashboards", - "kibana", - ], - "catalogue": Array [ - "dashboard", - ], - "icon": "dashboardApp", - "id": "dashboard", - "name": "Dashboard", - "navLinkId": "dashboards", - "order": 300, - "privileges": Object { - "all": Object { - "app": Array [ - "dashboards", - "kibana", - ], - "catalogue": Array [ - "dashboard", - ], - "savedObject": Object { - "all": Array [ - "dashboard", - "url", - "query", - "telemetry", - ], - "read": Array [ - "index-pattern", - "search", - "visualization", - "timelion-sheet", - "canvas-workpad", - "lens", - "map", - "config", - "url", - ], - }, - "ui": Array [ - "createNew", - "show", - "showWriteControls", - "saveQuery", - ], - }, - "read": Object { - "app": Array [ - "dashboards", - "kibana", - ], - "catalogue": Array [ - "dashboard", - ], - "savedObject": Object { - "all": Array [], - "read": Array [ - "index-pattern", - "search", - "visualization", - "timelion-sheet", - "canvas-workpad", - "map", - "dashboard", - "query", - "config", - "url", - ], - }, - "ui": Array [ - "show", - ], - }, - }, - "subFeatures": Array [ - Object { - "name": "Short URLs", - "privilegeGroups": Array [ - Object { - "groupType": "independent", - "privileges": Array [ - Object { - "id": "url_create", - "includeIn": "all", - "name": "Create Short URLs", - "savedObject": Object { - "all": Array [ - "url", - ], - "read": Array [], - }, - "ui": Array [ - "createShortUrl", - ], - }, - ], - }, - ], - }, - ], - }, - "subFeatures": Array [ - SubFeature { - "config": Object { - "name": "Short URLs", - "privilegeGroups": Array [ - Object { - "groupType": "independent", - "privileges": Array [ - Object { - "id": "url_create", - "includeIn": "all", - "name": "Create Short URLs", - "savedObject": Object { - "all": Array [ - "url", - ], - "read": Array [], - }, - "ui": Array [ - "createShortUrl", - ], - }, - ], - }, - ], - }, - }, - ], - }, - Feature { - "config": Object { - "app": Array [ - "dev_tools", - "kibana", - ], - "catalogue": Array [ - "console", - "searchprofiler", - "grokdebugger", - ], - "icon": "devToolsApp", - "id": "dev_tools", - "name": "Dev Tools", - "navLinkId": "dev_tools", - "order": 1300, - "privileges": Object { - "all": Object { - "api": Array [ - "console", - ], - "app": Array [ - "dev_tools", - "kibana", - ], - "catalogue": Array [ - "console", - "searchprofiler", - "grokdebugger", - ], - "savedObject": Object { - "all": Array [ - "telemetry", - ], - "read": Array [ - "config", - "url", - ], - }, - "ui": Array [ - "show", - "save", - ], - }, - "read": Object { - "api": Array [ - "console", - ], - "app": Array [ - "dev_tools", - "kibana", - ], - "catalogue": Array [ - "console", - "searchprofiler", - "grokdebugger", - ], - "savedObject": Object { - "all": Array [], - "read": Array [ - "config", - "url", - ], - }, - "ui": Array [ - "show", - ], - }, - }, - "privilegesTooltip": "User should also be granted the appropriate Elasticsearch cluster and index privileges", - }, - "subFeatures": Array [], - }, - Feature { - "config": Object { - "app": Array [ - "kibana", - ], - "catalogue": Array [ - "advanced_settings", - ], - "icon": "advancedSettingsApp", - "id": "advancedSettings", - "management": Object { - "kibana": Array [ - "settings", - ], - }, - "name": "Advanced Settings", - "order": 1500, - "privileges": Object { - "all": Object { - "app": Array [ - "kibana", - ], - "catalogue": Array [ - "advanced_settings", - ], - "management": Object { - "kibana": Array [ - "settings", - ], - }, - "savedObject": Object { - "all": Array [ - "config", - "telemetry", - ], - "read": Array [ - "config", - "url", - ], - }, - "ui": Array [ - "save", - ], - }, - "read": Object { - "app": Array [ - "kibana", - ], - "catalogue": Array [ - "advanced_settings", - ], - "management": Object { - "kibana": Array [ - "settings", - ], - }, - "savedObject": Object { - "all": Array [], - "read": Array [ - "config", - "url", - ], - }, - "ui": Array [], - }, - }, - }, - "subFeatures": Array [], - }, - Feature { - "config": Object { - "app": Array [ - "kibana", - ], - "catalogue": Array [ - "index_patterns", - ], - "icon": "indexPatternApp", - "id": "indexPatterns", - "management": Object { - "kibana": Array [ - "index_patterns", - ], - }, - "name": "Index Pattern Management", - "order": 1600, - "privileges": Object { - "all": Object { - "app": Array [ - "kibana", - ], - "catalogue": Array [ - "index_patterns", - ], - "management": Object { - "kibana": Array [ - "index_patterns", - ], - }, - "savedObject": Object { - "all": Array [ - "index-pattern", - "telemetry", - ], - "read": Array [ - "config", - "url", - ], - }, - "ui": Array [ - "save", - ], - }, - "read": Object { - "app": Array [ - "kibana", - ], - "catalogue": Array [ - "index_patterns", - ], - "management": Object { - "kibana": Array [ - "index_patterns", - ], - }, - "savedObject": Object { - "all": Array [], - "read": Array [ - "index-pattern", - "config", - "url", - ], - }, - "ui": Array [], - }, - }, - }, - "subFeatures": Array [], - }, - Feature { - "config": Object { - "app": Array [ - "kibana", - ], - "catalogue": Array [ - "saved_objects", - ], - "icon": "savedObjectsApp", - "id": "savedObjectsManagement", - "management": Object { - "kibana": Array [ - "objects", - ], - }, - "name": "Saved Objects Management", - "order": 1700, - "privileges": Object { - "all": Object { - "api": Array [ - "copySavedObjectsToSpaces", - ], - "app": Array [ - "kibana", - ], - "catalogue": Array [ - "saved_objects", - ], - "management": Object { - "kibana": Array [ - "objects", - ], - }, - "savedObject": Object { - "all": Array [ - "foo", - "telemetry", - ], - "read": Array [ - "config", - "url", - ], - }, - "ui": Array [ - "read", - "edit", - "delete", - "copyIntoSpace", - ], - }, - "read": Object { - "api": Array [ - "copySavedObjectsToSpaces", - ], - "app": Array [ - "kibana", - ], - "catalogue": Array [ - "saved_objects", - ], - "management": Object { - "kibana": Array [ - "objects", - ], - }, - "savedObject": Object { - "all": Array [], - "read": Array [ - "foo", - "config", - "url", - ], - }, - "ui": Array [ - "read", - ], - }, - }, - }, - "subFeatures": Array [], - }, - Feature { - "config": Object { - "app": Array [ - "timelion", - "kibana", - ], - "catalogue": Array [ - "timelion", - ], - "icon": "timelionApp", - "id": "timelion", - "name": "Timelion", - "navLinkId": "timelion", - "order": 350, - "privileges": Object { - "all": Object { - "app": Array [ - "timelion", - "kibana", - ], - "catalogue": Array [ - "timelion", - ], - "savedObject": Object { - "all": Array [ - "timelion-sheet", - "telemetry", - ], - "read": Array [ - "index-pattern", - "config", - "url", - ], - }, - "ui": Array [ - "save", - ], - }, - "read": Object { - "app": Array [ - "timelion", - "kibana", - ], - "catalogue": Array [ - "timelion", - ], - "savedObject": Object { - "all": Array [], - "read": Array [ - "index-pattern", - "timelion-sheet", - "config", - "url", - ], - }, - "ui": Array [], - }, - }, - }, - "subFeatures": Array [], - }, -] -`; diff --git a/x-pack/plugins/features/server/plugin.test.ts b/x-pack/plugins/features/server/plugin.test.ts index 16cd3ba43829f..74617cb0279a9 100644 --- a/x-pack/plugins/features/server/plugin.test.ts +++ b/x-pack/plugins/features/server/plugin.test.ts @@ -39,7 +39,18 @@ describe('Features Plugin', () => { const { getFeatures } = await plugin.start(coreStart); - expect(getFeatures()).toMatchSnapshot(); + expect(getFeatures().map(f => f.id)).toMatchInlineSnapshot(` + Array [ + "baz", + "discover", + "visualize", + "dashboard", + "dev_tools", + "advancedSettings", + "indexPatterns", + "savedObjectsManagement", + ] + `); }); it('returns OSS + registered features with timielion when available', async () => { @@ -56,6 +67,31 @@ describe('Features Plugin', () => { const { getFeatures } = await plugin.start(coreStart); - expect(getFeatures()).toMatchSnapshot(); + expect(getFeatures().map(f => f.id)).toMatchInlineSnapshot(` + Array [ + "baz", + "discover", + "visualize", + "dashboard", + "dev_tools", + "advancedSettings", + "indexPatterns", + "savedObjectsManagement", + "timelion", + ] + `); + }); + + it('registers not hidden saved objects types', async () => { + const plugin = new Plugin(initContext); + await plugin.setup(coreSetup, {}); + const { getFeatures } = await plugin.start(coreStart); + + const soTypes = + getFeatures().find(f => f.id === 'savedObjectsManagement')?.privileges?.all.savedObject.all || + []; + + expect(soTypes.includes('foo')).toBe(true); + expect(soTypes.includes('bar')).toBe(false); }); }); From b7a7035950b84ba59d8fdf0151795ba989e4c7f6 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Thu, 14 May 2020 16:07:19 +0200 Subject: [PATCH 6/9] rename --- x-pack/plugins/features/server/plugin.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/features/server/plugin.ts b/x-pack/plugins/features/server/plugin.ts index a9db9f8db170e..9b422269d8ce0 100644 --- a/x-pack/plugins/features/server/plugin.ts +++ b/x-pack/plugins/features/server/plugin.ts @@ -78,13 +78,13 @@ export class Plugin { private registerOssFeatures(savedObjects: SavedObjectsServiceStart) { const registry = savedObjects.getTypeRegistry(); - const savedObjectsTypes = registry + const savedObjectTypes = registry .getAllTypes() .filter(t => !t.hidden) .map(t => t.name); const features = buildOSSFeatures({ - savedObjectTypes: savedObjectsTypes, + savedObjectTypes, includeTimelion: this.isTimelionEnabled, }); From 64af9c9530be209a69b65a2666cb8d5a1269bd36 Mon Sep 17 00:00:00 2001 From: restrry Date: Thu, 14 May 2020 16:15:30 +0200 Subject: [PATCH 7/9] add degug logging --- x-pack/plugins/features/server/plugin.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/features/server/plugin.ts b/x-pack/plugins/features/server/plugin.ts index 9b422269d8ce0..d30599ebe14d8 100644 --- a/x-pack/plugins/features/server/plugin.ts +++ b/x-pack/plugins/features/server/plugin.ts @@ -72,9 +72,7 @@ export class Plugin { }); } - public stop() { - this.logger.debug('Stopping plugin'); - } + public stop() {} private registerOssFeatures(savedObjects: SavedObjectsServiceStart) { const registry = savedObjects.getTypeRegistry(); @@ -83,6 +81,11 @@ export class Plugin { .filter(t => !t.hidden) .map(t => t.name); + this.logger.debug( + `Registering OSS features with SO types: ${savedObjectTypes.join(', ')}. "includeTimelion": ${ + this.isTimelionEnabled + }.` + ); const features = buildOSSFeatures({ savedObjectTypes, includeTimelion: this.isTimelionEnabled, From ec102c2b5d0027340aebcd2758330ee21b910569 Mon Sep 17 00:00:00 2001 From: restrry Date: Thu, 14 May 2020 16:22:07 +0200 Subject: [PATCH 8/9] add warning for setup contract --- x-pack/plugins/features/server/plugin.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/x-pack/plugins/features/server/plugin.ts b/x-pack/plugins/features/server/plugin.ts index d30599ebe14d8..e3480eda9fe7d 100644 --- a/x-pack/plugins/features/server/plugin.ts +++ b/x-pack/plugins/features/server/plugin.ts @@ -26,6 +26,11 @@ import { defineRoutes } from './routes'; */ export interface PluginSetupContract { registerFeature(feature: FeatureConfig): void; + /* + * Calling this function during setup will crash Kibana. + * Use start contract instead. + * @deprecated + * */ getFeatures(): Feature[]; getFeaturesUICapabilities(): UICapabilities; } From 6634d5a7c8aca09252e6e43bc3c0ac1991275b31 Mon Sep 17 00:00:00 2001 From: restrry Date: Fri, 15 May 2020 17:25:34 +0200 Subject: [PATCH 9/9] fix typo --- x-pack/plugins/features/server/plugin.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/features/server/plugin.test.ts b/x-pack/plugins/features/server/plugin.test.ts index 74617cb0279a9..3d7cf19e58b0e 100644 --- a/x-pack/plugins/features/server/plugin.test.ts +++ b/x-pack/plugins/features/server/plugin.test.ts @@ -53,7 +53,7 @@ describe('Features Plugin', () => { `); }); - it('returns OSS + registered features with timielion when available', async () => { + it('returns OSS + registered features with timelion when available', async () => { const plugin = new Plugin(initContext); const { registerFeature } = await plugin.setup(coreSetup, { visTypeTimelion: { uiEnabled: true },