diff --git a/docs/development/core/server/kibana-plugin-server.logger.get.md b/docs/development/core/server/kibana-plugin-server.logger.get.md new file mode 100644 index 00000000000000..b4a2d8a124260d --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.logger.get.md @@ -0,0 +1,33 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [Logger](./kibana-plugin-server.logger.md) > [get](./kibana-plugin-server.logger.get.md) + +## Logger.get() method + +Returns a new [Logger](./kibana-plugin-server.logger.md) instance extending the current logger context. + +Signature: + +```typescript +get(...childContextPaths: string[]): Logger; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| childContextPaths | string[] | | + +Returns: + +`Logger` + +## Example + + +```typescript +const logger = loggerFactory.get('plugin', 'service'); // 'plugin.service' context +const subLogger = logger.get('feature'); // 'plugin.service.feature' context + +``` + diff --git a/docs/development/core/server/kibana-plugin-server.md b/docs/development/core/server/kibana-plugin-server.md index ea5ca6502b0765..cdd709375aa510 100644 --- a/docs/development/core/server/kibana-plugin-server.md +++ b/docs/development/core/server/kibana-plugin-server.md @@ -90,7 +90,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [PluginManifest](./kibana-plugin-server.pluginmanifest.md) | Describes the set of required and optional properties plugin can define in its mandatory JSON manifest file. | | [PluginsServiceSetup](./kibana-plugin-server.pluginsservicesetup.md) | | | [PluginsServiceStart](./kibana-plugin-server.pluginsservicestart.md) | | -| [RequestHandlerContext](./kibana-plugin-server.requesthandlercontext.md) | Plugin specific context passed to a route handler.Provides the following clients: - [savedObjects.client](./kibana-plugin-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [elasticsearch.dataClient](./kibana-plugin-server.scopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.adminClient](./kibana-plugin-server.scopedclusterclient.md) - Elasticsearch admin client which uses the credentials of the incoming request | +| [RequestHandlerContext](./kibana-plugin-server.requesthandlercontext.md) | Plugin specific context passed to a route handler.Provides the following clients: - [savedObjects.client](./kibana-plugin-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [elasticsearch.dataClient](./kibana-plugin-server.scopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.adminClient](./kibana-plugin-server.scopedclusterclient.md) - Elasticsearch admin client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request | | [RouteConfig](./kibana-plugin-server.routeconfig.md) | Route specific configuration. | | [RouteConfigOptions](./kibana-plugin-server.routeconfigoptions.md) | Additional route options. | | [RouteConfigOptionsBody](./kibana-plugin-server.routeconfigoptionsbody.md) | Additional body options for a route | diff --git a/docs/development/core/server/kibana-plugin-server.requesthandlercontext.md b/docs/development/core/server/kibana-plugin-server.requesthandlercontext.md index c9fc80596efa9d..d9b781e1e550ec 100644 --- a/docs/development/core/server/kibana-plugin-server.requesthandlercontext.md +++ b/docs/development/core/server/kibana-plugin-server.requesthandlercontext.md @@ -6,7 +6,7 @@ Plugin specific context passed to a route handler. -Provides the following clients: - [savedObjects.client](./kibana-plugin-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [elasticsearch.dataClient](./kibana-plugin-server.scopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.adminClient](./kibana-plugin-server.scopedclusterclient.md) - Elasticsearch admin client which uses the credentials of the incoming request +Provides the following clients: - [savedObjects.client](./kibana-plugin-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [elasticsearch.dataClient](./kibana-plugin-server.scopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.adminClient](./kibana-plugin-server.scopedclusterclient.md) - Elasticsearch admin client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request Signature: diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md index 5bb22579d123e0..1c78de966c46f7 100644 --- a/src/core/MIGRATION.md +++ b/src/core/MIGRATION.md @@ -46,6 +46,8 @@ - [How to](#how-to) - [Configure plugin](#configure-plugin) - [Handle plugin configuration deprecations](#handle-plugin-config-deprecations) + - [Use scoped services](#use-scoped-services) + - [Declare a custom scoped service](#declare-a-custom-scoped-service) - [Mock new platform services in tests](#mock-new-platform-services-in-tests) - [Writing mocks for your plugin](#writing-mocks-for-your-plugin) - [Using mocks in your tests](#using-mocks-in-your-tests) @@ -1190,22 +1192,23 @@ In server code, `core` can be accessed from either `server.newPlatform` or `kbnS | `server.config()` | [`initializerContext.config.create()`](/docs/development/core/server/kibana-plugin-server.plugininitializercontext.config.md) | Must also define schema. See _[how to configure plugin](#configure-plugin)_ | | `server.route` | [`core.http.createRouter`](/docs/development/core/server/kibana-plugin-server.httpservicesetup.createrouter.md) | [Examples](./MIGRATION_EXAMPLES.md#route-registration) | | `request.getBasePath()` | [`core.http.basePath.get`](/docs/development/core/server/kibana-plugin-server.httpservicesetup.basepath.md) | | -| `server.plugins.elasticsearch.getCluster('data')` | [`core.elasticsearch.dataClient$`](/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.dataclient_.md) | Handlers will also include a pre-configured client | -| `server.plugins.elasticsearch.getCluster('admin')` | [`core.elasticsearch.adminClient$`](/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.adminclient_.md) | Handlers will also include a pre-configured client | -| `xpackMainPlugin.info.feature(pluginID).registerLicenseCheckResultsGenerator` | [`x-pack licensing plugin`](/x-pack/plugins/licensing/README.md) | | +| `server.plugins.elasticsearch.getCluster('data')` | [`context.elasticsearch.dataClient`](/docs/development/core/server/kibana-plugin-server.iscopedclusterclient.md) | | +| `server.plugins.elasticsearch.getCluster('admin')` | [`context.elasticsearch.adminClient`](/docs/development/core/server/kibana-plugin-server.iscopedclusterclient.md) | | | `server.savedObjects.setScopedSavedObjectsClientFactory` | [`core.savedObjects.setClientFactory`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.setclientfactory.md) | | | `server.savedObjects.addScopedSavedObjectsClientWrapperFactory` | [`core.savedObjects.addClientWrapper`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.addclientwrapper.md) | | | `server.savedObjects.getSavedObjectsRepository` | [`core.savedObjects.createInternalRepository`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.createinternalrepository.md) [`core.savedObjects.createScopedRepository`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.createscopedrepository.md) | | | `server.savedObjects.getScopedSavedObjectsClient` | [`core.savedObjects.getScopedClient`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.getscopedclient.md) | | | `request.getSavedObjectsClient` | [`context.core.savedObjects.client`](/docs/development/core/server/kibana-plugin-server.requesthandlercontext.core.md) | | +| `request.getUiSettingsService` | [`context.uiSettings.client`](/docs/development/core/server/kibana-plugin-server.iuisettingsclient.md) | | | `kibana.Plugin.deprecations` | [Handle plugin configuration deprecations](#handle-plugin-config-deprecations) and [`PluginConfigDescriptor.deprecations`](docs/development/core/server/kibana-plugin-server.pluginconfigdescriptor.md) | Deprecations from New Platform are not applied to legacy configuration | _See also: [Server's CoreSetup API Docs](/docs/development/core/server/kibana-plugin-server.coresetup.md)_ ##### Plugin services -| Legacy Platform | New Platform | Notes | -| ------------------------------------------- | ------------------------------------------------------------------------------ | ----- | -| `server.plugins.xpack_main.registerFeature` | [`plugins.features.registerFeature`](x-pack/plugins/features/server/plugin.ts) | | +| Legacy Platform | New Platform | Notes | +| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ----- | +| `server.plugins.xpack_main.registerFeature` | [`plugins.features.registerFeature`](x-pack/plugins/features/server/plugin.ts) | | +| `server.plugins.xpack_main.feature(pluginID).registerLicenseCheckResultsGenerator` | [`x-pack licensing plugin`](/x-pack/plugins/licensing/README.md) | | #### UI Exports @@ -1399,7 +1402,7 @@ export const config: PluginConfigDescriptor = { deprecations: ({ rename, unused }) => [ rename('oldProperty', 'newProperty'), unused('someUnusedProperty'), - ] + ] }; ``` @@ -1413,7 +1416,7 @@ export const config: PluginConfigDescriptor = { deprecations: ({ renameFromRoot, unusedFromRoot }) => [ renameFromRoot('oldplugin.property', 'myplugin.property'), unusedFromRoot('oldplugin.deprecated'), - ] + ] }; ``` @@ -1421,6 +1424,68 @@ Note that deprecations registered in new platform's plugins are not applied to t During migration, if you still need the deprecations to be effective in the legacy plugin, you need to declare them in both plugin definitions. +### Use scoped services +Whenever Kibana needs to get access to data saved in elasticsearch, it should perform a check whether an end-user has access to the data. +In the legacy platform, Kibana requires to bind elasticsearch related API with an incoming request to access elasticsearch service on behalf of a user. +```js + async function handler(req, res) { + const dataCluster = server.plugins.elasticsearch.getCluster('data'); + const data = await dataCluster.callWithRequest(req, 'ping'); + } +``` + +The new platform introduced [a handler interface](/rfcs/text/0003_handler_interface.md) on the server-side to perform that association internally. Core services, that require impersonation with an incoming request, are +exposed via `context` argument of [the request handler interface.](/docs/development/core/server/kibana-plugin-server.requesthandler.md) +The above example looks in the new platform as +```js + async function handler(context, req, res) { + const data = await context.core.elasticsearch.adminClient.callAsInternalUser('ping') + } +``` + +The [request handler context](/docs/development/core/server/kibana-plugin-server.requesthandlercontext.md) exposed the next scoped **core** services: +| Legacy Platform | New Platform | +| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------| +| `request.getSavedObjectsClient` | [`context.savedObjects.client`](/docs/development/core/server/kibana-plugin-server.savedobjectsclient.md) | +| `server.plugins.elasticsearch.getCluster('admin')` | [`context.elasticsearch.adminClient`](/docs/development/core/server/kibana-plugin-server.iscopedclusterclient.md) | +| `server.plugins.elasticsearch.getCluster('data')` | [`context.elasticsearch.dataClient`](/docs/development/core/server/kibana-plugin-server.iscopedclusterclient.md) | +| `request.getUiSettingsService` | [`context.uiSettings.client`](/docs/development/core/server/kibana-plugin-server.iuisettingsclient.md) | + +#### Declare a custom scoped service +Plugins can extend the handler context with custom API that will be available to the plugin itself and all dependent plugins. +For example, the plugin creates a custom elasticsearch client and want to use it via the request handler context: + +```ts +import { CoreSetup, IScopedClusterClient } from 'kibana/server'; + +export interface MyPluginContext { + client: IScopedClusterClient; +} + +// extend RequestHandlerContext when a dependent plugin imports MyPluginContext from the file +declare module 'src/core/server' { + interface RequestHandlerContext { + myPlugin?: MyPluginContext; + } +} + +class Plugin { + setup(core: CoreSetup) { + const client = core.elasticsearch.createClient('myClient'); + core.http.registerRouteHandlerContext('myPlugin', (context, req, res) => { + return { client: client.asScoped(req) }; + }); + + router.get( + { path: '/api/my-plugin/', validate }, + async (context, req, res) => { + const data = await context.myPlugin.client.callAsCurrentUser('endpoint'); + ... + } + ); + } +``` + ### Mock new platform services in tests #### Writing mocks for your plugin diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 2aaa8306e871f0..ba930d46e08655 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -234,6 +234,8 @@ export { LegacyServiceSetupDeps, LegacyServiceStartDeps } from './legacy'; * data client which uses the credentials of the incoming request * - {@link ScopedClusterClient | elasticsearch.adminClient} - Elasticsearch * admin client which uses the credentials of the incoming request + * - {@link IUiSettingsClient | uiSettings.client} - uiSettings client + * which uses the credentials of the incoming request * * @public */ diff --git a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/field_chooser.html b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/field_chooser.html index adf4b1b4326e88..1587c2af797521 100644 --- a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/field_chooser.html +++ b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/field_chooser.html @@ -13,64 +13,6 @@ types="fieldTypes" > -
-
- - -
-
- - -
-
- - -
-
- -
- -
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/LoadingStatePrompt.tsx b/x-pack/legacy/plugins/apm/public/components/shared/LoadingStatePrompt.tsx index f68e2978f680fc..e1cf07c03dee96 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/LoadingStatePrompt.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/LoadingStatePrompt.tsx @@ -5,20 +5,14 @@ */ import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiEmptyPrompt } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; export function LoadingStatePrompt() { return ( - - {i18n.translate('xpack.apm.loading.prompt', { - defaultMessage: 'Loading...' - })} - - } - titleSize="s" - /> + + + + + ); } diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.tsx index 7637d28dd42297..2ba012afa689dc 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.tsx +++ b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.tsx @@ -86,7 +86,7 @@ class EditRolePageUI extends Component { }; } - public UNSAFE_componentWillMount() { + public componentDidMount() { if (this.props.action === 'clone' && isReservedRole(this.props.role)) { this.backToRoleList(); } diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/simple_privilege_section.test.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/simple_privilege_section.test.tsx index 1f29f774fd6cc0..f97fa93294ff5b 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/simple_privilege_section.test.tsx +++ b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/simple_privilege_section.test.tsx @@ -46,6 +46,7 @@ const buildProps = (customProps: any = {}) => { id: 'feature1', name: 'Feature 1', app: ['app'], + icon: 'spacesApp', privileges: { all: { app: ['app'], diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx b/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx index 49f5233db44e2b..7134049c4cf8f3 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; import { AdvancedSettingsSubtitle } from './advanced_settings_subtitle'; import { EuiCallOut } from '@elastic/eui'; +import { act } from '@testing-library/react'; describe('AdvancedSettingsSubtitle', () => { it('renders as expected', async () => { @@ -21,10 +22,10 @@ describe('AdvancedSettingsSubtitle', () => { ); // Wait for active space to resolve before requesting the component to update - await Promise.resolve(); - await Promise.resolve(); - - wrapper.update(); + await act(async () => { + await nextTick(); + wrapper.update(); + }); expect(wrapper.find(EuiCallOut)).toHaveLength(1); }); diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/advanced_settings_title.test.tsx b/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/advanced_settings_title.test.tsx index 7f2b6eee62c45e..bf792ca2cdacf7 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/advanced_settings_title.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/advanced_settings_title.test.tsx @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; import { AdvancedSettingsTitle } from './advanced_settings_title'; import { SpaceAvatar } from '../../../../components'; +import { act } from '@testing-library/react'; describe('AdvancedSettingsTitle', () => { it('renders without crashing', async () => { @@ -20,9 +21,11 @@ describe('AdvancedSettingsTitle', () => { Promise.resolve(space)} /> ); - await Promise.resolve(); - await Promise.resolve(); - wrapper.update(); + await act(async () => { + await nextTick(); + wrapper.update(); + }); + expect(wrapper.find(SpaceAvatar)).toHaveLength(1); }); }); diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_flyout.test.tsx b/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_flyout.test.tsx index 590c0edc0073ba..b3fd345b1d2b4f 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_flyout.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_flyout.test.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; import Boom from 'boom'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; import { mockManagementPlugin } from '../../../../../../../../../src/legacy/core_plugins/management/public/np_ready/mocks'; import { CopySavedObjectsToSpaceFlyout } from './copy_to_space_flyout'; import { CopyToSpaceForm } from './copy_to_space_form'; @@ -93,9 +93,10 @@ const setup = async (opts: SetupOpts = {}) => { if (!opts.returnBeforeSpacesLoad) { // Wait for spaces manager to complete and flyout to rerender - await Promise.resolve(); - await Promise.resolve(); - wrapper.update(); + await act(async () => { + await nextTick(); + wrapper.update(); + }); } return { wrapper, onClose, mockSpacesManager, mockToastNotifications, savedObjectToCopy }; @@ -113,8 +114,10 @@ describe('CopyToSpaceFlyout', () => { expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(0); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(1); - await Promise.resolve(); - wrapper.update(); + await act(async () => { + await nextTick(); + wrapper.update(); + }); expect(wrapper.find(CopyToSpaceForm)).toHaveLength(1); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); @@ -160,14 +163,13 @@ describe('CopyToSpaceFlyout', () => { }); const startButton = findTestSubject(wrapper, 'cts-initiate-button'); - act(() => { + + await act(async () => { startButton.simulate('click'); + await nextTick(); + wrapper.update(); }); - await Promise.resolve(); - - wrapper.update(); - expect(mockSpacesManager.copySavedObjects).toHaveBeenCalled(); expect(mockToastNotifications.addError).toHaveBeenCalled(); }); @@ -214,13 +216,13 @@ describe('CopyToSpaceFlyout', () => { }); const startButton = findTestSubject(wrapper, 'cts-initiate-button'); - act(() => { + + await act(async () => { startButton.simulate('click'); + await nextTick(); + wrapper.update(); }); - await Promise.resolve(); - wrapper.update(); - expect(mockSpacesManager.copySavedObjects).toHaveBeenCalled(); expect(mockToastNotifications.addError).not.toHaveBeenCalled(); @@ -231,13 +233,13 @@ describe('CopyToSpaceFlyout', () => { overwriteButton.simulate('click'); const finishButton = findTestSubject(wrapper, 'cts-finish-button'); - act(() => { + + await act(async () => { finishButton.simulate('click'); + await nextTick(); + wrapper.update(); }); - await Promise.resolve(); - wrapper.update(); - expect(mockSpacesManager.resolveCopySavedObjectsErrors).toHaveBeenCalled(); expect(mockToastNotifications.addError).toHaveBeenCalled(); }); @@ -275,14 +277,13 @@ describe('CopyToSpaceFlyout', () => { }); const startButton = findTestSubject(wrapper, 'cts-initiate-button'); - act(() => { + + await act(async () => { startButton.simulate('click'); + await nextTick(); + wrapper.update(); }); - await Promise.resolve(); - - wrapper.update(); - expect(mockSpacesManager.copySavedObjects).toHaveBeenCalledWith( [{ type: savedObjectToCopy.type, id: savedObjectToCopy.id }], ['space-1', 'space-2'], @@ -350,14 +351,13 @@ describe('CopyToSpaceFlyout', () => { }); const startButton = findTestSubject(wrapper, 'cts-initiate-button'); - act(() => { + + await act(async () => { startButton.simulate('click'); + await nextTick(); + wrapper.update(); }); - await Promise.resolve(); - - wrapper.update(); - expect(wrapper.find(CopyToSpaceForm)).toHaveLength(0); expect(wrapper.find(ProcessingCopyToSpace)).toHaveLength(1); @@ -368,13 +368,13 @@ describe('CopyToSpaceFlyout', () => { overwriteButton.simulate('click'); const finishButton = findTestSubject(wrapper, 'cts-finish-button'); - act(() => { + + await act(async () => { finishButton.simulate('click'); + await nextTick(); + wrapper.update(); }); - await Promise.resolve(); - wrapper.update(); - expect(mockSpacesManager.resolveCopySavedObjectsErrors).toHaveBeenCalledWith( [{ type: savedObjectToCopy.type, id: savedObjectToCopy.id }], { @@ -420,14 +420,13 @@ describe('CopyToSpaceFlyout', () => { }); const startButton = findTestSubject(wrapper, 'cts-initiate-button'); - act(() => { + + await act(async () => { startButton.simulate('click'); + await nextTick(); + wrapper.update(); }); - await Promise.resolve(); - - wrapper.update(); - expect(wrapper.find(CopyToSpaceForm)).toHaveLength(0); expect(wrapper.find(ProcessingCopyToSpace)).toHaveLength(1); diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap index 5e91c1bd0e36cd..5879ff621d64a5 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap +++ b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap @@ -190,12 +190,14 @@ exports[`EnabledFeatures renders as expected 1`] = ` Array [ Object { "app": Array [], + "icon": "spacesApp", "id": "feature-1", "name": "Feature 1", "privileges": Object {}, }, Object { "app": Array [], + "icon": "spacesApp", "id": "feature-2", "name": "Feature 2", "privileges": Object {}, diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/enabled_features.test.tsx b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/enabled_features.test.tsx index 8f82e6d413350f..f8bd4b889394ad 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/enabled_features.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/enabled_features.test.tsx @@ -16,12 +16,14 @@ const features: Feature[] = [ { id: 'feature-1', name: 'Feature 1', + icon: 'spacesApp', app: [], privileges: {}, }, { id: 'feature-2', name: 'Feature 2', + icon: 'spacesApp', app: [], privileges: {}, }, diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.test.tsx b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.test.tsx index 1597e4684222c2..c69a885ae05878 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.test.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ jest.mock('ui/kfetch', () => ({ - kfetch: () => Promise.resolve([{ id: 'feature-1', name: 'feature 1' }]), + kfetch: () => Promise.resolve([{ id: 'feature-1', name: 'feature 1', icon: 'spacesApp' }]), })); import '../../../__mocks__/xpack_info'; import { EuiButton, EuiLink, EuiSwitch } from '@elastic/eui'; diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx index 98ce64715f3253..b37458aace2a28 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx @@ -45,7 +45,7 @@ export class NavControlPopover extends Component { }; } - public componentWillMount() { + public componentDidMount() { this.activeSpace$ = this.props.spacesManager.onActiveSpaceChange$.subscribe({ next: activeSpace => { this.setState({ diff --git a/x-pack/legacy/plugins/xpack_main/server/lib/__tests__/xpack_info.js b/x-pack/legacy/plugins/xpack_main/server/lib/__tests__/xpack_info.js index 2edfbbc27fc456..2e0d608e522d7f 100644 --- a/x-pack/legacy/plugins/xpack_main/server/lib/__tests__/xpack_info.js +++ b/x-pack/legacy/plugins/xpack_main/server/lib/__tests__/xpack_info.js @@ -312,6 +312,54 @@ describe('XPackInfo', () => { }); }); + it('onLicenseInfoChange() allows to subscribe to license update', async () => { + const license$ = new BehaviorSubject(createLicense()); + + const xPackInfo = new XPackInfo(mockServer, { + licensing: { + license$, + refresh: () => null, + }, + }); + + const watcherFeature = xPackInfo.feature('watcher'); + watcherFeature.registerLicenseCheckResultsGenerator(info => ({ + type: info.license.getType(), + })); + + const statuses = []; + xPackInfo.onLicenseInfoChange(() => statuses.push(watcherFeature.getLicenseCheckResults())); + + license$.next(createLicense({ type: 'basic' })); + expect(statuses).to.eql([{ type: 'basic' }]); + + license$.next(createLicense({ type: 'trial' })); + expect(statuses).to.eql([{ type: 'basic' }, { type: 'trial' }]); + }); + + it('refreshNow() leads to onLicenseInfoChange()', async () => { + const license$ = new BehaviorSubject(createLicense()); + + const xPackInfo = new XPackInfo(mockServer, { + licensing: { + license$, + refresh: () => license$.next({ type: 'basic' }), + }, + }); + + const watcherFeature = xPackInfo.feature('watcher'); + + watcherFeature.registerLicenseCheckResultsGenerator(info => ({ + type: info.license.getType(), + })); + + const statuses = []; + xPackInfo.onLicenseInfoChange(() => statuses.push(watcherFeature.getLicenseCheckResults())); + + await xPackInfo.refreshNow(); + expect(statuses).to.eql([{ type: 'basic' }]); + }); + it('getSignature() returns correct signature.', async () => { const license$ = new BehaviorSubject(createLicense()); const xPackInfo = new XPackInfo(mockServer, { diff --git a/x-pack/legacy/plugins/xpack_main/server/lib/xpack_info.ts b/x-pack/legacy/plugins/xpack_main/server/lib/xpack_info.ts index fbb8929154c36a..9d5a8e64645ec6 100644 --- a/x-pack/legacy/plugins/xpack_main/server/lib/xpack_info.ts +++ b/x-pack/legacy/plugins/xpack_main/server/lib/xpack_info.ts @@ -101,6 +101,8 @@ export class XPackInfo { error: license.error, }; } + + this._licenseInfoChangedListeners.forEach(fn => fn()); }); this._license = new XPackInfoLicense(() => this._cache.license); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 4681beaf829ead..282af1d477489a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -1551,7 +1551,6 @@ "kbn.discover.fieldChooser.filter.hideMissingFieldsLabel": "未入力のフィールドを非表示", "kbn.discover.fieldChooser.filter.indexAndFieldsSectionAriaLabel": "インデックスとフィールド", "kbn.discover.fieldChooser.filter.popularTitle": "人気", - "kbn.discover.fieldChooser.filter.resetFiltersButtonLabel": "フィルターをリセット", "kbn.discover.fieldChooser.filter.searchableLabel": "検索可能", "kbn.discover.fieldChooser.filter.selectedFieldsTitle": "スクリプトフィールド", "kbn.discover.fieldChooser.filter.typeLabel": "タイプ", @@ -3550,7 +3549,6 @@ "xpack.apm.fetcher.error.status": "エラー", "xpack.apm.fetcher.error.title": "リソースの取得中にエラーが発生しました", "xpack.apm.fetcher.error.url": "URL", - "xpack.apm.loading.prompt": "読み込み中…", "xpack.apm.localFilters.titles.agentName": "エージェント名", "xpack.apm.localFilters.titles.containerId": "コンテナー ID", "xpack.apm.localFilters.titles.host": "ホスト", @@ -12709,4 +12707,4 @@ "xpack.licensing.welcomeBanner.licenseIsExpiredDescription.updateYourLicenseLinkText": "ライセンスを更新", "xpack.licensing.welcomeBanner.licenseIsExpiredTitle": "ご使用の {licenseType} ライセンスは期限切れです" } -} \ No newline at end of file +} diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index c2b220732a865a..47c7050ee5c742 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -1552,7 +1552,6 @@ "kbn.discover.fieldChooser.filter.hideMissingFieldsLabel": "隐藏缺失字段", "kbn.discover.fieldChooser.filter.indexAndFieldsSectionAriaLabel": "索引和字段", "kbn.discover.fieldChooser.filter.popularTitle": "常用", - "kbn.discover.fieldChooser.filter.resetFiltersButtonLabel": "重置筛选", "kbn.discover.fieldChooser.filter.searchableLabel": "可搜索", "kbn.discover.fieldChooser.filter.selectedFieldsTitle": "选定字段", "kbn.discover.fieldChooser.filter.typeLabel": "类型", @@ -3551,7 +3550,6 @@ "xpack.apm.fetcher.error.status": "错误", "xpack.apm.fetcher.error.title": "提取资源时出错", "xpack.apm.fetcher.error.url": "URL", - "xpack.apm.loading.prompt": "正在加载……", "xpack.apm.localFilters.titles.agentName": "代理名称", "xpack.apm.localFilters.titles.containerId": "容器 ID", "xpack.apm.localFilters.titles.host": "主机",