diff --git a/.eslintignore b/.eslintignore index 86a01b68ecab1..c3921bd22e1ab 100644 --- a/.eslintignore +++ b/.eslintignore @@ -11,7 +11,6 @@ bower_components /src/plugins/data/common/es_query/kuery/ast/_generated_/** /src/legacy/core_plugins/vis_type_timelion/public/_generated_/** src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data -/src/legacy/ui/public/angular-bootstrap /src/legacy/ui/public/flot-charts /test/fixtures/scenarios /src/legacy/core_plugins/console/public/webpackShims diff --git a/.eslintrc.js b/.eslintrc.js index 199f3743fd621..abfe5e0a6cc27 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -302,6 +302,8 @@ module.exports = { 'test/plugin_functional/plugins/**/public/np_ready/**/*', 'test/plugin_functional/plugins/**/server/np_ready/**/*', 'src/legacy/core_plugins/**/public/np_ready/**/*', + 'src/legacy/core_plugins/vis_type_*/public/**/*', + '!src/legacy/core_plugins/vis_type_*/public/legacy*', 'src/legacy/core_plugins/**/server/np_ready/**/*', 'x-pack/legacy/plugins/**/public/np_ready/**/*', 'x-pack/legacy/plugins/**/server/np_ready/**/*', diff --git a/docs/apm/troubleshooting.asciidoc b/docs/apm/troubleshooting.asciidoc index 22279b69b70fe..c4611f3b41e55 100644 --- a/docs/apm/troubleshooting.asciidoc +++ b/docs/apm/troubleshooting.asciidoc @@ -6,6 +6,7 @@ your proposed changes at https://github.com/elastic/kibana. Also check out the https://discuss.elastic.co/c/apm[APM discussion forum]. +[[no-apm-data-found]] ==== No APM data found This section can help with any of the following: @@ -69,3 +70,41 @@ or because something is happening to the request that the Agent doesn't understa To resolve this, you'll need to head over to the relevant {apm-agents-ref}[Agent documentation]. Specifically, view the Agent's supported technologies page. You can also use the Agent's public API to manually set a name for the transaction. + +==== Fields are not searchable + +In Elasticsearch, index patterns are used to define settings and mappings that determine how fields should be analyzed. +The recommended index template file for APM Server is installed when Kibana starts. +This template defines which fields are available in Kibana for features like the Kuery bar, +or for linking to other plugins like Logs, Uptime, and Discover. + +As an example, some agents store cookie values in `http.request.cookies`. +Since `http.request` has disabled dynamic indexing, and `http.request.cookies` is not declared in a custom mapping, +the values in `http.request.cookies` are not indexed and thus not searchable. + +*Ensure an index pattern exists* +As a first step, you should ensure the correct index pattern exists. +In Kibana, navigate to *Management > Kibana > Index Patterns*. +In the pattern list, you should see an apm index pattern; The default is `apm-*`. +If you don't, the index pattern doesn't exist. See <> for information on how to fix this problem. + +Selecting the `apm-*` index pattern shows a listing of every field defined in the pattern. + +*Ensure a field is searchable* +There are two things you can do to if you'd like to ensure a field is searchable: + +1. Index your additional data as {apm-overview-ref}/metadata.html[labels] instead. +These are dynamic by default, which means they will be indexed and become searchable and aggregatable. + +2. Use the {apm-server-ref}/configuration-template.html[`append_fields`] feature. As an example, +adding the following to `apm-server.yml` will enable dynamic indexing for `http.request.cookies`: + +[source,yml] +---- +setup.template.enabled: true +setup.template.overwrite: true +setup.template.append_fields: + - name: http.request.cookies + type: object + dynamic: true +---- diff --git a/docs/settings/alert-action-settings.asciidoc b/docs/settings/alert-action-settings.asciidoc new file mode 100644 index 0000000000000..e3b9e61667bdf --- /dev/null +++ b/docs/settings/alert-action-settings.asciidoc @@ -0,0 +1,47 @@ +[role="xpack"] +[[alert-action-settings-kb]] +=== Alerting and action settings in Kibana +++++ +Alerting and action settings +++++ + +Alerts and actions are enabled by default in {kib}, but require you configure the following in order to use them: + +. <>. +. <>. +. <>. + +You can configure the following settings in the `kibana.yml` file. + + +[float] +[[general-alert-action-settings]] +==== General settings + +`xpack.encrypted_saved_objects.encryptionKey`:: + +A string of 32 or more characters used to encrypt sensitive properties on alerts and actions before they're stored in {es}. Third party credentials — such as the username and password used to connect to an SMTP service — are an example of encrypted properties. ++ +If not set, {kib} will generate a random key on startup, but all alert and action functions will be blocked. Generated keys are not allowed for alerts and actions because when a new key is generated on restart, existing encrypted data becomes inaccessible. For the same reason, alerts and actions in high-availability deployments of {kib} will behave unexpectedly if the key isn't the same on all instances of {kib}. ++ +Although the key can be specified in clear text in `kibana.yml`, it's recommended to store this key securely in the <>. + +[float] +[[alert-settings]] +==== Action settings + +`xpack.actions.whitelistedHosts`:: +A list of hostnames that {kib} is allowed to connect to when built-in actions are triggered. It defaults to `[*]`, allowing any host, but keep in mind the potential for SSRF attacks when hosts are not explicitly whitelisted. An empty list `[]` can be used to block built-in actions from making any external connections. ++ +Note that hosts associated with built-in actions, such as Slack and PagerDuty, are not automatically whitelisted. If you are not using the default `[*]` setting, you must ensure that the corresponding endpoints are whitelisted as well. + +`xpack.actions.enabledActionTypes`:: +A list of action types that are enabled. It defaults to `[*]`, enabling all types. The names for built-in {kib} action types are prefixed with a `.` and include: `.server-log`, `.slack`, `.email`, `.index`, `.pagerduty`, and `.webhook`. An empty list `[]` will disable all action types. ++ +Disabled action types will not appear as an option when creating new connectors, but existing connectors and actions of that type will remain in {kib} and will not function. + +[float] +[[action-settings]] +==== Alert settings + +You do not need to configure any additional settings to use alerting in {kib}. diff --git a/docs/settings/settings-xkb.asciidoc b/docs/settings/settings-xkb.asciidoc index f509900e05843..f9727db838d55 100644 --- a/docs/settings/settings-xkb.asciidoc +++ b/docs/settings/settings-xkb.asciidoc @@ -10,6 +10,7 @@ include::{asciidoc-dir}/../../shared/settings.asciidoc[] For more {kib} configuration settings, see <>. +include::alert-action-settings.asciidoc[] include::apm-settings.asciidoc[] include::dev-settings.asciidoc[] include::graph-settings.asciidoc[] diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 3212cdfafd876..4eddb1779a26a 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -457,16 +457,7 @@ Rollup user interface. `i18n.locale`:: *Default: en* Set this value to change the Kibana interface language. Valid locales are: `en`, `zh-CN`, `ja-JP`. -`xpack.actions.enabledActionTypes:`:: *Default: +[ {asterisk} ]+* Set this value -to an array of action types that are enabled. An element of `*` indicates all -action types registered are enabled. The action types provided by Kibana are: -`.server-log`, `.slack`, `.email`, `.index`, `.pagerduty`, `.webhook`. - -`xpack.actions.whitelistedHosts:`:: *Default: +[ {asterisk} ]+* Set this value -to an array of host names which actions such as email, slack, pagerduty, and -webhook can connect to. An element of `*` indicates any host can be connected -to. An empty array indicates no hosts can be connected to. - +include::{docdir}/settings/alert-action-settings.asciidoc[] include::{docdir}/settings/apm-settings.asciidoc[] include::{docdir}/settings/dev-settings.asciidoc[] include::{docdir}/settings/graph-settings.asciidoc[] diff --git a/docs/user/security/authentication/index.asciidoc b/docs/user/security/authentication/index.asciidoc index a36b7b9c6d5f5..3906f15167bd0 100644 --- a/docs/user/security/authentication/index.asciidoc +++ b/docs/user/security/authentication/index.asciidoc @@ -12,6 +12,7 @@ - <> - <> - <> +- <> [[basic-authentication]] ==== Basic authentication @@ -214,3 +215,26 @@ leaked, it can't be re-used after logout. This is known as "local" logout. {kib} can also initiate a "global" logout or _Single Logout_ if it's supported by the external authentication provider and not explicitly disabled by {es}. In this case, the user is redirected to the external authentication provider for log out of all applications associated with the active provider session. + +[[kerberos]] +==== Kerberos single sign-on + +As with the previous SSOs, make sure that you have configured {es} first accordingly. See {ref}/kerberos-realm.html[Kerberos authentication]. + +Next, to enable Kerberos in {kib}, you will need to enable the Kerberos authentication provider in the `kibana.yml` configuration file, as follows: + +[source,yaml] +----------------------------------------------- +xpack.security.authc.providers: [kerberos] +----------------------------------------------- + +You may want to be able to authenticate with the basic authentication provider as a secondary mechanism or while you are setting up Kerberos for the stack: + +[source,yaml] +----------------------------------------------- +xpack.security.authc.providers: [kerberos, basic] +----------------------------------------------- + +As a reminder, the order is important as it determines the order in which each authentication provider is attempted. + +Kibana uses SPNEGO, which wraps the Kerberos protocol for use with HTTP, extending it to web applications. At the end of the Kerberos handshake, Kibana will forward the service ticket to Elasticsearch. Elasticsearch will unpack it and it will respond with an access and refresh token which are then used for subsequent authentication. diff --git a/src/core/public/overlays/modal/modal_service.tsx b/src/core/public/overlays/modal/modal_service.tsx index ba7887b1afa5c..3cf1fe745be8e 100644 --- a/src/core/public/overlays/modal/modal_service.tsx +++ b/src/core/public/overlays/modal/modal_service.tsx @@ -20,7 +20,7 @@ /* eslint-disable max-classes-per-file */ import { i18n as t } from '@kbn/i18n'; -import { EuiModal, EuiConfirmModal, EuiOverlayMask } from '@elastic/eui'; +import { EuiModal, EuiConfirmModal, EuiOverlayMask, EuiConfirmModalProps } from '@elastic/eui'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { Subject } from 'rxjs'; @@ -68,6 +68,7 @@ export interface OverlayModalConfirmOptions { className?: string; closeButtonAriaLabel?: string; 'data-test-subj'?: string; + defaultFocusedButton?: EuiConfirmModalProps['defaultFocusedButton']; } /** diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index fb48524c20fb9..aa7ca4fee675e 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -6,6 +6,7 @@ import { Breadcrumb } from '@elastic/eui'; import { EuiButtonEmptyProps } from '@elastic/eui'; +import { EuiConfirmModalProps } from '@elastic/eui'; import { EuiGlobalToastListToast } from '@elastic/eui'; import { ExclusiveUnion } from '@elastic/eui'; import { IconType } from '@elastic/eui'; diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index 78fc041345577..ef114f51f3100 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -88,7 +88,6 @@ export const IGNORE_DIRECTORY_GLOBS = [ 'src/babel-*', 'packages/*', 'packages/kbn-ui-framework/generator-kui', - 'src/legacy/ui/public/angular-bootstrap', 'src/legacy/ui/public/flot-charts', 'src/legacy/ui/public/utils/lodash-mixins', 'test/functional/fixtures/es_archiver/visualize_source-filters', @@ -124,9 +123,6 @@ export const TEMPORARILY_IGNORED_PATHS = [ 'src/legacy/core_plugins/timelion/server/series_functions/__tests__/fixtures/tlConfig.js', 'src/fixtures/config_upgrade_from_4.0.0_to_4.0.1-snapshot.json', 'src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_seriesMultiple.js', - 'src/legacy/ui/public/angular-bootstrap/bindHtml/bindHtml.js', - 'src/legacy/ui/public/angular-bootstrap/tooltip/tooltip-html-unsafe-popup.html', - 'src/legacy/ui/public/angular-bootstrap/tooltip/tooltip-popup.html', 'src/legacy/ui/public/assets/favicons/android-chrome-192x192.png', 'src/legacy/ui/public/assets/favicons/android-chrome-256x256.png', 'src/legacy/ui/public/assets/favicons/android-chrome-512x512.png', diff --git a/src/legacy/core_plugins/data/public/search/aggs/agg_config.ts b/src/legacy/core_plugins/data/public/search/aggs/agg_config.ts index ba7faf8c34b59..2b21c5c4868a5 100644 --- a/src/legacy/core_plugins/data/public/search/aggs/agg_config.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/agg_config.ts @@ -35,7 +35,7 @@ import { Schema } from './schemas'; import { ISearchSource, FetchOptions, - fieldFormats, + FieldFormatsContentType, KBN_FIELD_TYPES, } from '../../../../../../plugins/data/public'; @@ -383,7 +383,7 @@ export class AggConfig { return this.aggConfigs.timeRange; } - fieldFormatter(contentType?: fieldFormats.ContentType, defaultFormat?: any) { + fieldFormatter(contentType?: FieldFormatsContentType, defaultFormat?: any) { const format = this.type && this.type.getFormat(this); if (format) { @@ -393,7 +393,7 @@ export class AggConfig { return this.fieldOwnFormatter(contentType, defaultFormat); } - fieldOwnFormatter(contentType?: fieldFormats.ContentType, defaultFormat?: any) { + fieldOwnFormatter(contentType?: FieldFormatsContentType, defaultFormat?: any) { const fieldFormatsService = npStart.plugins.data.fieldFormats; const field = this.getField(); let format = field && field.format; diff --git a/src/legacy/core_plugins/data/public/search/aggs/agg_type.ts b/src/legacy/core_plugins/data/public/search/aggs/agg_type.ts index 56299839d0a6d..5ccf0f65c0e92 100644 --- a/src/legacy/core_plugins/data/public/search/aggs/agg_type.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/agg_type.ts @@ -29,7 +29,7 @@ import { BaseParamType } from './param_types/base'; import { AggParamType } from './param_types/agg'; import { KBN_FIELD_TYPES, - fieldFormats, + IFieldFormat, ISearchSource, } from '../../../../../../plugins/data/public'; @@ -58,7 +58,7 @@ export interface AggTypeConfig< inspectorAdapters: Adapters, abortSignal?: AbortSignal ) => Promise; - getFormat?: (agg: TAggConfig) => fieldFormats.FieldFormat; + getFormat?: (agg: TAggConfig) => IFieldFormat; getValue?: (agg: TAggConfig, bucket: any) => any; getKey?: (bucket: any, key: any, agg: TAggConfig) => any; } @@ -199,7 +199,7 @@ export class AggType< * @param {agg} agg - the agg to pick a format for * @return {FieldFormat} */ - getFormat: (agg: TAggConfig) => fieldFormats.FieldFormat; + getFormat: (agg: TAggConfig) => IFieldFormat; getValue: (agg: TAggConfig, bucket: any) => any; diff --git a/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts index e224253a6e314..41e806668337e 100644 --- a/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts @@ -19,7 +19,7 @@ import moment from 'moment'; import { createFilterDateRange } from './date_range'; -import { fieldFormats } from '../../../../../../../../plugins/data/public'; +import { fieldFormats, FieldFormatsGetConfigFn } from '../../../../../../../../plugins/data/public'; import { AggConfigs } from '../../agg_configs'; import { BUCKET_TYPES } from '../bucket_agg_types'; import { IBucketAggConfig } from '../_bucket_agg_type'; @@ -28,7 +28,7 @@ jest.mock('ui/new_platform'); describe('AggConfig Filters', () => { describe('Date range', () => { - const getConfig = (() => {}) as fieldFormats.GetConfigFn; + const getConfig = (() => {}) as FieldFormatsGetConfigFn; const getAggConfigs = () => { const field = { name: '@timestamp', diff --git a/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/histogram.test.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/histogram.test.ts index 1a78967261fa6..9f845847df5d9 100644 --- a/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/histogram.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/histogram.test.ts @@ -20,13 +20,13 @@ import { createFilterHistogram } from './histogram'; import { AggConfigs } from '../../agg_configs'; import { BUCKET_TYPES } from '../bucket_agg_types'; import { IBucketAggConfig } from '../_bucket_agg_type'; -import { fieldFormats } from '../../../../../../../../plugins/data/public'; +import { fieldFormats, FieldFormatsGetConfigFn } from '../../../../../../../../plugins/data/public'; jest.mock('ui/new_platform'); describe('AggConfig Filters', () => { describe('histogram', () => { - const getConfig = (() => {}) as fieldFormats.GetConfigFn; + const getConfig = (() => {}) as FieldFormatsGetConfigFn; const getAggConfigs = () => { const field = { name: 'bytes', diff --git a/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/range.test.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/range.test.ts index 2f74f23721813..33344ca0a3484 100644 --- a/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/range.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/range.test.ts @@ -18,7 +18,7 @@ */ import { createFilterRange } from './range'; -import { fieldFormats } from '../../../../../../../../plugins/data/public'; +import { fieldFormats, FieldFormatsGetConfigFn } from '../../../../../../../../plugins/data/public'; import { AggConfigs } from '../../agg_configs'; import { BUCKET_TYPES } from '../bucket_agg_types'; import { IBucketAggConfig } from '../_bucket_agg_type'; @@ -27,7 +27,7 @@ jest.mock('ui/new_platform'); describe('AggConfig Filters', () => { describe('range', () => { - const getConfig = (() => {}) as fieldFormats.GetConfigFn; + const getConfig = (() => {}) as FieldFormatsGetConfigFn; const getAggConfigs = () => { const field = { name: 'bytes', diff --git a/src/legacy/core_plugins/data/public/search/aggs/buckets/range.test.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/range.test.ts index 4c0fa7311461e..b1b0c4bc30a58 100644 --- a/src/legacy/core_plugins/data/public/search/aggs/buckets/range.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/range.test.ts @@ -19,7 +19,7 @@ import { AggConfigs } from '../agg_configs'; import { BUCKET_TYPES } from './bucket_agg_types'; -import { fieldFormats } from '../../../../../../../plugins/data/public'; +import { FieldFormatsGetConfigFn, fieldFormats } from '../../../../../../../plugins/data/public'; jest.mock('ui/new_platform'); @@ -44,7 +44,7 @@ const buckets = [ ]; describe('Range Agg', () => { - const getConfig = (() => {}) as fieldFormats.GetConfigFn; + const getConfig = (() => {}) as FieldFormatsGetConfigFn; const getAggConfigs = () => { const field = { name: 'bytes', diff --git a/src/legacy/core_plugins/data/public/search/aggs/buckets/terms.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/terms.ts index b41b16af122fa..0ed44aa876744 100644 --- a/src/legacy/core_plugins/data/public/search/aggs/buckets/terms.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/terms.ts @@ -30,7 +30,8 @@ import { IAggConfigs } from '../agg_configs'; import { Adapters } from '../../../../../../../plugins/inspector/public'; import { ISearchSource, - fieldFormats, + IFieldFormat, + FieldFormatsContentType, KBN_FIELD_TYPES, } from '../../../../../../../plugins/data/public'; @@ -80,9 +81,9 @@ export const termsBucketAgg = new BucketAggType({ const params = agg.params; return agg.getFieldDisplayName() + ': ' + params.order.text; }, - getFormat(bucket): fieldFormats.FieldFormat { + getFormat(bucket): IFieldFormat { return { - getConverterFor: (type: fieldFormats.ContentType) => { + getConverterFor: (type: FieldFormatsContentType) => { return (val: any) => { if (val === '__other__') { return bucket.params.otherBucketLabel; @@ -94,7 +95,7 @@ export const termsBucketAgg = new BucketAggType({ return bucket.params.field.format.convert(val, type); }; }, - } as fieldFormats.FieldFormat; + } as IFieldFormat; }, createFilter: createFilterTerms, postFlightRequest: async ( diff --git a/src/legacy/core_plugins/kibana/public/dashboard/legacy.ts b/src/legacy/core_plugins/kibana/public/dashboard/legacy.ts index ca2dc9d5fb4f5..9c13337a71126 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/legacy.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/legacy.ts @@ -18,34 +18,14 @@ */ import { PluginInitializerContext } from 'kibana/public'; -import { npSetup, npStart, legacyChrome } from './legacy_imports'; -import { LegacyAngularInjectedDependencies } from './plugin'; +import { npSetup, npStart } from './legacy_imports'; import { start as data } from '../../../data/public/legacy'; import { start as embeddables } from '../../../embeddable_api/public/np_ready/public/legacy'; -import './dashboard_config'; import { plugin } from './index'; -/** - * Get dependencies relying on the global angular context. - * They also have to get resolved together with the legacy imports above - */ -async function getAngularDependencies(): Promise { - const injector = await legacyChrome.dangerouslyGetActiveInjector(); - - return { - dashboardConfig: injector.get('dashboardConfig'), - }; -} - (async () => { const instance = plugin({} as PluginInitializerContext); - instance.setup(npSetup.core, { - ...npSetup.plugins, - npData: npSetup.plugins.data, - __LEGACY: { - getAngularDependencies, - }, - }); + instance.setup(npSetup.core, npSetup.plugins); instance.start(npStart.core, { ...npStart.plugins, data, diff --git a/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts index b729691831e9a..57edf5e838170 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts @@ -30,15 +30,11 @@ export const legacyChrome = chrome; export { SavedObjectSaveOpts } from 'ui/saved_objects/types'; export { npSetup, npStart } from 'ui/new_platform'; export { subscribeWithScope } from 'ui/utils/subscribe_with_scope'; -// @ts-ignore -export { ConfirmationButtonTypes } from 'ui/modals/confirm_modal'; export { KbnUrl } from 'ui/url/kbn_url'; // @ts-ignore export { createTopNavDirective, createTopNavHelper } from 'ui/kbn_top_nav/kbn_top_nav'; // @ts-ignore export { KbnUrlProvider, RedirectWhenMissingProvider } from 'ui/url/index'; -// @ts-ignore -export { confirmModalFactory } from 'ui/modals/confirm_modal'; export { IInjector } from 'ui/chrome'; export { SavedObjectLoader } from 'ui/saved_objects'; export { VISUALIZE_EMBEDDABLE_TYPE } from '../../../visualizations/public/embeddable'; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts index b0e4785edcb0b..e608eb7b7f48c 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts @@ -17,7 +17,7 @@ * under the License. */ -import { EuiConfirmModal, EuiIcon } from '@elastic/eui'; +import { EuiIcon } from '@elastic/eui'; import angular, { IModule } from 'angular'; import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; import { @@ -30,7 +30,6 @@ import { import { Storage } from '../../../../../../plugins/kibana_utils/public'; import { configureAppAngularModule, - confirmModalFactory, createTopNavDirective, createTopNavHelper, IPrivate, @@ -54,7 +53,7 @@ export interface RenderDeps { navigation: NavigationStart; savedObjectsClient: SavedObjectsClientContract; savedDashboards: SavedObjectLoader; - dashboardConfig: any; + dashboardConfig: KibanaLegacyStart['dashboardConfig']; dashboardCapabilities: any; uiSettings: IUiSettingsClient; chrome: ChromeStart; @@ -111,7 +110,6 @@ function createLocalAngularModule(core: AppMountContext['core'], navigation: Nav createLocalConfigModule(core); createLocalKbnUrlModule(); createLocalTopNavModule(navigation); - createLocalConfirmModalModule(); createLocalIconModule(); const dashboardAngularModule = angular.module(moduleName, [ @@ -122,7 +120,6 @@ function createLocalAngularModule(core: AppMountContext['core'], navigation: Nav 'app/dashboard/TopNav', 'app/dashboard/KbnUrl', 'app/dashboard/Promise', - 'app/dashboard/ConfirmModal', 'app/dashboard/icon', ]); return dashboardAngularModule; @@ -134,13 +131,6 @@ function createLocalIconModule() { .directive('icon', reactDirective => reactDirective(EuiIcon)); } -function createLocalConfirmModalModule() { - angular - .module('app/dashboard/ConfirmModal', ['react']) - .factory('confirmModal', confirmModalFactory) - .directive('confirmModal', reactDirective => reactDirective(EuiConfirmModal)); -} - function createLocalKbnUrlModule() { angular .module('app/dashboard/KbnUrl', ['app/dashboard/Private', 'ngRoute']) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx index 0537e3f8fc456..ad69ef322a909 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx @@ -25,7 +25,7 @@ import { IInjector } from '../legacy_imports'; import { ViewMode } from '../../../../embeddable_api/public/np_ready/public'; import { SavedObjectDashboard } from '../saved_dashboard/saved_dashboard'; -import { DashboardAppState, SavedDashboardPanel, ConfirmModalFn } from './types'; +import { DashboardAppState, SavedDashboardPanel } from './types'; import { IIndexPattern, TimeRange, @@ -87,8 +87,6 @@ export interface DashboardAppScope extends ng.IScope { export function initDashboardAppDirective(app: any, deps: RenderDeps) { app.directive('dashboardApp', function($injector: IInjector) { - const confirmModal = $injector.get('confirmModal'); - return { restrict: 'E', controllerAs: 'dashboardApp', @@ -105,7 +103,6 @@ export function initDashboardAppDirective(app: any, deps: RenderDeps) { $route, $scope, $routeParams, - confirmModal, indexPatterns: deps.npDataStart.indexPatterns, kbnUrlStateStorage, history, diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx index 9f6b01d5beb49..0b55adc1d52be 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx @@ -19,6 +19,7 @@ import _, { uniq } from 'lodash'; import { i18n } from '@kbn/i18n'; +import { EUI_MODAL_CANCEL_BUTTON } from '@elastic/eui'; import React from 'react'; import angular from 'angular'; @@ -27,12 +28,7 @@ import { map } from 'rxjs/operators'; import { History } from 'history'; import { DashboardEmptyScreen, DashboardEmptyScreenProps } from './dashboard_empty_screen'; -import { - ConfirmationButtonTypes, - migrateLegacyQuery, - SavedObjectSaveOpts, - subscribeWithScope, -} from '../legacy_imports'; +import { migrateLegacyQuery, SavedObjectSaveOpts, subscribeWithScope } from '../legacy_imports'; import { COMPARE_ALL_OPTIONS, compareFilters, @@ -63,7 +59,7 @@ import { openAddPanelFlyout, ViewMode, } from '../../../../embeddable_api/public/np_ready/public'; -import { ConfirmModalFn, NavAction, SavedDashboardPanel } from './types'; +import { NavAction, SavedDashboardPanel } from './types'; import { showOptionsPopover } from './top_nav/show_options_popover'; import { DashboardSaveModal } from './top_nav/save_modal'; @@ -82,14 +78,14 @@ import { removeQueryParam, unhashUrl, } from '../../../../../../plugins/kibana_utils/public'; +import { KibanaLegacyStart } from '../../../../../../plugins/kibana_legacy/public'; export interface DashboardAppControllerDependencies extends RenderDeps { $scope: DashboardAppScope; $route: any; $routeParams: any; indexPatterns: IndexPatternsContract; - dashboardConfig: any; - confirmModal: ConfirmModalFn; + dashboardConfig: KibanaLegacyStart['dashboardConfig']; history: History; kbnUrlStateStorage: IKbnUrlStateStorage; } @@ -107,7 +103,6 @@ export class DashboardAppController { dashboardConfig, localStorage, indexPatterns, - confirmModal, savedQueryService, embeddables, share, @@ -634,27 +629,31 @@ export class DashboardAppController { } } - confirmModal( - i18n.translate('kbn.dashboard.changeViewModeConfirmModal.discardChangesDescription', { - defaultMessage: `Once you discard your changes, there's no getting them back.`, - }), - { - onConfirm: revertChangesAndExitEditMode, - onCancel: _.noop, - confirmButtonText: i18n.translate( - 'kbn.dashboard.changeViewModeConfirmModal.confirmButtonLabel', - { defaultMessage: 'Discard changes' } - ), - cancelButtonText: i18n.translate( - 'kbn.dashboard.changeViewModeConfirmModal.cancelButtonLabel', - { defaultMessage: 'Continue editing' } - ), - defaultFocusedButton: ConfirmationButtonTypes.CANCEL, - title: i18n.translate('kbn.dashboard.changeViewModeConfirmModal.discardChangesTitle', { - defaultMessage: 'Discard changes to dashboard?', + overlays + .openConfirm( + i18n.translate('kbn.dashboard.changeViewModeConfirmModal.discardChangesDescription', { + defaultMessage: `Once you discard your changes, there's no getting them back.`, }), - } - ); + { + confirmButtonText: i18n.translate( + 'kbn.dashboard.changeViewModeConfirmModal.confirmButtonLabel', + { defaultMessage: 'Discard changes' } + ), + cancelButtonText: i18n.translate( + 'kbn.dashboard.changeViewModeConfirmModal.cancelButtonLabel', + { defaultMessage: 'Continue editing' } + ), + defaultFocusedButton: EUI_MODAL_CANCEL_BUTTON, + title: i18n.translate('kbn.dashboard.changeViewModeConfirmModal.discardChangesTitle', { + defaultMessage: 'Discard changes to dashboard?', + }), + } + ) + .then(isConfirmed => { + if (isConfirmed) { + revertChangesAndExitEditMode(); + } + }); }; /** diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/types.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/types.ts index 3151fbf821b9f..146affda28200 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/types.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/types.ts @@ -137,15 +137,3 @@ export interface StagedFilter { operator: string; index: string; } - -export type ConfirmModalFn = ( - message: string, - confirmOptions: { - onConfirm: () => void; - onCancel: () => void; - confirmButtonText: string; - cancelButtonText: string; - defaultFocusedButton: string; - title: string; - } -) => void; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts b/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts index 7ae1c723a3914..09ae49f2305fd 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts @@ -45,30 +45,25 @@ import { SharePluginStart } from '../../../../../plugins/share/public'; import { AngularRenderedAppUpdater, KibanaLegacySetup, + KibanaLegacyStart, } from '../../../../../plugins/kibana_legacy/public'; import { createSavedDashboardLoader } from './saved_dashboard/saved_dashboards'; import { createKbnUrlTracker } from '../../../../../plugins/kibana_utils/public'; import { getQueryStateContainer } from '../../../../../plugins/data/public'; -export interface LegacyAngularInjectedDependencies { - dashboardConfig: any; -} - export interface DashboardPluginStartDependencies { data: DataStart; npData: NpDataStart; embeddables: IEmbeddableStart; navigation: NavigationStart; share: SharePluginStart; + kibanaLegacy: KibanaLegacyStart; } export interface DashboardPluginSetupDependencies { - __LEGACY: { - getAngularDependencies: () => Promise; - }; home: HomePublicPluginSetup; kibanaLegacy: KibanaLegacySetup; - npData: NpDataSetup; + data: NpDataSetup; } export class DashboardPlugin implements Plugin { @@ -78,6 +73,7 @@ export class DashboardPlugin implements Plugin { embeddables: IEmbeddableStart; navigation: NavigationStart; share: SharePluginStart; + dashboardConfig: KibanaLegacyStart['dashboardConfig']; } | null = null; private appStateUpdater = new BehaviorSubject(() => ({})); @@ -85,12 +81,7 @@ export class DashboardPlugin implements Plugin { public setup( core: CoreSetup, - { - __LEGACY: { getAngularDependencies }, - home, - kibanaLegacy, - npData, - }: DashboardPluginSetupDependencies + { home, kibanaLegacy, data: npData }: DashboardPluginSetupDependencies ) { const { querySyncStateContainer, stop: stopQuerySyncStateContainer } = getQueryStateContainer( npData.query @@ -126,8 +117,8 @@ export class DashboardPlugin implements Plugin { navigation, share, npDataStart, + dashboardConfig, } = this.startDependencies; - const angularDependencies = await getAngularDependencies(); const savedDashboards = createSavedDashboardLoader({ savedObjectsClient, indexPatterns: npDataStart.indexPatterns, @@ -137,7 +128,7 @@ export class DashboardPlugin implements Plugin { const deps: RenderDeps = { core: contextCore as LegacyCoreStart, - ...angularDependencies, + dashboardConfig, navigation, share, npDataStart, @@ -186,7 +177,14 @@ export class DashboardPlugin implements Plugin { start( { savedObjects: { client: savedObjectsClient } }: CoreStart, - { data: dataStart, embeddables, navigation, npData, share }: DashboardPluginStartDependencies + { + data: dataStart, + embeddables, + navigation, + npData, + share, + kibanaLegacy: { dashboardConfig }, + }: DashboardPluginStartDependencies ) { this.startDependencies = { npDataStart: npData, @@ -194,6 +192,7 @@ export class DashboardPlugin implements Plugin { embeddables, navigation, share, + dashboardConfig, }; } diff --git a/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts b/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts index cc4dabd123ff4..eb6d7e6467f2f 100644 --- a/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts +++ b/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts @@ -21,7 +21,6 @@ // these are necessary to bootstrap the local angular. // They can stay even after NP cutover import angular from 'angular'; -import 'ui/angular-bootstrap'; import { EuiIcon } from '@elastic/eui'; // @ts-ignore import { StateProvider } from 'ui/state_management/state'; @@ -64,6 +63,7 @@ import { createFieldChooserDirective } from './np_ready/components/field_chooser import { createDiscoverFieldDirective } from './np_ready/components/field_chooser/discover_field'; import { CollapsibleSidebarProvider } from './np_ready/angular/directives/collapsible_sidebar/collapsible_sidebar'; import { DiscoverStartPlugins } from './plugin'; +import { initAngularBootstrap } from '../../../../../plugins/kibana_legacy/public'; import { createCssTruncateDirective } from './np_ready/angular/directives/css_truncate'; // @ts-ignore import { FixedScrollProvider } from './np_ready/angular/directives/fixed_scroll'; @@ -85,6 +85,7 @@ import { * needs to render, so in the end the current 'kibana' angular module is no longer necessary */ export function getInnerAngularModule(name: string, core: CoreStart, deps: DiscoverStartPlugins) { + initAngularBootstrap(); const module = initializeInnerAngularModule(name, core, deps.navigation, deps.data); configureAppAngularModule(module, core as LegacyCoreStart, true); return module; diff --git a/src/legacy/core_plugins/kibana/public/home/index.ts b/src/legacy/core_plugins/kibana/public/home/index.ts index f02ec234e0a83..c4e58e1a5e1ae 100644 --- a/src/legacy/core_plugins/kibana/public/home/index.ts +++ b/src/legacy/core_plugins/kibana/public/home/index.ts @@ -17,7 +17,6 @@ * under the License. */ -import { FeatureCatalogueRegistryProvider } from 'ui/registry/feature_catalogue'; import { npSetup, npStart } from 'ui/new_platform'; import chrome from 'ui/chrome'; import { HomePlugin, LegacyAngularInjectedDependencies } from './plugin'; @@ -44,26 +43,12 @@ async function getAngularDependencies(): Promise { const instance = new HomePlugin(); instance.setup(npSetup.core, { ...npSetup.plugins, __LEGACY: { metadata: npStart.core.injectedMetadata.getLegacyMetadata(), - getFeatureCatalogueEntries: async () => { - if (!copiedLegacyCatalogue) { - const injector = await chrome.dangerouslyGetActiveInjector(); - const Private = injector.get('Private'); - // Merge legacy registry with new registry - (Private(FeatureCatalogueRegistryProvider as any) as any).inTitleOrder.map( - npSetup.plugins.home.featureCatalogue.register - ); - copiedLegacyCatalogue = true; - } - return npStart.plugins.home.featureCatalogue.get(); - }, getAngularDependencies, }, }); diff --git a/src/legacy/core_plugins/kibana/public/home/kibana_services.ts b/src/legacy/core_plugins/kibana/public/home/kibana_services.ts index 90fb32a88d43c..66c4d995e2566 100644 --- a/src/legacy/core_plugins/kibana/public/home/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/home/kibana_services.ts @@ -31,14 +31,13 @@ import { import { UiStatsMetricType } from '@kbn/analytics'; import { Environment, - FeatureCatalogueEntry, HomePublicPluginSetup, + FeatureCatalogueEntry, } from '../../../../../plugins/home/public'; import { KibanaLegacySetup } from '../../../../../plugins/kibana_legacy/public'; export interface HomeKibanaServices { indexPatternService: any; - getFeatureCatalogueEntries: () => Promise; metadata: { app: unknown; bundleId: string; @@ -58,6 +57,7 @@ export interface HomeKibanaServices { uiSettings: IUiSettingsClient; config: KibanaLegacySetup['config']; homeConfig: HomePublicPluginSetup['config']; + directories: readonly FeatureCatalogueEntry[]; http: HttpStart; savedObjectsClient: SavedObjectsClientContract; toastNotifications: NotificationsSetup['toasts']; diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/application.tsx b/src/legacy/core_plugins/kibana/public/home/np_ready/application.tsx index 8345491d99972..2149885f3ee11 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/application.tsx +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/application.tsx @@ -26,8 +26,7 @@ import { getServices } from '../kibana_services'; export const renderApp = async (element: HTMLElement) => { const homeTitle = i18n.translate('kbn.home.breadcrumbs.homeTitle', { defaultMessage: 'Home' }); - const { getFeatureCatalogueEntries, chrome } = getServices(); - const directories = await getFeatureCatalogueEntries(); + const { directories, chrome } = getServices(); chrome.setBreadcrumbs([{ text: homeTitle }]); render(, element); diff --git a/src/legacy/core_plugins/kibana/public/home/plugin.ts b/src/legacy/core_plugins/kibana/public/home/plugin.ts index 5802f33627fb3..e530906d5698e 100644 --- a/src/legacy/core_plugins/kibana/public/home/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/home/plugin.ts @@ -25,9 +25,9 @@ import { KibanaLegacySetup } from '../../../../../plugins/kibana_legacy/public'; import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/public'; import { Environment, - FeatureCatalogueEntry, HomePublicPluginStart, HomePublicPluginSetup, + FeatureCatalogueEntry, } from '../../../../../plugins/home/public'; export interface LegacyAngularInjectedDependencies { @@ -55,7 +55,6 @@ export interface HomePluginSetupDependencies { devMode: boolean; uiSettings: { defaults: UiSettingsState; user?: UiSettingsState | undefined }; }; - getFeatureCatalogueEntries: () => Promise; getAngularDependencies: () => Promise; }; usageCollection: UsageCollectionSetup; @@ -67,6 +66,7 @@ export class HomePlugin implements Plugin { private dataStart: DataPublicPluginStart | null = null; private savedObjectsClient: any = null; private environment: Environment | null = null; + private directories: readonly FeatureCatalogueEntry[] | null = null; setup( core: CoreSetup, @@ -100,6 +100,7 @@ export class HomePlugin implements Plugin { environment: this.environment!, config: kibanaLegacy.config, homeConfig: home.config, + directories: this.directories!, ...angularDependencies, }); const { renderApp } = await import('./np_ready/application'); @@ -110,6 +111,7 @@ export class HomePlugin implements Plugin { start(core: CoreStart, { data, home }: HomePluginStartDependencies) { this.environment = home.environment.get(); + this.directories = home.featureCatalogue.get(); this.dataStart = data; this.savedObjectsClient = core.savedObjects.client; } diff --git a/src/legacy/core_plugins/kibana/public/management/index.js b/src/legacy/core_plugins/kibana/public/management/index.js index 1305310b6f615..6e5269e11652f 100644 --- a/src/legacy/core_plugins/kibana/public/management/index.js +++ b/src/legacy/core_plugins/kibana/public/management/index.js @@ -18,7 +18,6 @@ */ import React from 'react'; -import { i18n } from '@kbn/i18n'; import { render, unmountComponentAtNode } from 'react-dom'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -30,10 +29,6 @@ import appTemplate from './app.html'; import landingTemplate from './landing.html'; import { management, MANAGEMENT_BREADCRUMB } from 'ui/management'; import { ManagementSidebarNav } from '../../../../../plugins/management/public'; -import { - FeatureCatalogueRegistryProvider, - FeatureCatalogueCategory, -} from 'ui/registry/feature_catalogue'; import { timefilter } from 'ui/timefilter'; import { EuiPageContent, @@ -170,19 +165,3 @@ uiModules.get('apps/management').directive('kbnManagementLanding', function(kbnV }, }; }); - -FeatureCatalogueRegistryProvider.register(() => { - return { - id: 'stack-management', - title: i18n.translate('kbn.stackManagement.managementLabel', { - defaultMessage: 'Stack Management', - }), - description: i18n.translate('kbn.stackManagement.managementDescription', { - defaultMessage: 'Your center console for managing the Elastic Stack.', - }), - icon: 'managementApp', - path: '/app/kibana#/management', - showOnHomePage: false, - category: FeatureCatalogueCategory.ADMIN, - }; -}); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.js index 77b43a651d548..b5c6000eb2fe1 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.js @@ -43,6 +43,7 @@ export class CreateIndexPatternWizard extends Component { indexPatternCreationType: PropTypes.object.isRequired, config: PropTypes.object.isRequired, changeUrl: PropTypes.func.isRequired, + openConfirm: PropTypes.func.isRequired, }).isRequired, }; @@ -142,12 +143,16 @@ export class CreateIndexPatternWizard extends Component { values: { title: this.title }, defaultMessage: "An index pattern with the title '{title}' already exists.", }); - try { - await services.confirmModalPromise(confirmMessage, { - confirmButtonText: 'Go to existing pattern', - }); + + const isConfirmed = await services.openConfirm(confirmMessage, { + confirmButtonText: i18n.translate('kbn.management.indexPattern.goToPatternButtonLabel', { + defaultMessage: 'Go to existing pattern', + }), + }); + + if (isConfirmed) { return services.changeUrl(`/management/kibana/index_patterns/${indexPatternId}`); - } catch (err) { + } else { return false; } } diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js index d1087b4575e82..d06bc8784de51 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js @@ -43,10 +43,10 @@ uiRoutes.when('/management/kibana/index_pattern', { $http: npStart.core.http, savedObjectsClient: npStart.core.savedObjects.client, indexPatternCreationType, - confirmModalPromise: $injector.get('confirmModalPromise'), changeUrl: url => { $scope.$evalAsync(() => kbnUrl.changePath(url)); }, + openConfirm: npStart.core.overlays.openConfirm, }; const initialQuery = $routeParams.id ? decodeURIComponent($routeParams.id) : undefined; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js index eb7358c66e226..0cbac20a947bf 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js @@ -198,8 +198,7 @@ uiModules $route, Promise, config, - Private, - confirmModal + Private ) { const { startSyncingState, @@ -290,15 +289,19 @@ uiModules confirmButtonText: i18n.translate('kbn.management.editIndexPattern.refreshButton', { defaultMessage: 'Refresh', }), - onConfirm: async () => { - await $scope.indexPattern.init(true); - $scope.fields = $scope.indexPattern.getNonScriptedFields(); - }, title: i18n.translate('kbn.management.editIndexPattern.refreshHeader', { defaultMessage: 'Refresh field list?', }), }; - confirmModal(confirmMessage, confirmModalOptions); + + npStart.core.overlays + .openConfirm(confirmMessage, confirmModalOptions) + .then(async isConfirmed => { + if (isConfirmed) { + await $scope.indexPattern.init(true); + $scope.fields = $scope.indexPattern.getNonScriptedFields(); + } + }); }; $scope.removePattern = function() { @@ -322,12 +325,16 @@ uiModules confirmButtonText: i18n.translate('kbn.management.editIndexPattern.deleteButton', { defaultMessage: 'Delete', }), - onConfirm: doRemove, title: i18n.translate('kbn.management.editIndexPattern.deleteHeader', { defaultMessage: 'Delete index pattern?', }), }; - confirmModal('', confirmModalOptions); + + npStart.core.overlays.openConfirm('', confirmModalOptions).then(isConfirmed => { + if (isConfirmed) { + doRemove(); + } + }); }; $scope.setDefaultPattern = function() { diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js index 8ab26f8c0d1c8..310797a7f3a0c 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js @@ -27,10 +27,6 @@ import indexTemplate from './index.html'; import indexPatternListTemplate from './list.html'; import { IndexPatternTable } from './index_pattern_table'; import { npStart } from 'ui/new_platform'; -import { - FeatureCatalogueRegistryProvider, - FeatureCatalogueCategory, -} from 'ui/registry/feature_catalogue'; import { i18n } from '@kbn/i18n'; import { I18nContext } from 'ui/i18n'; import { UICapabilitiesProvider } from 'ui/capabilities/react'; @@ -175,19 +171,3 @@ management.getSection('kibana').register('index_patterns', { order: 0, url: '#/management/kibana/index_patterns/', }); - -FeatureCatalogueRegistryProvider.register(() => { - return { - id: 'index_patterns', - title: i18n.translate('kbn.management.indexPatternHeader', { - defaultMessage: 'Index Patterns', - }), - description: i18n.translate('kbn.management.indexPatternLabel', { - defaultMessage: 'Manage the index patterns that help retrieve your data from Elasticsearch.', - }), - icon: 'indexPatternApp', - path: '/app/kibana#/management/kibana/index_patterns', - showOnHomePage: true, - category: FeatureCatalogueCategory.ADMIN, - }; -}); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/_objects.js b/src/legacy/core_plugins/kibana/public/management/sections/objects/_objects.js index c16e4cb00c2bd..e3ab862cd84b7 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/_objects.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/_objects.js @@ -38,7 +38,6 @@ function updateObjectsTable($scope, $injector) { const $http = $injector.get('$http'); const kbnUrl = $injector.get('kbnUrl'); const config = $injector.get('config'); - const confirmModalPromise = $injector.get('confirmModalPromise'); const savedObjectsClient = npStart.core.savedObjects.client; const services = savedObjectManagementRegistry.all().map(obj => obj.service); @@ -54,7 +53,7 @@ function updateObjectsTable($scope, $injector) { fatalError(error, location)); } const confirmModalOptions = { - onConfirm: doDelete, confirmButtonText: i18n.translate( 'kbn.management.objects.confirmModalOptions.deleteButtonLabel', { @@ -244,12 +244,19 @@ uiModules defaultMessage: 'Delete saved Kibana object?', }), }; - confirmModal( - i18n.translate('kbn.management.objects.confirmModalOptions.modalDescription', { - defaultMessage: "You can't recover deleted objects", - }), - confirmModalOptions - ); + + overlays + .openConfirm( + i18n.translate('kbn.management.objects.confirmModalOptions.modalDescription', { + defaultMessage: "You can't recover deleted objects", + }), + confirmModalOptions + ) + .then(isConfirmed => { + if (isConfirmed) { + doDelete(); + } + }); }; $scope.submit = function() { diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/index.js b/src/legacy/core_plugins/kibana/public/management/sections/objects/index.js index 7bd57e87bc5c9..3965c42ac088d 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/index.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/index.js @@ -23,10 +23,6 @@ import './_view'; import './_objects'; import 'ace'; import { uiModules } from 'ui/modules'; -import { - FeatureCatalogueRegistryProvider, - FeatureCatalogueCategory, -} from 'ui/registry/feature_catalogue'; // add the module deps to this module uiModules.get('apps/management'); @@ -38,20 +34,3 @@ management.getSection('kibana').register('objects', { order: 10, url: '#/management/kibana/objects', }); - -FeatureCatalogueRegistryProvider.register(() => { - return { - id: 'saved_objects', - title: i18n.translate('kbn.management.objects.savedObjectsTitle', { - defaultMessage: 'Saved Objects', - }), - description: i18n.translate('kbn.management.objects.savedObjectsDescription', { - defaultMessage: - 'Import, export, and manage your saved searches, visualizations, and dashboards.', - }), - icon: 'savedObjectsApp', - path: '/app/kibana#/management/kibana/objects', - showOnHomePage: true, - category: FeatureCatalogueCategory.ADMIN, - }; -}); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_saved_objects.js b/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_saved_objects.js index e3cee4186e278..e13e8c1efe8f7 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_saved_objects.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_saved_objects.js @@ -79,24 +79,25 @@ async function importIndexPattern(doc, indexPatterns, overwriteAll, confirmModal let newId = await emptyPattern.create(overwriteAll); if (!newId) { // We can override and we want to prompt for confirmation - try { - await confirmModalPromise( - i18n.translate('kbn.management.indexPattern.confirmOverwriteLabel', { - values: { title: this.title }, - defaultMessage: "Are you sure you want to overwrite '{title}'?", + const isConfirmed = await confirmModalPromise( + i18n.translate('kbn.management.indexPattern.confirmOverwriteLabel', { + values: { title: this.title }, + defaultMessage: "Are you sure you want to overwrite '{title}'?", + }), + { + title: i18n.translate('kbn.management.indexPattern.confirmOverwriteTitle', { + defaultMessage: 'Overwrite {type}?', + values: { type }, }), - { - title: i18n.translate('kbn.management.indexPattern.confirmOverwriteTitle', { - defaultMessage: 'Overwrite {type}?', - values: { type }, - }), - confirmButtonText: i18n.translate('kbn.management.indexPattern.confirmOverwriteButton', { - defaultMessage: 'Overwrite', - }), - } - ); + confirmButtonText: i18n.translate('kbn.management.indexPattern.confirmOverwriteButton', { + defaultMessage: 'Overwrite', + }), + } + ); + + if (isConfirmed) { newId = await emptyPattern.create(true); - } catch (err) { + } else { return; } } diff --git a/src/legacy/core_plugins/kibana/public/management/sections/settings/index.js b/src/legacy/core_plugins/kibana/public/management/sections/settings/index.js index 6d8987b1a928e..16d70a9f4ed57 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/settings/index.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/settings/index.js @@ -23,10 +23,6 @@ import { uiModules } from 'ui/modules'; import { capabilities } from 'ui/capabilities'; import { I18nContext } from 'ui/i18n'; import indexTemplate from './index.html'; -import { - FeatureCatalogueRegistryProvider, - FeatureCatalogueCategory, -} from 'ui/registry/feature_catalogue'; import React from 'react'; import { AdvancedSettings } from './advanced_settings'; @@ -83,19 +79,3 @@ management.getSection('kibana').register('settings', { order: 20, url: '#/management/kibana/settings', }); - -FeatureCatalogueRegistryProvider.register(() => { - return { - id: 'advanced_settings', - title: i18n.translate('kbn.management.settings.advancedSettingsLabel', { - defaultMessage: 'Advanced Settings', - }), - description: i18n.translate('kbn.management.settings.advancedSettingsDescription', { - defaultMessage: 'Directly edit settings that control behavior in Kibana.', - }), - icon: 'advancedSettingsApp', - path: '/app/kibana#/management/kibana/settings', - showOnHomePage: false, - category: FeatureCatalogueCategory.ADMIN, - }; -}); diff --git a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts index d3a7f6ac1ff7d..ff7d167ccaacd 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts @@ -46,8 +46,6 @@ export { subscribeWithScope } from 'ui/utils/subscribe_with_scope'; export { EventsProvider } from 'ui/events'; // @ts-ignore export { createTopNavDirective, createTopNavHelper } from 'ui/kbn_top_nav/kbn_top_nav'; -// @ts-ignore -export { confirmModalFactory } from 'ui/modals/confirm_modal'; export { registerTimefilterWithGlobalStateFactory } from 'ui/timefilter/setup_router'; // @ts-ignore export { KbnUrlProvider, RedirectWhenMissingProvider } from 'ui/url'; diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts index 222b035708976..44e7e9c2a7413 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts @@ -18,7 +18,6 @@ */ import angular, { IModule } from 'angular'; -import { EuiConfirmModal } from '@elastic/eui'; import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; import { AppMountContext, LegacyCoreStart } from 'kibana/public'; @@ -26,7 +25,6 @@ import { AppStateProvider, AppState, configureAppAngularModule, - confirmModalFactory, createTopNavDirective, createTopNavHelper, EventsProvider, @@ -93,7 +91,6 @@ function createLocalAngularModule(core: AppMountContext['core'], navigation: Nav createLocalStateModule(); createLocalPersistedStateModule(); createLocalTopNavModule(navigation); - createLocalConfirmModalModule(); const visualizeAngularModule: IModule = angular.module(moduleName, [ ...thirdPartyAngularDependencies, @@ -103,18 +100,10 @@ function createLocalAngularModule(core: AppMountContext['core'], navigation: Nav 'app/visualize/PersistedState', 'app/visualize/TopNav', 'app/visualize/State', - 'app/visualize/ConfirmModal', ]); return visualizeAngularModule; } -function createLocalConfirmModalModule() { - angular - .module('app/visualize/ConfirmModal', ['react']) - .factory('confirmModal', confirmModalFactory) - .directive('confirmModal', reactDirective => reactDirective(EuiConfirmModal)); -} - function createLocalStateModule() { angular .module('app/visualize/State', [ diff --git a/src/legacy/core_plugins/management/public/legacy.ts b/src/legacy/core_plugins/management/public/legacy.ts index 7c17f0c6bddc0..4481bad79c47d 100644 --- a/src/legacy/core_plugins/management/public/legacy.ts +++ b/src/legacy/core_plugins/management/public/legacy.ts @@ -41,5 +41,5 @@ import { plugin } from '.'; const pluginInstance = plugin({} as PluginInitializerContext); -export const setup = pluginInstance.setup(npSetup.core, {}); +export const setup = pluginInstance.setup(npSetup.core, { home: npSetup.plugins.home }); export const start = pluginInstance.start(npStart.core, {}); diff --git a/src/legacy/core_plugins/management/public/np_ready/mocks.ts b/src/legacy/core_plugins/management/public/np_ready/mocks.ts index 13a0cf4c891a3..5ed7c045d1f64 100644 --- a/src/legacy/core_plugins/management/public/np_ready/mocks.ts +++ b/src/legacy/core_plugins/management/public/np_ready/mocks.ts @@ -19,7 +19,12 @@ import { PluginInitializerContext } from 'src/core/public'; import { coreMock } from '../../../../../core/public/mocks'; -import { ManagementSetup, ManagementStart, ManagementPlugin } from './plugin'; +import { + ManagementSetup, + ManagementStart, + ManagementPlugin, + ManagementPluginSetupDependencies, +} from './plugin'; const createSetupContract = (): ManagementSetup => ({ indexPattern: { @@ -49,7 +54,13 @@ const createStartContract = (): ManagementStart => ({}); const createInstance = async () => { const plugin = new ManagementPlugin({} as PluginInitializerContext); - const setup = plugin.setup(coreMock.createSetup(), {}); + const setup = plugin.setup(coreMock.createSetup(), ({ + home: { + featureCatalogue: { + register: jest.fn(), + }, + }, + } as unknown) as ManagementPluginSetupDependencies); const doStart = () => plugin.start(coreMock.createStart(), {}); return { diff --git a/src/legacy/core_plugins/management/public/np_ready/plugin.ts b/src/legacy/core_plugins/management/public/np_ready/plugin.ts index 032a46439ba55..7dd2b23d40610 100644 --- a/src/legacy/core_plugins/management/public/np_ready/plugin.ts +++ b/src/legacy/core_plugins/management/public/np_ready/plugin.ts @@ -17,14 +17,16 @@ * under the License. */ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public'; +import { HomePublicPluginSetup } from 'src/plugins/home/public'; import { IndexPatternManagementService, IndexPatternManagementSetup } from './services'; import { SavedObjectsManagementService, SavedObjectsManagementServiceSetup, } from './services/saved_objects_management'; -// eslint-disable-next-line @typescript-eslint/no-empty-interface -interface ManagementPluginSetupDependencies {} +export interface ManagementPluginSetupDependencies { + home: HomePublicPluginSetup; +} // eslint-disable-next-line @typescript-eslint/no-empty-interface interface ManagementPluginStartDependencies {} @@ -50,10 +52,10 @@ export class ManagementPlugin constructor(initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup, deps: ManagementPluginSetupDependencies) { + public setup(core: CoreSetup, { home }: ManagementPluginSetupDependencies) { return { - indexPattern: this.indexPattern.setup({ httpClient: core.http }), - savedObjects: this.savedObjects.setup(), + indexPattern: this.indexPattern.setup({ httpClient: core.http, home }), + savedObjects: this.savedObjects.setup({ home }), }; } diff --git a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/index_pattern_management_service.ts b/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/index_pattern_management_service.ts index b421024b60f4b..2b6f008dd928a 100644 --- a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/index_pattern_management_service.ts +++ b/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/index_pattern_management_service.ts @@ -17,12 +17,18 @@ * under the License. */ +import { i18n } from '@kbn/i18n'; +import { + FeatureCatalogueCategory, + HomePublicPluginSetup, +} from '../../../../../../../plugins/home/public'; import { HttpSetup } from '../../../../../../../core/public'; import { IndexPatternCreationManager, IndexPatternCreationConfig } from './creation'; import { IndexPatternListManager, IndexPatternListConfig } from './list'; interface SetupDependencies { httpClient: HttpSetup; + home: HomePublicPluginSetup; } /** @@ -31,13 +37,28 @@ interface SetupDependencies { * @internal */ export class IndexPatternManagementService { - public setup({ httpClient }: SetupDependencies) { + public setup({ httpClient, home }: SetupDependencies) { const creation = new IndexPatternCreationManager(httpClient); const list = new IndexPatternListManager(); creation.add(IndexPatternCreationConfig); list.add(IndexPatternListConfig); + home.featureCatalogue.register({ + id: 'index_patterns', + title: i18n.translate('management.indexPatternHeader', { + defaultMessage: 'Index Patterns', + }), + description: i18n.translate('management.indexPatternLabel', { + defaultMessage: + 'Manage the index patterns that help retrieve your data from Elasticsearch.', + }), + icon: 'indexPatternApp', + path: '/app/kibana#/management/kibana/index_patterns', + showOnHomePage: true, + category: FeatureCatalogueCategory.ADMIN, + }); + return { creation, list, diff --git a/src/legacy/core_plugins/management/public/np_ready/services/saved_objects_management/saved_objects_management_service.ts b/src/legacy/core_plugins/management/public/np_ready/services/saved_objects_management/saved_objects_management_service.ts index d5e90d12cccc9..be102b2a4dce7 100644 --- a/src/legacy/core_plugins/management/public/np_ready/services/saved_objects_management/saved_objects_management_service.ts +++ b/src/legacy/core_plugins/management/public/np_ready/services/saved_objects_management/saved_objects_management_service.ts @@ -16,10 +16,35 @@ * specific language governing permissions and limitations * under the License. */ + +import { i18n } from '@kbn/i18n'; +import { + FeatureCatalogueCategory, + HomePublicPluginSetup, +} from '../../../../../../../plugins/home/public'; import { SavedObjectsManagementActionRegistry } from './saved_objects_management_action_registry'; +interface SetupDependencies { + home: HomePublicPluginSetup; +} + export class SavedObjectsManagementService { - public setup() { + public setup({ home }: SetupDependencies) { + home.featureCatalogue.register({ + id: 'saved_objects', + title: i18n.translate('management.objects.savedObjectsTitle', { + defaultMessage: 'Saved Objects', + }), + description: i18n.translate('management.objects.savedObjectsDescription', { + defaultMessage: + 'Import, export, and manage your saved searches, visualizations, and dashboards.', + }), + icon: 'savedObjectsApp', + path: '/app/kibana#/management/kibana/objects', + showOnHomePage: true, + category: FeatureCatalogueCategory.ADMIN, + }); + return { registry: SavedObjectsManagementActionRegistry, }; diff --git a/src/legacy/core_plugins/timelion/public/app.js b/src/legacy/core_plugins/timelion/public/app.js index a7fa9e0290a1c..ff8f75c23435e 100644 --- a/src/legacy/core_plugins/timelion/public/app.js +++ b/src/legacy/core_plugins/timelion/public/app.js @@ -114,7 +114,6 @@ app.controller('timelion', function( $timeout, AppState, config, - confirmModal, kbnUrl, Private ) { @@ -230,7 +229,6 @@ app.controller('timelion', function( } const confirmModalOptions = { - onConfirm: doDelete, confirmButtonText: i18n.translate('timelion.topNavMenu.delete.modal.confirmButtonLabel', { defaultMessage: 'Delete', }), @@ -241,12 +239,18 @@ app.controller('timelion', function( }; $scope.$evalAsync(() => { - confirmModal( - i18n.translate('timelion.topNavMenu.delete.modal.warningText', { - defaultMessage: `You can't recover deleted sheets.`, - }), - confirmModalOptions - ); + npStart.core.overlays + .openConfirm( + i18n.translate('timelion.topNavMenu.delete.modal.warningText', { + defaultMessage: `You can't recover deleted sheets.`, + }), + confirmModalOptions + ) + .then(isConfirmed => { + if (isConfirmed) { + doDelete(); + } + }); }); }, testId: 'timelionDeleteButton', diff --git a/src/legacy/core_plugins/timelion/public/register_feature.ts b/src/legacy/core_plugins/timelion/public/register_feature.ts deleted file mode 100644 index 7dd44b58bd1d7..0000000000000 --- a/src/legacy/core_plugins/timelion/public/register_feature.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { FeatureCatalogueCategory } from 'ui/registry/feature_catalogue'; -import { i18n } from '@kbn/i18n'; - -export const registerFeature = () => { - return { - id: 'timelion', - title: 'Timelion', - description: i18n.translate('timelion.registerFeatureDescription', { - defaultMessage: - 'Use an expression language to analyze time series data and visualize the results.', - }), - icon: 'timelionApp', - path: '/app/timelion', - showOnHomePage: false, - category: FeatureCatalogueCategory.DATA, - }; -}; diff --git a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.tsx b/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.tsx index 9cad09a2e435d..64abee729f4e7 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.tsx +++ b/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.tsx @@ -24,7 +24,7 @@ import { isColorDark } from '@elastic/eui'; import { getFormat } from '../legacy_imports'; import { MetricVisValue } from './metric_vis_value'; -import { fieldFormats } from '../../../../../plugins/data/public'; +import { FieldFormatsContentType, IFieldFormat } from '../../../../../plugins/data/public'; import { Context } from '../metric_vis_fn'; import { KibanaDatatable } from '../../../../../plugins/expressions/public'; import { getHeatmapColors } from '../../../../../plugins/charts/public'; @@ -100,9 +100,9 @@ export class MetricVisComponent extends Component { } private getFormattedValue = ( - fieldFormatter: fieldFormats.FieldFormat, + fieldFormatter: IFieldFormat, value: any, - format: fieldFormats.ContentType = 'text' + format: FieldFormatsContentType = 'text' ) => { if (isNaN(value)) return '-'; return fieldFormatter.convert(value, format); @@ -119,7 +119,7 @@ export class MetricVisComponent extends Component { const metrics: MetricVisMetric[] = []; let bucketColumnId: string; - let bucketFormatter: fieldFormats.FieldFormat; + let bucketFormatter: IFieldFormat; if (dimensions.bucket) { bucketColumnId = table.columns[dimensions.bucket.accessor].id; diff --git a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts index 4f535c62f56a0..28565e0181b84 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts +++ b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts @@ -19,6 +19,9 @@ import $ from 'jquery'; +// TODO This is an integration test and thus requires a running platform. When moving to the new platform, +// this test has to be migrated to the newly created integration test environment. +// eslint-disable-next-line @kbn/eslint/no-restricted-paths import { npStart } from 'ui/new_platform'; // @ts-ignore import getStubIndexPattern from 'fixtures/stubbed_logstash_index_pattern'; diff --git a/src/legacy/core_plugins/vis_type_table/public/get_inner_angular.ts b/src/legacy/core_plugins/vis_type_table/public/get_inner_angular.ts index 9f3a8327c9ad9..18d8e7bc9d8bb 100644 --- a/src/legacy/core_plugins/vis_type_table/public/get_inner_angular.ts +++ b/src/legacy/core_plugins/vis_type_table/public/get_inner_angular.ts @@ -21,7 +21,6 @@ // these are necessary to bootstrap the local angular. // They can stay even after NP cutover import angular from 'angular'; -import 'ui/angular-bootstrap'; import 'angular-recursion'; import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; import { CoreStart, LegacyCoreStart, IUiSettingsClient } from 'kibana/public'; @@ -34,6 +33,9 @@ import { StateManagementConfigProvider, configureAppAngularModule, } from './legacy_imports'; +import { initAngularBootstrap } from '../../../../plugins/kibana_legacy/public'; + +initAngularBootstrap(); const thirdPartyAngularDependencies = ['ngSanitize', 'ui.bootstrap', 'RecursionHelper']; diff --git a/src/legacy/core_plugins/vis_type_table/public/paginated_table/paginated_table.test.ts b/src/legacy/core_plugins/vis_type_table/public/paginated_table/paginated_table.test.ts index 781782e42fbaf..7352236f03feb 100644 --- a/src/legacy/core_plugins/vis_type_table/public/paginated_table/paginated_table.test.ts +++ b/src/legacy/core_plugins/vis_type_table/public/paginated_table/paginated_table.test.ts @@ -22,11 +22,15 @@ import angular, { IRootScopeService, IScope, ICompileService } from 'angular'; import $ from 'jquery'; import 'angular-sanitize'; import 'angular-mocks'; -import '../table_vis.mock'; import { getAngularModule } from '../get_inner_angular'; import { initTableVisLegacyModule } from '../table_vis_legacy_module'; -import { npStart } from '../legacy_imports'; +import { coreMock } from '../../../../../core/public/mocks'; + +jest.mock('ui/new_platform'); +jest.mock('../../../../../plugins/kibana_legacy/public/angular/angular_config', () => ({ + configureAppAngularModule: () => {}, +})); interface Sort { columnIndex: number; @@ -69,7 +73,7 @@ describe('Table Vis - Paginated table', () => { let paginatedTable: any; const initLocalAngular = () => { - const tableVisModule = getAngularModule('kibana/table_vis', npStart.core); + const tableVisModule = getAngularModule('kibana/table_vis', coreMock.createStart()); initTableVisLegacyModule(tableVisModule); }; diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis.mock.ts b/src/legacy/core_plugins/vis_type_table/public/table_vis.mock.ts deleted file mode 100644 index d04964cb7af03..0000000000000 --- a/src/legacy/core_plugins/vis_type_table/public/table_vis.mock.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { createUiNewPlatformMock } from 'ui/new_platform/__mocks__/helpers'; -import { StubBrowserStorage } from 'test_utils/stub_browser_storage'; -import { injectedMetadataServiceMock } from '../../../../core/public/mocks'; - -jest.doMock('ui/new_platform', () => { - const npMock = createUiNewPlatformMock(); - return { - npSetup: { - ...npMock.npSetup, - core: { - ...npMock.npSetup.core, - injectedMetadata: injectedMetadataServiceMock.createSetupContract(), - }, - }, - npStart: { - ...npMock.npStart, - core: { - ...npMock.npStart.core, - injectedMetadata: injectedMetadataServiceMock.createStartContract(), - }, - }, - }; -}); - -Object.assign(window, { - sessionStorage: new StubBrowserStorage(), -}); diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_controller.test.ts b/src/legacy/core_plugins/vis_type_table/public/table_vis_controller.test.ts index d8912975227bf..0e1e48d00a1b2 100644 --- a/src/legacy/core_plugins/vis_type_table/public/table_vis_controller.test.ts +++ b/src/legacy/core_plugins/vis_type_table/public/table_vis_controller.test.ts @@ -21,21 +21,26 @@ import angular, { IRootScopeService, IScope, ICompileService } from 'angular'; import 'angular-mocks'; import 'angular-sanitize'; import $ from 'jquery'; -import './table_vis.mock'; // @ts-ignore import StubIndexPattern from 'test_utils/stub_index_pattern'; import { getAngularModule } from './get_inner_angular'; import { initTableVisLegacyModule } from './table_vis_legacy_module'; -import { npStart, IAggConfig, tabifyAggResponse } from './legacy_imports'; import { tableVisTypeDefinition } from './table_vis_type'; import { Vis } from '../../visualizations/public'; -import { setup as visualizationsSetup } from '../../visualizations/public/np_ready/public/legacy'; // eslint-disable-next-line import { stubFields } from '../../../../plugins/data/public/stubs'; // eslint-disable-next-line -import { setFieldFormats } from '../../../../plugins/data/public/services'; import { tableVisResponseHandler } from './table_vis_response_handler'; +import { coreMock } from '../../../../core/public/mocks'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { AggConfigs } from 'ui/agg_types'; +import { tabifyAggResponse, IAggConfig } from './legacy_imports'; + +jest.mock('ui/new_platform'); +jest.mock('../../../../plugins/kibana_legacy/public/angular/angular_config', () => ({ + configureAppAngularModule: () => {}, +})); interface TableVisScope extends IScope { [key: string]: any; @@ -79,14 +84,11 @@ describe('Table Vis - Controller', () => { let stubIndexPattern: any; const initLocalAngular = () => { - const tableVisModule = getAngularModule('kibana/table_vis', npStart.core); + const tableVisModule = getAngularModule('kibana/table_vis', coreMock.createStart()); initTableVisLegacyModule(tableVisModule); }; beforeEach(initLocalAngular); - beforeAll(() => { - visualizationsSetup.types.createBaseVisualization(tableVisTypeDefinition); - }); beforeEach(angular.mock.module('kibana/table_vis')); beforeEach( @@ -98,38 +100,38 @@ describe('Table Vis - Controller', () => { ); beforeEach(() => { - setFieldFormats(({ - getDefaultInstance: jest.fn(), - } as unknown) as any); stubIndexPattern = new StubIndexPattern( 'logstash-*', (cfg: any) => cfg, 'time', stubFields, - npStart.core + coreMock.createStart() ); }); function getRangeVis(params?: object) { - // @ts-ignore - return new Vis(stubIndexPattern, { - type: 'table', - params: params || {}, - aggs: [ - { type: 'count', schema: 'metric' }, - { - type: 'range', - schema: 'bucket', - params: { - field: 'bytes', - ranges: [ - { from: 0, to: 1000 }, - { from: 1000, to: 2000 }, - ], + return ({ + type: tableVisTypeDefinition, + params: Object.assign({}, tableVisTypeDefinition.visConfig.defaults, params), + aggs: new AggConfigs( + stubIndexPattern, + [ + { type: 'count', schema: 'metric' }, + { + type: 'range', + schema: 'bucket', + params: { + field: 'bytes', + ranges: [ + { from: 0, to: 1000 }, + { from: 1000, to: 2000 }, + ], + }, }, - }, - ], - }); + ], + tableVisTypeDefinition.editorConfig.schemas.all + ), + } as unknown) as Vis; } const dimensions = { @@ -241,13 +243,13 @@ describe('Table Vis - Controller', () => { const vis = getRangeVis({ showPartialRows: true }); initController(vis); - expect(vis.isHierarchical()).toEqual(true); + expect(vis.type.hierarchicalData(vis)).toEqual(true); }); test('passes partialRows:false to tabify based on the vis params', () => { const vis = getRangeVis({ showPartialRows: false }); initController(vis); - expect(vis.isHierarchical()).toEqual(false); + expect(vis.type.hierarchicalData(vis)).toEqual(false); }); }); diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx index eed5ffe8c3584..ab7c2cd980c42 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx @@ -21,10 +21,10 @@ import React from 'react'; import { EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ValidatedDualRange } from 'ui/validated_range'; import { VisOptionsProps } from '../../../vis_default_editor/public'; import { SelectOption, SwitchOption } from '../../../vis_type_vislib/public'; import { TagCloudVisParams } from '../types'; +import { ValidatedDualRange } from '../legacy_imports'; function TagCloudOptions({ stateParams, setValue, vis }: VisOptionsProps) { const handleFontSizeChange = ([minFontSize, maxFontSize]: [string | number, string | number]) => { diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js index f2163abbbc723..5528278adf4eb 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js @@ -21,9 +21,9 @@ import React from 'react'; import * as Rx from 'rxjs'; import { take } from 'rxjs/operators'; import { render, unmountComponentAtNode } from 'react-dom'; +import { I18nProvider } from '@kbn/i18n/react'; -import { getFormat } from 'ui/visualize/loader/pipeline_helpers/utilities'; -import { I18nContext } from 'ui/i18n'; +import { getFormat } from '../legacy_imports'; import { Label } from './label'; import { TagCloud } from './tag_cloud'; @@ -65,9 +65,9 @@ export function createTagCloudVisualization({ colors }) { this._containerNode.appendChild(this._feedbackNode); this._feedbackMessage = React.createRef(); render( - + - , + , this._feedbackNode ); diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/legacy_imports.ts b/src/legacy/core_plugins/vis_type_tagcloud/public/legacy_imports.ts index ecc56ea0c34be..d5b442bc5b346 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/legacy_imports.ts +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/legacy_imports.ts @@ -18,3 +18,5 @@ */ export { Schemas } from 'ui/agg_types'; +export { ValidatedDualRange } from 'ui/validated_range'; +export { getFormat } from 'ui/visualize/loader/pipeline_helpers/utilities'; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/get_default_query_language.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/get_default_query_language.js index 61662787c982d..26723da5ab5c9 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/get_default_query_language.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/get_default_query_language.js @@ -17,8 +17,8 @@ * under the License. */ -import chrome from 'ui/chrome'; +import { getUISettings } from '../../services'; export function getDefaultQueryLanguage() { - return chrome.getUiSettingsClient().get('search:queryLanguage'); + return getUISettings().get('search:queryLanguage'); } diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/tick_formatter.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/tick_formatter.js index 0705805312d2f..3ab8e0f6b885e 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/tick_formatter.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/tick_formatter.js @@ -19,11 +19,11 @@ import handlebars from 'handlebars/dist/handlebars'; import { isNumber } from 'lodash'; -import { npStart } from 'ui/new_platform'; import { inputFormats, outputFormats, isDuration } from '../lib/durations'; +import { getFieldFormats } from '../../services'; export const createTickFormatter = (format = '0,0.[00]', template, getConfig = null) => { - const fieldFormats = npStart.plugins.data.fieldFormats; + const fieldFormats = getFieldFormats(); if (!template) template = '{{value}}'; const render = handlebars.compile(template, { knownHelpersOnly: true }); diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/tick_formatter.test.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/tick_formatter.test.js index 76d3cff17343e..e87cba126bb46 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/tick_formatter.test.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/tick_formatter.test.js @@ -17,9 +17,9 @@ * under the License. */ -import { npStart } from 'ui/new_platform'; import { createTickFormatter } from './tick_formatter'; import { getFieldFormatsRegistry } from '../../../../../../test_utils/public/stub_field_formats'; +import { setFieldFormats } from '../../services'; const mockUiSettings = { get: item => { @@ -46,9 +46,7 @@ const mockCore = { }; describe('createTickFormatter(format, template)', () => { - npStart.plugins.data = { - fieldFormats: getFieldFormatsRegistry(mockCore), - }; + setFieldFormats(getFieldFormatsRegistry(mockCore)); test('returns a number with two decimal place by default', () => { const fn = createTickFormatter(); diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/panel_config/gauge.test.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/panel_config/gauge.test.js index 9ec8184dbaebb..d92dafadb68bc 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/panel_config/gauge.test.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/panel_config/gauge.test.js @@ -26,6 +26,10 @@ jest.mock('plugins/data', () => { }; }); +jest.mock('../lib/get_default_query_language', () => ({ + getDefaultQueryLanguage: () => 'kuery', +})); + import { GaugePanelConfig } from './gauge'; describe('GaugePanelConfig', () => { diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor.js index 3dedb67bd1d99..b2dd1813e6d20 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor.js @@ -30,13 +30,11 @@ import { createBrushHandler } from '../lib/create_brush_handler'; import { fetchFields } from '../lib/fetch_fields'; import { extractIndexPatterns } from '../../../../../plugins/vis_type_timeseries/common/extract_index_patterns'; import { esKuery } from '../../../../../plugins/data/public'; - -import { npStart } from 'ui/new_platform'; +import { getSavedObjectsClient, getUISettings, getDataStart, getCoreStart } from '../services'; import { CoreStartContextProvider } from '../contexts/query_input_bar_context'; import { KibanaContextProvider } from '../../../../../plugins/kibana_react/public'; import { Storage } from '../../../../../plugins/kibana_utils/public'; -import { timefilter } from 'ui/timefilter'; const VIS_STATE_DEBOUNCE_DELAY = 200; const APP_NAME = 'VisEditor'; @@ -52,7 +50,7 @@ export class VisEditor extends Component { visFields: props.visFields, extractedIndexPatterns: [''], }; - this.onBrush = createBrushHandler(timefilter); + this.onBrush = createBrushHandler(getDataStart().query.timefilter); this.visDataSubject = new Rx.BehaviorSubject(this.props.visData); this.visData$ = this.visDataSubject.asObservable().pipe(share()); @@ -60,8 +58,8 @@ export class VisEditor extends Component { // core dependencies required by React components downstream. this.coreContext = { appName: APP_NAME, - uiSettings: npStart.core.uiSettings, - savedObjectsClient: npStart.core.savedObjects.client, + uiSettings: getUISettings(), + savedObjectsClient: getSavedObjectsClient(), store: this.localStorage, }; } @@ -175,8 +173,8 @@ export class VisEditor extends Component { services={{ appName: APP_NAME, storage: this.localStorage, - data: npStart.plugins.data, - ...npStart.core, + data: getDataStart(), + ...getCoreStart(), }} >
diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/table/vis.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/table/vis.js index 94f4506cd0172..1fe9358cbfea9 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/table/vis.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/table/vis.js @@ -20,7 +20,6 @@ import _, { isArray, last, get } from 'lodash'; import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { npStart } from 'ui/new_platform'; import { createTickFormatter } from '../../lib/tick_formatter'; import { calculateLabel } from '../../../../../../../plugins/vis_type_timeseries/common/calculate_label'; import { isSortable } from './is_sortable'; @@ -28,6 +27,7 @@ import { EuiToolTip, EuiIcon } from '@elastic/eui'; import { replaceVars } from '../../lib/replace_vars'; import { fieldFormats } from '../../../../../../../plugins/data/public'; import { FormattedMessage } from '@kbn/i18n/react'; +import { getFieldFormats } from '../../../services'; import { METRIC_TYPES } from '../../../../../../../plugins/vis_type_timeseries/common/metric_types'; @@ -49,7 +49,7 @@ export class TableVis extends Component { constructor(props) { super(props); - const fieldFormatsService = npStart.plugins.data.fieldFormats; + const fieldFormatsService = getFieldFormats(); const DateFormat = fieldFormatsService.getType(fieldFormats.FIELD_FORMAT_IDS.DATE); this.dateFormatter = new DateFormat({}, this.props.getConfig); diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/timeseries/vis.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/timeseries/vis.js index 5243f5f92a621..954d3d174bb8c 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/timeseries/vis.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/timeseries/vis.js @@ -22,7 +22,6 @@ import React, { Component } from 'react'; import reactCSS from 'reactcss'; import { startsWith, get, cloneDeep, map } from 'lodash'; -import { toastNotifications } from 'ui/notify'; import { htmlIdGenerator } from '@elastic/eui'; import { ScaleType } from '@elastic/charts'; @@ -36,6 +35,7 @@ import { areFieldsDifferent } from '../../lib/charts'; import { createXaxisFormatter } from '../../lib/create_xaxis_formatter'; import { isBackgroundDark } from '../../../lib/set_is_reversed'; import { STACKED_OPTIONS } from '../../../visualizations/constants'; +import { getCoreStart } from '../../../services'; export class TimeseriesVisualization extends Component { static propTypes = { @@ -108,6 +108,7 @@ export class TimeseriesVisualization extends Component { createTickFormatter(get(model, 'formatter'), get(model, 'value_template'), getConfig); componentDidUpdate() { + const toastNotifications = getCoreStart().notifications.toasts; if ( this.showToastNotification && this.notificationReason !== this.showToastNotification.reason diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/legacy.ts b/src/legacy/core_plugins/vis_type_timeseries/public/legacy.ts index 93b35ee284f18..fb22bbd4146e2 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/legacy.ts +++ b/src/legacy/core_plugins/vis_type_timeseries/public/legacy.ts @@ -32,4 +32,4 @@ const plugins: Readonly = { const pluginInstance = plugin({} as PluginInitializerContext); export const setup = pluginInstance.setup(npSetup.core, plugins); -export const start = pluginInstance.start(npStart.core); +export const start = pluginInstance.start(npStart.core, npStart.plugins); diff --git a/src/legacy/ui/public/modals/index.js b/src/legacy/core_plugins/vis_type_timeseries/public/legacy_imports.ts similarity index 79% rename from src/legacy/ui/public/modals/index.js rename to src/legacy/core_plugins/vis_type_timeseries/public/legacy_imports.ts index d061264345959..401acfc8df766 100644 --- a/src/legacy/ui/public/modals/index.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/legacy_imports.ts @@ -17,8 +17,8 @@ * under the License. */ -import './confirm_modal'; -import './confirm_modal_promise'; - -export { ConfirmationButtonTypes } from './confirm_modal'; -export { ModalOverlay } from './modal_overlay'; +export { PersistedState } from 'ui/persisted_state'; +// @ts-ignore +export { defaultFeedbackMessage } from 'ui/vis/default_feedback_message'; +// @ts-ignore +export { timezoneProvider } from 'ui/vis/lib/timezone'; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/lib/fetch_fields.js b/src/legacy/core_plugins/vis_type_timeseries/public/lib/fetch_fields.js index 68e694f23fa7f..9c64d0da2d88a 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/lib/fetch_fields.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/lib/fetch_fields.js @@ -16,19 +16,16 @@ * specific language governing permissions and limitations * under the License. */ -import { kfetch } from 'ui/kfetch'; -import { toastNotifications } from 'ui/notify'; import { i18n } from '@kbn/i18n'; import { extractIndexPatterns } from '../../../../../plugins/vis_type_timeseries/common/extract_index_patterns'; +import { getCoreStart } from '../services'; export async function fetchFields(indexPatterns = ['*']) { const patterns = Array.isArray(indexPatterns) ? indexPatterns : [indexPatterns]; try { const indexFields = await Promise.all( patterns.map(pattern => { - return kfetch({ - method: 'GET', - pathname: '/api/metrics/fields', + return getCoreStart().http.get('/api/metrics/fields', { query: { index: pattern, }, @@ -43,7 +40,7 @@ export async function fetchFields(indexPatterns = ['*']) { }, {}); return fields; } catch (error) { - toastNotifications.addDanger({ + getCoreStart().notifications.toasts.addDanger({ title: i18n.translate('visTypeTimeseries.fetchFields.loadIndexPatternFieldsErrorMessage', { defaultMessage: 'Unable to load index_pattern fields', }), diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/metrics_fn.ts b/src/legacy/core_plugins/vis_type_timeseries/public/metrics_fn.ts index 225d81b71b8e0..5786399fc7830 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/metrics_fn.ts +++ b/src/legacy/core_plugins/vis_type_timeseries/public/metrics_fn.ts @@ -19,11 +19,11 @@ import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { PersistedState } from 'ui/persisted_state'; import { ExpressionFunction, KibanaContext, Render } from '../../../../plugins/expressions/public'; // @ts-ignore import { metricsRequestHandler } from './request_handler'; +import { PersistedState } from './legacy_imports'; const name = 'tsvb'; type Context = KibanaContext | null; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/metrics_type.ts b/src/legacy/core_plugins/vis_type_timeseries/public/metrics_type.ts index 22d2b3b10e566..01750ee0c448d 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/metrics_type.ts +++ b/src/legacy/core_plugins/vis_type_timeseries/public/metrics_type.ts @@ -18,8 +18,7 @@ */ import { i18n } from '@kbn/i18n'; -// @ts-ignore -import { defaultFeedbackMessage } from 'ui/vis/default_feedback_message'; +import { defaultFeedbackMessage } from './legacy_imports'; // @ts-ignore import { metricsRequestHandler } from './request_handler'; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/plugin.ts b/src/legacy/core_plugins/vis_type_timeseries/public/plugin.ts index 4d1222d6f5a87..38a9c68487854 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_timeseries/public/plugin.ts @@ -16,29 +16,31 @@ * specific language governing permissions and limitations * under the License. */ -import { - PluginInitializerContext, - CoreSetup, - CoreStart, - Plugin, - SavedObjectsClientContract, - IUiSettingsClient, -} from '../../../../core/public'; +import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'kibana/public'; import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; import { VisualizationsSetup } from '../../visualizations/public'; import { createMetricsFn } from './metrics_fn'; import { metricsVisDefinition } from './metrics_type'; -import { setSavedObjectsClient, setUISettings, setI18n } from './services'; +import { + setSavedObjectsClient, + setUISettings, + setI18n, + setFieldFormats, + setCoreStart, + setDataStart, +} from './services'; +import { DataPublicPluginStart } from '../../../../plugins/data/public'; /** @internal */ export interface MetricsPluginSetupDependencies { expressions: ReturnType; visualizations: VisualizationsSetup; } -export interface MetricsVisualizationDependencies { - uiSettings: IUiSettingsClient; - savedObjectsClient: SavedObjectsClientContract; + +/** @internal */ +export interface MetricsPluginStartDependencies { + data: DataPublicPluginStart; } /** @internal */ @@ -58,9 +60,11 @@ export class MetricsPlugin implements Plugin, void> { visualizations.types.createReactVisualization(metricsVisDefinition); } - public start(core: CoreStart) { - // nothing to do here yet + public start(core: CoreStart, { data }: MetricsPluginStartDependencies) { setSavedObjectsClient(core.savedObjects); setI18n(core.i18n); + setFieldFormats(data.fieldFormats); + setDataStart(data); + setCoreStart(core); } } diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/request_handler.js b/src/legacy/core_plugins/vis_type_timeseries/public/request_handler.js index 84f62612aa974..032ef335314d9 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/request_handler.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/request_handler.js @@ -18,10 +18,8 @@ */ import { validateInterval } from './lib/validate_interval'; -import { timezoneProvider } from 'ui/vis/lib/timezone'; -import { timefilter } from 'ui/timefilter'; -import { kfetch } from 'ui/kfetch'; -import { getUISettings } from './services'; +import { timezoneProvider } from './legacy_imports'; +import { getUISettings, getDataStart, getCoreStart } from './services'; export const metricsRequestHandler = async ({ uiState, @@ -34,7 +32,7 @@ export const metricsRequestHandler = async ({ const config = getUISettings(); const timezone = timezoneProvider(config)(); const uiStateObj = uiState.get(visParams.type, {}); - const parsedTimeRange = timefilter.calculateBounds(timeRange); + const parsedTimeRange = getDataStart().query.timefilter.timefilter.calculateBounds(timeRange); const scaledDataFormat = config.get('dateFormat:scaled'); const dateFormat = config.get('dateFormat'); @@ -44,9 +42,7 @@ export const metricsRequestHandler = async ({ validateInterval(parsedTimeRange, visParams, maxBuckets); - const resp = await kfetch({ - pathname: '/api/metrics/vis/data', - method: 'POST', + const resp = await getCoreStart().http.post('/api/metrics/vis/data', { body: JSON.stringify({ timerange: { timezone, diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/services.ts b/src/legacy/core_plugins/vis_type_timeseries/public/services.ts index af04578b8e27f..c16ed47f54874 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/services.ts +++ b/src/legacy/core_plugins/vis_type_timeseries/public/services.ts @@ -17,13 +17,22 @@ * under the License. */ -import { I18nStart, SavedObjectsStart, IUiSettingsClient } from 'src/core/public'; +import { I18nStart, SavedObjectsStart, IUiSettingsClient, CoreStart } from 'src/core/public'; import { createGetterSetter } from '../../../../plugins/kibana_utils/public'; +import { DataPublicPluginStart, FieldFormatsStart } from '../../../../plugins/data/public'; export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); +export const [getFieldFormats, setFieldFormats] = createGetterSetter( + 'FieldFormats' +); + export const [getSavedObjectsClient, setSavedObjectsClient] = createGetterSetter( 'SavedObjectsClient' ); +export const [getCoreStart, setCoreStart] = createGetterSetter('CoreStart'); + +export const [getDataStart, setDataStart] = createGetterSetter('DataStart'); + export const [getI18n, setI18n] = createGetterSetter('I18n'); diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/index.js b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/index.js index bcd0b6314cef1..986111b462b35 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/index.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/index.js @@ -33,9 +33,9 @@ import { } from '@elastic/charts'; import { EuiIcon } from '@elastic/eui'; -import { timezoneProvider } from 'ui/vis/lib/timezone'; +import { timezoneProvider } from '../../../legacy_imports'; import { eventBus, ACTIVE_CURSOR } from '../../lib/active_cursor'; -import chrome from 'ui/chrome'; +import { getUISettings } from '../../../services'; import { GRID_LINE_CONFIG, ICON_TYPES_MAP, STACKED_OPTIONS } from '../../constants'; import { AreaSeriesDecorator } from './decorators/area_decorator'; import { BarSeriesDecorator } from './decorators/bar_decorator'; @@ -85,7 +85,7 @@ export const TimeSeries = ({ }, []); // eslint-disable-line const tooltipFormatter = decorateFormatter(xAxisFormatter); - const uiSettings = chrome.getUiSettingsClient(); + const uiSettings = getUISettings(); const timeZone = timezoneProvider(uiSettings)(); const hasBarChart = series.some(({ bars }) => bars.show); diff --git a/src/legacy/core_plugins/vis_type_vega/public/__mocks__/services.ts b/src/legacy/core_plugins/vis_type_vega/public/__mocks__/services.ts new file mode 100644 index 0000000000000..64a9aaaf3b7a6 --- /dev/null +++ b/src/legacy/core_plugins/vis_type_vega/public/__mocks__/services.ts @@ -0,0 +1,55 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { createGetterSetter } from '../../../../../plugins/kibana_utils/common'; +import { DataPublicPluginStart } from '../../../../../plugins/data/public'; +import { IUiSettingsClient, NotificationsStart, SavedObjectsStart } from 'kibana/public'; +import { dataPluginMock } from '../../../../../plugins/data/public/mocks'; +import { coreMock } from '../../../../../core/public/mocks'; + +export const [getData, setData] = createGetterSetter('Data'); +setData(dataPluginMock.createStartContract()); + +export const [getNotifications, setNotifications] = createGetterSetter( + 'Notifications' +); +setNotifications(coreMock.createStart().notifications); + +export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); +setUISettings(coreMock.createStart().uiSettings); + +export const [getSavedObjects, setSavedObjects] = createGetterSetter( + 'SavedObjects' +); +setSavedObjects(coreMock.createStart().savedObjects); + +export const [getInjectedVars, setInjectedVars] = createGetterSetter<{ + esShardTimeout: number; + enableExternalUrls: boolean; + emsTileLayerId: unknown; +}>('InjectedVars'); +setInjectedVars({ + emsTileLayerId: {}, + enableExternalUrls: true, + esShardTimeout: 10000, +}); + +export const getEsShardTimeout = () => getInjectedVars().esShardTimeout; +export const getEnableExternalUrls = () => getInjectedVars().enableExternalUrls; +export const getEmsTileLayerId = () => getInjectedVars().emsTileLayerId; diff --git a/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js b/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js index 6c9eb86a9d2c0..b2ad45b5d7b6d 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js +++ b/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js @@ -43,6 +43,9 @@ import { SearchCache } from '../data_model/search_cache'; import { setup as visualizationsSetup } from '../../../visualizations/public/np_ready/public/legacy'; import { createVegaTypeDefinition } from '../vega_type'; +// TODO This is an integration test and thus requires a running platform. When moving to the new platform, +// this test has to be migrated to the newly created integration test environment. +// eslint-disable-next-line @kbn/eslint/no-restricted-paths import { npStart } from 'ui/new_platform'; const THRESHOLD = 0.1; diff --git a/src/legacy/core_plugins/vis_type_vega/public/components/vega_vis_editor.tsx b/src/legacy/core_plugins/vis_type_vega/public/components/vega_vis_editor.tsx index 18d48aea5d39a..707a6830b5ba4 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/components/vega_vis_editor.tsx +++ b/src/legacy/core_plugins/vis_type_vega/public/components/vega_vis_editor.tsx @@ -24,7 +24,7 @@ import compactStringify from 'json-stringify-pretty-compact'; import hjson from 'hjson'; import { i18n } from '@kbn/i18n'; -import { toastNotifications } from 'ui/notify'; +import { getNotifications } from '../services'; import { VisParams } from '../vega_fn'; import { VegaHelpMenu } from './vega_help_menu'; import { VegaActionsMenu } from './vega_actions_menu'; @@ -50,7 +50,7 @@ function format(value: string, stringify: typeof compactStringify, options?: any return stringify(spec, options); } catch (err) { // This is a common case - user tries to format an invalid HJSON text - toastNotifications.addError(err, { + getNotifications().toasts.addError(err, { title: i18n.translate('visTypeVega.editor.formatError', { defaultMessage: 'Error formatting spec', }), diff --git a/src/legacy/core_plugins/vis_type_vega/public/data_model/es_query_parser.js b/src/legacy/core_plugins/vis_type_vega/public/data_model/es_query_parser.js index 2f25f70610a81..7c239800483f0 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/data_model/es_query_parser.js +++ b/src/legacy/core_plugins/vis_type_vega/public/data_model/es_query_parser.js @@ -21,7 +21,7 @@ import _ from 'lodash'; import moment from 'moment'; import { i18n } from '@kbn/i18n'; -import { getEsShardTimeout } from '../helpers'; +import { getEsShardTimeout } from '../services'; const TIMEFILTER = '%timefilter%'; const AUTOINTERVAL = '%autointerval%'; diff --git a/src/legacy/core_plugins/vis_type_vega/public/data_model/es_query_parser.test.js b/src/legacy/core_plugins/vis_type_vega/public/data_model/es_query_parser.test.js index 691e5e8944241..c519da33ab1c9 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/data_model/es_query_parser.test.js +++ b/src/legacy/core_plugins/vis_type_vega/public/data_model/es_query_parser.test.js @@ -21,10 +21,6 @@ import { cloneDeep } from 'lodash'; import moment from 'moment'; import { EsQueryParser } from './es_query_parser'; -jest.mock('../helpers', () => ({ - getEsShardTimeout: jest.fn(() => '10000'), -})); - const second = 1000; const minute = 60 * second; const hour = 60 * minute; @@ -47,6 +43,8 @@ function create(min, max, dashboardCtx) { return inst; } +jest.mock('../services'); + describe(`EsQueryParser time`, () => { test(`roundInterval(4s)`, () => { expect(EsQueryParser._roundInterval(4 * second)).toBe(`1s`); diff --git a/src/legacy/core_plugins/vis_type_vega/public/data_model/search_cache.test.js b/src/legacy/core_plugins/vis_type_vega/public/data_model/search_cache.test.js index 0ec018f46c02b..92f80545ce1b5 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/data_model/search_cache.test.js +++ b/src/legacy/core_plugins/vis_type_vega/public/data_model/search_cache.test.js @@ -18,6 +18,7 @@ */ import { SearchCache } from './search_cache'; +jest.mock('../services'); describe(`SearchCache`, () => { class FauxEs { diff --git a/src/legacy/core_plugins/vis_type_vega/public/data_model/time_cache.test.js b/src/legacy/core_plugins/vis_type_vega/public/data_model/time_cache.test.js index b76709ea2c934..074744a0bda5e 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/data_model/time_cache.test.js +++ b/src/legacy/core_plugins/vis_type_vega/public/data_model/time_cache.test.js @@ -18,6 +18,7 @@ */ import { TimeCache } from './time_cache'; +jest.mock('../services'); describe(`TimeCache`, () => { class FauxTimefilter { diff --git a/src/legacy/core_plugins/vis_type_vega/public/data_model/vega_parser.test.js b/src/legacy/core_plugins/vis_type_vega/public/data_model/vega_parser.test.js index 1bc8b1f90daab..78d1cad874311 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/data_model/vega_parser.test.js +++ b/src/legacy/core_plugins/vis_type_vega/public/data_model/vega_parser.test.js @@ -20,6 +20,7 @@ import { cloneDeep } from 'lodash'; import { VegaParser } from './vega_parser'; import { bypassExternalUrlCheck } from '../vega_view/vega_base_view'; +jest.mock('../services'); describe(`VegaParser._setDefaultValue`, () => { function check(spec, expected, ...params) { diff --git a/src/legacy/core_plugins/vis_type_vega/public/helpers/index.js b/src/legacy/core_plugins/vis_type_vega/public/helpers/index.js deleted file mode 100644 index e9d6eb21fd3c7..0000000000000 --- a/src/legacy/core_plugins/vis_type_vega/public/helpers/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export * from './vega_config_provider'; diff --git a/src/legacy/core_plugins/vis_type_vega/public/helpers/vega_config_provider.js b/src/legacy/core_plugins/vis_type_vega/public/legacy_imports.ts similarity index 77% rename from src/legacy/core_plugins/vis_type_vega/public/helpers/vega_config_provider.js rename to src/legacy/core_plugins/vis_type_vega/public/legacy_imports.ts index cc41b18479f93..9e1067ed9099a 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/helpers/vega_config_provider.js +++ b/src/legacy/core_plugins/vis_type_vega/public/legacy_imports.ts @@ -17,7 +17,9 @@ * under the License. */ -import chrome from 'ui/chrome'; - -export const getEsShardTimeout = () => chrome.getInjected('esShardTimeout'); -export const getEnableExternalUrls = () => chrome.getInjected('enableExternalUrls'); +// @ts-ignore +export { defaultFeedbackMessage } from 'ui/vis/default_feedback_message'; +// @ts-ignore +export { KibanaMapLayer } from 'ui/vis/map/kibana_map_layer'; +// @ts-ignore +export { KibanaMap } from 'ui/vis/map/kibana_map'; diff --git a/src/legacy/core_plugins/vis_type_vega/public/plugin.ts b/src/legacy/core_plugins/vis_type_vega/public/plugin.ts index 75444a4a4f8e4..9721de9848cfc 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_vega/public/plugin.ts @@ -21,7 +21,13 @@ import { LegacyDependenciesPlugin, LegacyDependenciesPluginSetup } from './shim' import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; import { Plugin as DataPublicPlugin } from '../../../../plugins/data/public'; import { VisualizationsSetup } from '../../visualizations/public'; -import { setNotifications, setData, setSavedObjects } from './services'; +import { + setNotifications, + setData, + setSavedObjects, + setInjectedVars, + setUISettings, +} from './services'; import { createVegaFn } from './vega_fn'; import { createVegaTypeDefinition } from './vega_type'; @@ -59,6 +65,13 @@ export class VegaPlugin implements Plugin, void> { core: CoreSetup, { data, expressions, visualizations, __LEGACY }: VegaPluginSetupDependencies ) { + setInjectedVars({ + esShardTimeout: core.injectedMetadata.getInjectedVar('esShardTimeout') as number, + enableExternalUrls: core.injectedMetadata.getInjectedVar('enableExternalUrls') as boolean, + emsTileLayerId: core.injectedMetadata.getInjectedVar('emsTileLayerId', true), + }); + setUISettings(core.uiSettings); + const visualizationDependencies: Readonly = { core, plugins: { diff --git a/src/legacy/core_plugins/vis_type_vega/public/services.ts b/src/legacy/core_plugins/vis_type_vega/public/services.ts index 94723f1a378d2..88e0e0098bf8c 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/services.ts +++ b/src/legacy/core_plugins/vis_type_vega/public/services.ts @@ -21,6 +21,7 @@ import { SavedObjectsStart } from 'kibana/public'; import { NotificationsStart } from 'src/core/public'; import { DataPublicPluginStart } from '../../../../plugins/data/public'; import { createGetterSetter } from '../../../../plugins/kibana_utils/public'; +import { IUiSettingsClient } from '../../../../core/public'; export const [getData, setData] = createGetterSetter('Data'); @@ -28,6 +29,18 @@ export const [getNotifications, setNotifications] = createGetterSetter('UISettings'); + export const [getSavedObjects, setSavedObjects] = createGetterSetter( 'SavedObjects' ); + +export const [getInjectedVars, setInjectedVars] = createGetterSetter<{ + esShardTimeout: number; + enableExternalUrls: boolean; + emsTileLayerId: unknown; +}>('InjectedVars'); + +export const getEsShardTimeout = () => getInjectedVars().esShardTimeout; +export const getEnableExternalUrls = () => getInjectedVars().enableExternalUrls; +export const getEmsTileLayerId = () => getInjectedVars().emsTileLayerId; diff --git a/src/legacy/core_plugins/vis_type_vega/public/shim/legacy_dependencies_plugin.ts b/src/legacy/core_plugins/vis_type_vega/public/shim/legacy_dependencies_plugin.ts index 5cf65d62a6aed..8925f76cffa43 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/shim/legacy_dependencies_plugin.ts +++ b/src/legacy/core_plugins/vis_type_vega/public/shim/legacy_dependencies_plugin.ts @@ -17,7 +17,10 @@ * under the License. */ +// TODO remove this file as soon as serviceSettings is exposed in the new platform +// eslint-disable-next-line @kbn/eslint/no-restricted-paths import chrome from 'ui/chrome'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths import 'ui/vis/map/service_settings'; import { CoreStart, Plugin } from 'kibana/public'; diff --git a/src/legacy/core_plugins/vis_type_vega/public/vega_type.ts b/src/legacy/core_plugins/vis_type_vega/public/vega_type.ts index a7ca0dd3bb349..1d4655b4d525f 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/vega_type.ts +++ b/src/legacy/core_plugins/vis_type_vega/public/vega_type.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; // @ts-ignore -import { defaultFeedbackMessage } from 'ui/vis/default_feedback_message'; +import { defaultFeedbackMessage } from './legacy_imports'; import { Status } from '../../visualizations/public'; import { DefaultEditorSize } from '../../vis_default_editor/public'; import { VegaVisualizationDependencies } from './plugin'; diff --git a/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_base_view.js b/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_base_view.js index 9d6adfd11aedd..a6c17547d058e 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_base_view.js +++ b/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_base_view.js @@ -17,7 +17,6 @@ * under the License. */ -import chrome from 'ui/chrome'; import $ from 'jquery'; import moment from 'moment'; import dateMath from '@elastic/datemath'; @@ -29,7 +28,7 @@ import { i18n } from '@kbn/i18n'; import { TooltipHandler } from './vega_tooltip'; import { esFilters } from '../../../../../plugins/data/public'; -import { getEnableExternalUrls } from '../helpers/vega_config_provider'; +import { getEnableExternalUrls } from '../services'; vega.scheme('elastic', VISUALIZATION_COLORS); @@ -279,20 +278,14 @@ export class VegaBaseView { * @param {string} [index] as defined in Kibana, or default if missing */ async removeFilterHandler(query, index) { - const $injector = await chrome.dangerouslyGetActiveInjector(); const indexId = await this._findIndex(index); const filter = esFilters.buildQueryFilter(query, indexId); - // This is a workaround for the https://github.com/elastic/kibana/issues/18863 - // Once fixed, replace with a direct call (no await is needed because its not async) - // this._queryfilter.removeFilter(filter); - $injector.get('$rootScope').$evalAsync(() => { - try { - this._filterManager.removeFilter(filter); - } catch (err) { - this.onError(err); - } - }); + try { + this._filterManager.removeFilter(filter); + } catch (err) { + this.onError(err); + } } removeAllFiltersHandler() { diff --git a/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_map_layer.js b/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_map_layer.js index 2794de6946ba0..38540e9f218fb 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_map_layer.js +++ b/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_map_layer.js @@ -19,7 +19,7 @@ import L from 'leaflet'; import 'leaflet-vega'; -import { KibanaMapLayer } from 'ui/vis/map/kibana_map_layer'; +import { KibanaMapLayer } from '../legacy_imports'; export class VegaMapLayer extends KibanaMapLayer { constructor(spec, options) { diff --git a/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_map_view.js b/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_map_view.js index 82bcd6626789f..487c90d01ada3 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_map_view.js +++ b/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_map_view.js @@ -17,12 +17,12 @@ * under the License. */ -import { KibanaMap } from 'ui/vis/map/kibana_map'; import * as vega from 'vega-lib'; +import { i18n } from '@kbn/i18n'; import { VegaBaseView } from './vega_base_view'; import { VegaMapLayer } from './vega_map_layer'; -import { i18n } from '@kbn/i18n'; -import chrome from 'ui/chrome'; +import { KibanaMap } from '../legacy_imports'; +import { getEmsTileLayerId, getUISettings } from '../services'; export class VegaMapView extends VegaBaseView { async _initViewCustomizations() { @@ -35,10 +35,10 @@ export class VegaMapView extends VegaBaseView { const tmsServices = await this._serviceSettings.getTMSServices(); // In some cases, Vega may be initialized twice, e.g. after awaiting... if (!this._$container) return; - const emsTileLayerId = chrome.getInjected('emsTileLayerId', true); + const emsTileLayerId = getEmsTileLayerId(); const mapStyle = mapConfig.mapStyle === 'default' ? emsTileLayerId.bright : mapConfig.mapStyle; - const isDarkMode = chrome.getUiSettingsClient().get('theme:darkMode'); + const isDarkMode = getUISettings().get('theme:darkMode'); baseMapOpts = tmsServices.find(s => s.id === mapStyle); baseMapOpts = { ...baseMapOpts, diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/response_handlers.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/response_handlers.js index 642a032d8b9c2..3574fb232883d 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/response_handlers.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/response_handlers.js @@ -21,6 +21,7 @@ import sinon from 'sinon'; import ngMock from 'ng_mock'; import expect from '@kbn/expect'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths import { aggResponseIndex } from 'ui/agg_response'; import { vislibSeriesResponseHandler } from '../response_handler'; diff --git a/src/legacy/ui/public/angular-bootstrap/bindHtml/bindHtml.js b/src/legacy/ui/public/angular-bootstrap/bindHtml/bindHtml.js deleted file mode 100755 index bafc738268626..0000000000000 --- a/src/legacy/ui/public/angular-bootstrap/bindHtml/bindHtml.js +++ /dev/null @@ -1,10 +0,0 @@ -angular.module('ui.bootstrap.bindHtml', []) - - .directive('bindHtmlUnsafe', function () { - return function (scope, element, attr) { - element.addClass('ng-binding').data('$binding', attr.bindHtmlUnsafe); - scope.$watch(attr.bindHtmlUnsafe, function bindHtmlUnsafeWatchAction(value) { - element.html(value || ''); - }); - }; - }); diff --git a/src/legacy/ui/public/angular-bootstrap/index.js b/src/legacy/ui/public/angular-bootstrap/index.js deleted file mode 100644 index f574345af48ab..0000000000000 --- a/src/legacy/ui/public/angular-bootstrap/index.js +++ /dev/null @@ -1,47 +0,0 @@ - -/* eslint-disable */ - -/** - * TODO: Write custom components that address our needs to directly and deprecate these Bootstrap components. - */ - -import 'angular'; - -import { uiModules } from 'ui/modules'; - -uiModules.get('kibana', [ - 'ui.bootstrap', -]); - -/* - * angular-ui-bootstrap - * http://angular-ui.github.io/bootstrap/ - - * Version: 0.12.1 - 2015-02-20 - * License: MIT - */ -angular.module('ui.bootstrap', [ - 'ui.bootstrap.tpls', - 'ui.bootstrap.bindHtml', - 'ui.bootstrap.tooltip', -]); - -angular.module('ui.bootstrap.tpls', [ - 'template/tooltip/tooltip-html-unsafe-popup.html', - 'template/tooltip/tooltip-popup.html', -]); - -import './bindHtml/bindHtml'; -import './tooltip/tooltip'; - -import tooltipUnsafePopup from './tooltip/tooltip-html-unsafe-popup.html'; - -angular.module('template/tooltip/tooltip-html-unsafe-popup.html', []).run(['$templateCache', function($templateCache) { - $templateCache.put('template/tooltip/tooltip-html-unsafe-popup.html', tooltipUnsafePopup); -}]); - -import tooltipPopup from './tooltip/tooltip-popup.html'; - -angular.module('template/tooltip/tooltip-popup.html', []).run(['$templateCache', function($templateCache) { - $templateCache.put('template/tooltip/tooltip-popup.html', tooltipPopup); -}]); diff --git a/src/legacy/ui/public/angular-bootstrap/tooltip/position.js b/src/legacy/ui/public/angular-bootstrap/tooltip/position.js deleted file mode 100755 index 3444c33449152..0000000000000 --- a/src/legacy/ui/public/angular-bootstrap/tooltip/position.js +++ /dev/null @@ -1,152 +0,0 @@ -angular.module('ui.bootstrap.position', []) - -/** - * A set of utility methods that can be use to retrieve position of DOM elements. - * It is meant to be used where we need to absolute-position DOM elements in - * relation to other, existing elements (this is the case for tooltips, popovers, - * typeahead suggestions etc.). - */ - .factory('$position', ['$document', '$window', function ($document, $window) { - - function getStyle(el, cssprop) { - if (el.currentStyle) { //IE - return el.currentStyle[cssprop]; - } else if ($window.getComputedStyle) { - return $window.getComputedStyle(el)[cssprop]; - } - // finally try and get inline style - return el.style[cssprop]; - } - - /** - * Checks if a given element is statically positioned - * @param element - raw DOM element - */ - function isStaticPositioned(element) { - return (getStyle(element, 'position') || 'static' ) === 'static'; - } - - /** - * returns the closest, non-statically positioned parentOffset of a given element - * @param element - */ - var parentOffsetEl = function (element) { - var docDomEl = $document[0]; - var offsetParent = element.offsetParent || docDomEl; - while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) { - offsetParent = offsetParent.offsetParent; - } - return offsetParent || docDomEl; - }; - - return { - /** - * Provides read-only equivalent of jQuery's position function: - * http://api.jquery.com/position/ - */ - position: function (element) { - var elBCR = this.offset(element); - var offsetParentBCR = { top: 0, left: 0 }; - var offsetParentEl = parentOffsetEl(element[0]); - if (offsetParentEl != $document[0]) { - offsetParentBCR = this.offset(angular.element(offsetParentEl)); - offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop; - offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft; - } - - var boundingClientRect = element[0].getBoundingClientRect(); - return { - width: boundingClientRect.width || element.prop('offsetWidth'), - height: boundingClientRect.height || element.prop('offsetHeight'), - top: elBCR.top - offsetParentBCR.top, - left: elBCR.left - offsetParentBCR.left - }; - }, - - /** - * Provides read-only equivalent of jQuery's offset function: - * http://api.jquery.com/offset/ - */ - offset: function (element) { - var boundingClientRect = element[0].getBoundingClientRect(); - return { - width: boundingClientRect.width || element.prop('offsetWidth'), - height: boundingClientRect.height || element.prop('offsetHeight'), - top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop), - left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft) - }; - }, - - /** - * Provides coordinates for the targetEl in relation to hostEl - */ - positionElements: function (hostEl, targetEl, positionStr, appendToBody) { - - var positionStrParts = positionStr.split('-'); - var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center'; - - var hostElPos, - targetElWidth, - targetElHeight, - targetElPos; - - hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl); - - targetElWidth = targetEl.prop('offsetWidth'); - targetElHeight = targetEl.prop('offsetHeight'); - - var shiftWidth = { - center: function () { - return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2; - }, - left: function () { - return hostElPos.left; - }, - right: function () { - return hostElPos.left + hostElPos.width; - } - }; - - var shiftHeight = { - center: function () { - return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2; - }, - top: function () { - return hostElPos.top; - }, - bottom: function () { - return hostElPos.top + hostElPos.height; - } - }; - - switch (pos0) { - case 'right': - targetElPos = { - top: shiftHeight[pos1](), - left: shiftWidth[pos0]() - }; - break; - case 'left': - targetElPos = { - top: shiftHeight[pos1](), - left: hostElPos.left - targetElWidth - }; - break; - case 'bottom': - targetElPos = { - top: shiftHeight[pos0](), - left: shiftWidth[pos1]() - }; - break; - default: - targetElPos = { - top: hostElPos.top - targetElHeight, - left: shiftWidth[pos1]() - }; - break; - } - - return targetElPos; - } - }; - }]); diff --git a/src/legacy/ui/public/angular-bootstrap/tooltip/tooltip.js b/src/legacy/ui/public/angular-bootstrap/tooltip/tooltip.js deleted file mode 100755 index b59b2922d8089..0000000000000 --- a/src/legacy/ui/public/angular-bootstrap/tooltip/tooltip.js +++ /dev/null @@ -1,374 +0,0 @@ -import './position'; - -/** - * The following features are still outstanding: animation as a - * function, placement as a function, inside, support for more triggers than - * just mouse enter/leave, html tooltips, and selector delegation. - */ -angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] ) - -/** - * The $tooltip service creates tooltip- and popover-like directives as well as - * houses global options for them. - */ -.provider( '$tooltip', function () { - // The default options tooltip and popover. - var defaultOptions = { - placement: 'top', - animation: true, - popupDelay: 0 - }; - - // Default hide triggers for each show trigger - var triggerMap = { - 'mouseenter': 'mouseleave', - 'click': 'click', - 'focus': 'blur' - }; - - // The options specified to the provider globally. - var globalOptions = {}; - - /** - * `options({})` allows global configuration of all tooltips in the - * application. - * - * var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) { - * // place tooltips left instead of top by default - * $tooltipProvider.options( { placement: 'left' } ); - * }); - */ - this.options = function( value ) { - angular.extend( globalOptions, value ); - }; - - /** - * This allows you to extend the set of trigger mappings available. E.g.: - * - * $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' ); - */ - this.setTriggers = function setTriggers ( triggers ) { - angular.extend( triggerMap, triggers ); - }; - - /** - * This is a helper function for translating camel-case to snake-case. - */ - function snake_case(name){ - var regexp = /[A-Z]/g; - var separator = '-'; - return name.replace(regexp, function(letter, pos) { - return (pos ? separator : '') + letter.toLowerCase(); - }); - } - - /** - * Returns the actual instance of the $tooltip service. - * TODO support multiple triggers - */ - this.$get = [ '$window', '$compile', '$timeout', '$document', '$position', '$interpolate', function ( $window, $compile, $timeout, $document, $position, $interpolate ) { - return function $tooltip ( type, prefix, defaultTriggerShow ) { - var options = angular.extend( {}, defaultOptions, globalOptions ); - - /** - * Returns an object of show and hide triggers. - * - * If a trigger is supplied, - * it is used to show the tooltip; otherwise, it will use the `trigger` - * option passed to the `$tooltipProvider.options` method; else it will - * default to the trigger supplied to this directive factory. - * - * The hide trigger is based on the show trigger. If the `trigger` option - * was passed to the `$tooltipProvider.options` method, it will use the - * mapped trigger from `triggerMap` or the passed trigger if the map is - * undefined; otherwise, it uses the `triggerMap` value of the show - * trigger; else it will just use the show trigger. - */ - function getTriggers ( trigger ) { - var show = trigger || options.trigger || defaultTriggerShow; - var hide = triggerMap[show] || show; - return { - show: show, - hide: hide - }; - } - - var directiveName = snake_case( type ); - - var startSym = $interpolate.startSymbol(); - var endSym = $interpolate.endSymbol(); - var template = - '
'+ - '
'; - - return { - restrict: 'EA', - compile: function (tElem, tAttrs) { - var tooltipLinker = $compile( template ); - - return function link ( scope, element, attrs ) { - var tooltip; - var tooltipLinkedScope; - var transitionTimeout; - var popupTimeout; - var appendToBody = angular.isDefined( options.appendToBody ) ? options.appendToBody : false; - var triggers = getTriggers( undefined ); - var hasEnableExp = angular.isDefined(attrs[prefix+'Enable']); - var ttScope = scope.$new(true); - - var positionTooltip = function () { - - var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody); - ttPosition.top += 'px'; - ttPosition.left += 'px'; - - // Now set the calculated positioning. - tooltip.css( ttPosition ); - }; - - // By default, the tooltip is not open. - // TODO add ability to start tooltip opened - ttScope.isOpen = false; - - function toggleTooltipBind () { - if ( ! ttScope.isOpen ) { - showTooltipBind(); - } else { - hideTooltipBind(); - } - } - - // Show the tooltip with delay if specified, otherwise show it immediately - function showTooltipBind() { - if(hasEnableExp && !scope.$eval(attrs[prefix+'Enable'])) { - return; - } - - prepareTooltip(); - - if ( ttScope.popupDelay ) { - // Do nothing if the tooltip was already scheduled to pop-up. - // This happens if show is triggered multiple times before any hide is triggered. - if (!popupTimeout) { - popupTimeout = $timeout( show, ttScope.popupDelay, false ); - popupTimeout - .then(reposition => reposition()) - .catch((error) => { - // if the timeout is canceled then the string `canceled` is thrown. To prevent - // this from triggering an 'unhandled promise rejection' in angular 1.5+ the - // $timeout service explicitly tells $q that the promise it generated is "handled" - // but that does not include down chain promises like the one created by calling - // `popupTimeout.then()`. Because of this we need to ignore the "canceled" string - // and only propagate real errors - if (error !== 'canceled') { - throw error - } - }); - } - } else { - show()(); - } - } - - function hideTooltipBind () { - scope.$evalAsync(function () { - hide(); - }); - } - - // Show the tooltip popup element. - function show() { - - popupTimeout = null; - - // If there is a pending remove transition, we must cancel it, lest the - // tooltip be mysteriously removed. - if ( transitionTimeout ) { - $timeout.cancel( transitionTimeout ); - transitionTimeout = null; - } - - // Don't show empty tooltips. - if ( ! ttScope.content ) { - return angular.noop; - } - - createTooltip(); - - // Set the initial positioning. - tooltip.css({ top: 0, left: 0, display: 'block' }); - ttScope.$digest(); - - positionTooltip(); - - // And show the tooltip. - ttScope.isOpen = true; - ttScope.$digest(); // digest required as $apply is not called - - // Return positioning function as promise callback for correct - // positioning after draw. - return positionTooltip; - } - - // Hide the tooltip popup element. - function hide() { - // First things first: we don't show it anymore. - ttScope.isOpen = false; - - //if tooltip is going to be shown after delay, we must cancel this - $timeout.cancel( popupTimeout ); - popupTimeout = null; - - // And now we remove it from the DOM. However, if we have animation, we - // need to wait for it to expire beforehand. - // FIXME: this is a placeholder for a port of the transitions library. - if ( ttScope.animation ) { - if (!transitionTimeout) { - transitionTimeout = $timeout(removeTooltip, 500); - } - } else { - removeTooltip(); - } - } - - function createTooltip() { - // There can only be one tooltip element per directive shown at once. - if (tooltip) { - removeTooltip(); - } - tooltipLinkedScope = ttScope.$new(); - tooltip = tooltipLinker(tooltipLinkedScope, function (tooltip) { - if ( appendToBody ) { - $document.find( 'body' ).append( tooltip ); - } else { - element.after( tooltip ); - } - }); - } - - function removeTooltip() { - transitionTimeout = null; - if (tooltip) { - tooltip.remove(); - tooltip = null; - } - if (tooltipLinkedScope) { - tooltipLinkedScope.$destroy(); - tooltipLinkedScope = null; - } - } - - function prepareTooltip() { - prepPlacement(); - prepPopupDelay(); - } - - /** - * Observe the relevant attributes. - */ - attrs.$observe( type, function ( val ) { - ttScope.content = val; - - if (!val && ttScope.isOpen ) { - hide(); - } - }); - - attrs.$observe( prefix+'Title', function ( val ) { - ttScope.title = val; - }); - - function prepPlacement() { - var val = attrs[ prefix + 'Placement' ]; - ttScope.placement = angular.isDefined( val ) ? val : options.placement; - } - - function prepPopupDelay() { - var val = attrs[ prefix + 'PopupDelay' ]; - var delay = parseInt( val, 10 ); - ttScope.popupDelay = ! isNaN(delay) ? delay : options.popupDelay; - } - - var unregisterTriggers = function () { - element.unbind(triggers.show, showTooltipBind); - element.unbind(triggers.hide, hideTooltipBind); - }; - - function prepTriggers() { - var val = attrs[ prefix + 'Trigger' ]; - unregisterTriggers(); - - triggers = getTriggers( val ); - - if ( triggers.show === triggers.hide ) { - element.bind( triggers.show, toggleTooltipBind ); - } else { - element.bind( triggers.show, showTooltipBind ); - element.bind( triggers.hide, hideTooltipBind ); - } - } - prepTriggers(); - - var animation = scope.$eval(attrs[prefix + 'Animation']); - ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation; - - var appendToBodyVal = scope.$eval(attrs[prefix + 'AppendToBody']); - appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody; - - // if a tooltip is attached to we need to remove it on - // location change as its parent scope will probably not be destroyed - // by the change. - if ( appendToBody ) { - scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess () { - if ( ttScope.isOpen ) { - hide(); - } - }); - } - - // Make sure tooltip is destroyed and removed. - scope.$on('$destroy', function onDestroyTooltip() { - $timeout.cancel( transitionTimeout ); - $timeout.cancel( popupTimeout ); - unregisterTriggers(); - removeTooltip(); - ttScope = null; - }); - }; - } - }; - }; - }]; -}) - -.directive( 'tooltip', [ '$tooltip', function ( $tooltip ) { - return $tooltip( 'tooltip', 'tooltip', 'mouseenter' ); -}]) - -.directive( 'tooltipPopup', function () { - return { - restrict: 'EA', - replace: true, - scope: { content: '@', placement: '@', animation: '&', isOpen: '&' }, - templateUrl: 'template/tooltip/tooltip-popup.html' - }; -}) - -.directive( 'tooltipHtmlUnsafe', [ '$tooltip', function ( $tooltip ) { - return $tooltip( 'tooltipHtmlUnsafe', 'tooltip', 'mouseenter' ); -}]) - -.directive( 'tooltipHtmlUnsafePopup', function () { - return { - restrict: 'EA', - replace: true, - scope: { content: '@', placement: '@', animation: '&', isOpen: '&' }, - templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html' - }; -}); \ No newline at end of file diff --git a/src/legacy/ui/public/autoload/modules.js b/src/legacy/ui/public/autoload/modules.js index 938796ed279ea..b40f051a5ec10 100644 --- a/src/legacy/ui/public/autoload/modules.js +++ b/src/legacy/ui/public/autoload/modules.js @@ -23,7 +23,6 @@ import '../config'; import '../notify'; import '../private'; import '../promises'; -import '../modals'; import '../state_management/app_state'; import '../state_management/global_state'; import '../style_compile'; diff --git a/src/legacy/ui/public/modals/__tests__/confirm_modal.js b/src/legacy/ui/public/modals/__tests__/confirm_modal.js deleted file mode 100644 index 6c05fb977c701..0000000000000 --- a/src/legacy/ui/public/modals/__tests__/confirm_modal.js +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import angular from 'angular'; -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import sinon from 'sinon'; -import _ from 'lodash'; - -describe('ui/modals/confirm_modal', function() { - let confirmModal; - let $rootScope; - - beforeEach(function() { - ngMock.module('kibana'); - ngMock.inject(function($injector) { - confirmModal = $injector.get('confirmModal'); - $rootScope = $injector.get('$rootScope'); - }); - }); - - function findByDataTestSubj(dataTestSubj) { - return angular.element(document.body).find(`[data-test-subj=${dataTestSubj}]`); - } - - afterEach(function() { - const confirmButton = findByDataTestSubj('confirmModalConfirmButton'); - if (confirmButton) { - angular.element(confirmButton).click(); - } - }); - - describe('throws an exception', function() { - it('when no custom confirm button passed', function() { - expect(() => confirmModal('hi', { onConfirm: _.noop })).to.throwError(); - }); - - it('when no custom noConfirm function is passed', function() { - expect(() => confirmModal('hi', { confirmButtonText: 'bye' })).to.throwError(); - }); - - it('when showClose is on but title is not given', function() { - const options = { customConfirmButton: 'b', onConfirm: _.noop, showClose: true }; - expect(() => confirmModal('hi', options)).to.throwError(); - }); - }); - - it('shows the message', function() { - const myMessage = 'Hi, how are you?'; - confirmModal(myMessage, { confirmButtonText: 'GREAT!', onConfirm: _.noop }); - - $rootScope.$digest(); - const message = findByDataTestSubj('confirmModalBodyText')[0].innerText.trim(); - expect(message).to.equal(myMessage); - }); - - describe('shows custom text', function() { - const confirmModalOptions = { - confirmButtonText: 'Troodon', - cancelButtonText: 'Dilophosaurus', - title: 'Dinosaurs', - onConfirm: _.noop, - }; - - it('for confirm button', () => { - confirmModal("What's your favorite dinosaur?", confirmModalOptions); - $rootScope.$digest(); - const confirmButtonText = findByDataTestSubj('confirmModalConfirmButton')[0].innerText.trim(); - expect(confirmButtonText).to.equal('Troodon'); - }); - - it('for cancel button', () => { - confirmModal("What's your favorite dinosaur?", confirmModalOptions); - $rootScope.$digest(); - const cancelButtonText = findByDataTestSubj('confirmModalCancelButton')[0].innerText.trim(); - expect(cancelButtonText).to.equal('Dilophosaurus'); - }); - - it('for title text', () => { - confirmModal("What's your favorite dinosaur?", confirmModalOptions); - $rootScope.$digest(); - const titleText = findByDataTestSubj('confirmModalTitleText')[0].innerText.trim(); - expect(titleText).to.equal('Dinosaurs'); - }); - }); - - describe('callbacks are called:', function() { - const confirmCallback = sinon.spy(); - const cancelCallback = sinon.spy(); - - const confirmModalOptions = { - confirmButtonText: 'bye', - onConfirm: confirmCallback, - onCancel: cancelCallback, - title: 'hi', - }; - - beforeEach(() => { - confirmCallback.resetHistory(); - cancelCallback.resetHistory(); - }); - - it('onCancel', function() { - confirmModal('hi', confirmModalOptions); - $rootScope.$digest(); - findByDataTestSubj('confirmModalCancelButton').click(); - - expect(confirmCallback.called).to.be(false); - expect(cancelCallback.called).to.be(true); - }); - - it('onConfirm', function() { - confirmModal('hi', confirmModalOptions); - $rootScope.$digest(); - findByDataTestSubj('confirmModalConfirmButton').click(); - - expect(confirmCallback.called).to.be(true); - expect(cancelCallback.called).to.be(false); - }); - }); -}); diff --git a/src/legacy/ui/public/modals/__tests__/confirm_modal_promise.js b/src/legacy/ui/public/modals/__tests__/confirm_modal_promise.js deleted file mode 100644 index 0967b3caefbbb..0000000000000 --- a/src/legacy/ui/public/modals/__tests__/confirm_modal_promise.js +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import testSubjSelector from '@kbn/test-subj-selector'; -import ngMock from 'ng_mock'; -import sinon from 'sinon'; -import $ from 'jquery'; - -describe('ui/modals/confirm_modal_promise', function() { - let $rootScope; - let message; - let confirmModalPromise; - let promise; - - beforeEach(function() { - ngMock.module('kibana'); - ngMock.inject(function($injector) { - confirmModalPromise = $injector.get('confirmModalPromise'); - $rootScope = $injector.get('$rootScope'); - }); - - message = 'woah'; - - promise = confirmModalPromise(message, { confirmButtonText: 'click me' }); - }); - - afterEach(function() { - $rootScope.$digest(); - $(testSubjSelector('confirmModalConfirmButton')).click(); - }); - - describe('before timeout completes', function() { - it('returned promise is not resolved', function() { - const callback = sinon.spy(); - promise.then(callback, callback); - $rootScope.$apply(); - expect(callback.called).to.be(false); - }); - }); - - describe('after timeout completes', function() { - it('confirmation dialogue is loaded to dom with message', function() { - $rootScope.$digest(); - const confirmModalElement = $(testSubjSelector('confirmModal')); - expect(confirmModalElement).to.not.be(undefined); - - const htmlString = confirmModalElement[0].innerHTML; - - expect(htmlString.indexOf(message)).to.be.greaterThan(0); - }); - - describe('when confirmed', function() { - it('promise is fulfilled with true', function() { - const confirmCallback = sinon.spy(); - const cancelCallback = sinon.spy(); - - promise.then(confirmCallback, cancelCallback); - $rootScope.$digest(); - const confirmButton = $(testSubjSelector('confirmModalConfirmButton')); - - confirmButton.click(); - expect(confirmCallback.called).to.be(true); - expect(cancelCallback.called).to.be(false); - }); - }); - - describe('when canceled', function() { - it('promise is rejected with false', function() { - const confirmCallback = sinon.spy(); - const cancelCallback = sinon.spy(); - promise.then(confirmCallback, cancelCallback); - - $rootScope.$digest(); - const noButton = $(testSubjSelector('confirmModalCancelButton')); - noButton.click(); - - expect(cancelCallback.called).to.be(true); - expect(confirmCallback.called).to.be(false); - }); - }); - - describe('error is thrown', function() { - it('when no confirm button text is used', function() { - const confirmCallback = sinon.spy(); - const cancelCallback = sinon.spy(); - confirmModalPromise(message).then(confirmCallback, cancelCallback); - - $rootScope.$digest(); - sinon.assert.notCalled(confirmCallback); - sinon.assert.calledOnce(cancelCallback); - sinon.assert.calledWithExactly( - cancelCallback, - sinon.match.has('message', sinon.match(/confirmation button text/)) - ); - }); - }); - }); -}); diff --git a/src/legacy/ui/public/modals/confirm_modal.html b/src/legacy/ui/public/modals/confirm_modal.html deleted file mode 100644 index 3eabe81fe9bd3..0000000000000 --- a/src/legacy/ui/public/modals/confirm_modal.html +++ /dev/null @@ -1,10 +0,0 @@ - diff --git a/src/legacy/ui/public/modals/confirm_modal.js b/src/legacy/ui/public/modals/confirm_modal.js deleted file mode 100644 index c609beff2fb16..0000000000000 --- a/src/legacy/ui/public/modals/confirm_modal.js +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import angular from 'angular'; -import { i18n } from '@kbn/i18n'; -import { noop } from 'lodash'; -import { uiModules } from '../modules'; -import template from './confirm_modal.html'; -import { ModalOverlay } from './modal_overlay'; - -const module = uiModules.get('kibana'); - -import { - EUI_MODAL_CONFIRM_BUTTON as CONFIRM_BUTTON, - EUI_MODAL_CANCEL_BUTTON as CANCEL_BUTTON, -} from '@elastic/eui'; - -export const ConfirmationButtonTypes = { - CONFIRM: CONFIRM_BUTTON, - CANCEL: CANCEL_BUTTON, -}; - -export function confirmModalFactory($rootScope, $compile) { - let modalPopover; - const confirmQueue = []; - - /** - * @param {String} message - the message to show in the body of the confirmation dialog. - * @param {ConfirmModalOptions} - Options to further customize the dialog. - */ - return function confirmModal(message, customOptions) { - const defaultOptions = { - onCancel: noop, - cancelButtonText: i18n.translate('common.ui.modals.cancelButtonLabel', { - defaultMessage: 'Cancel', - }), - defaultFocusedButton: ConfirmationButtonTypes.CONFIRM, - }; - - if (!customOptions.confirmButtonText || !customOptions.onConfirm) { - throw new Error('Please specify confirmation button text and onConfirm action'); - } - - const options = Object.assign(defaultOptions, customOptions); - - // Special handling for onClose - if no specific callback was supplied, default to the - // onCancel callback. - options.onClose = customOptions.onClose || options.onCancel; - - const confirmScope = $rootScope.$new(); - - confirmScope.message = message; - confirmScope.defaultFocusedButton = options.defaultFocusedButton; - confirmScope.confirmButtonText = options.confirmButtonText; - confirmScope.cancelButtonText = options.cancelButtonText; - confirmScope.title = options.title; - confirmScope.onConfirm = () => { - destroy(); - options.onConfirm(); - }; - confirmScope.onCancel = () => { - destroy(); - options.onCancel(); - }; - confirmScope.onClose = () => { - destroy(); - options.onClose(); - }; - - function showModal(confirmScope) { - const modalInstance = $compile(template)(confirmScope); - modalPopover = new ModalOverlay(modalInstance); - } - - if (modalPopover) { - confirmQueue.unshift(confirmScope); - } else { - showModal(confirmScope); - } - - function destroy() { - modalPopover.destroy(); - modalPopover = undefined; - angular.element(document.body).off('keydown'); - confirmScope.$destroy(); - - if (confirmQueue.length > 0) { - showModal(confirmQueue.pop()); - } - } - }; -} - -/** - * @typedef {Object} ConfirmModalOptions - * @property {String} confirmButtonText - * @property {String=} cancelButtonText - * @property {function} onConfirm - * @property {function=} onCancel - * @property {String=} title - If given, shows a title on the confirm modal. - */ - -module.factory('confirmModal', confirmModalFactory); diff --git a/src/legacy/ui/public/modals/confirm_modal_promise.js b/src/legacy/ui/public/modals/confirm_modal_promise.js deleted file mode 100644 index 54f568e80bff0..0000000000000 --- a/src/legacy/ui/public/modals/confirm_modal_promise.js +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { uiModules } from '../modules'; -import './'; - -const module = uiModules.get('kibana'); - -/** - * @typedef {Object} PromisifiedConfirmOptions - * @property {String} confirmButtonText - * @property {String=} cancelButtonText - */ - -/** - * A "promisified" version of ConfirmModal that binds onCancel and onConfirm to - * Resolve and Reject methods. - */ -module.factory('confirmModalPromise', function(Promise, confirmModal) { - /** - * @param {String} message - * @param {PromisifiedConfirmOptions} customOptions - */ - return (message, customOptions) => - new Promise((resolve, reject) => { - const defaultOptions = { - onConfirm: resolve, - onCancel: reject, - }; - const confirmOptions = Object.assign(defaultOptions, customOptions); - confirmModal(message, confirmOptions); - }); -}); diff --git a/src/legacy/ui/public/modals/modal_overlay.html b/src/legacy/ui/public/modals/modal_overlay.html deleted file mode 100644 index 2abc5768f46f1..0000000000000 --- a/src/legacy/ui/public/modals/modal_overlay.html +++ /dev/null @@ -1 +0,0 @@ -
diff --git a/src/legacy/ui/public/modals/modal_overlay.js b/src/legacy/ui/public/modals/modal_overlay.js deleted file mode 100644 index 6ddecee9f2f71..0000000000000 --- a/src/legacy/ui/public/modals/modal_overlay.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import angular from 'angular'; -import modalOverlayTemplate from './modal_overlay.html'; - -/** - * Appends the modal to the dom on instantiation, and removes it when destroy is called. - */ -export class ModalOverlay { - constructor(modalElement) { - this.overlayElement = angular.element(modalOverlayTemplate); - this.overlayElement.append(modalElement); - - angular.element(document.body).append(this.overlayElement); - } - - /** - * Removes the overlay and modal from the dom. - */ - destroy() { - this.overlayElement.remove(); - } -} diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js index 3cc33504d3daa..47ef690c4f83e 100644 --- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js +++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js @@ -176,7 +176,11 @@ let isAutoRefreshSelectorEnabled = true; export const npStart = { core: { - chrome: {}, + chrome: { + overlays: { + openModal: sinon.fake(), + }, + }, }, plugins: { management: { @@ -212,6 +216,10 @@ export const npStart = { config: { defaultAppId: 'home', }, + dashboardConfig: { + turnHideWriteControlsOn: sinon.fake(), + getHideWriteControls: sinon.fake(), + }, }, data: { autocomplete: { diff --git a/src/legacy/ui/public/react_components.js b/src/legacy/ui/public/react_components.js index fea25d2c71da3..b771e37c9d538 100644 --- a/src/legacy/ui/public/react_components.js +++ b/src/legacy/ui/public/react_components.js @@ -19,14 +19,12 @@ import 'ngreact'; -import { EuiConfirmModal, EuiIcon, EuiIconTip } from '@elastic/eui'; +import { EuiIcon, EuiIconTip } from '@elastic/eui'; import { uiModules } from './modules'; const app = uiModules.get('app/kibana', ['react']); -app.directive('confirmModal', reactDirective => reactDirective(EuiConfirmModal)); - app.directive('icon', reactDirective => reactDirective(EuiIcon)); app.directive('iconTip', reactDirective => diff --git a/src/legacy/ui/public/registry/feature_catalogue.js b/src/legacy/ui/public/registry/feature_catalogue.js deleted file mode 100644 index 23aaf2fb0a1d9..0000000000000 --- a/src/legacy/ui/public/registry/feature_catalogue.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { uiRegistry } from './_registry'; -import { capabilities } from '../capabilities'; -export { FeatureCatalogueCategory } from '../../../../plugins/home/public'; - -export const FeatureCatalogueRegistryProvider = uiRegistry({ - name: 'featureCatalogue', - index: ['id'], - group: ['category'], - order: ['title'], - filter: featureCatalogItem => { - const isDisabledViaCapabilities = capabilities.get().catalogue[featureCatalogItem.id] === false; - return !isDisabledViaCapabilities && Object.keys(featureCatalogItem).length > 0; - }, -}); diff --git a/src/legacy/ui/public/registry/feature_catalogue.test.js b/src/legacy/ui/public/registry/feature_catalogue.test.js deleted file mode 100644 index 15aed78143882..0000000000000 --- a/src/legacy/ui/public/registry/feature_catalogue.test.js +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -jest.mock('ui/capabilities', () => ({ - capabilities: { - get: () => ({ - navLinks: {}, - management: {}, - catalogue: { - item1: true, - item2: false, - item3: true, - }, - }), - }, -})); -import { FeatureCatalogueCategory, FeatureCatalogueRegistryProvider } from './feature_catalogue'; - -describe('FeatureCatalogueRegistryProvider', () => { - beforeAll(() => { - FeatureCatalogueRegistryProvider.register(() => { - return { - id: 'item1', - title: 'foo', - description: 'this is foo', - icon: 'savedObjectsApp', - path: '/app/kibana#/management/kibana/objects', - showOnHomePage: true, - category: FeatureCatalogueCategory.ADMIN, - }; - }); - - FeatureCatalogueRegistryProvider.register(() => { - return { - id: 'item2', - title: 'bar', - description: 'this is bar', - icon: 'savedObjectsApp', - path: '/app/kibana#/management/kibana/objects', - showOnHomePage: true, - category: FeatureCatalogueCategory.ADMIN, - }; - }); - - // intentionally not listed in uiCapabilities.catalogue above - FeatureCatalogueRegistryProvider.register(() => { - return { - id: 'item4', - title: 'secret', - description: 'this is a secret', - icon: 'savedObjectsApp', - path: '/app/kibana#/management/kibana/objects', - showOnHomePage: true, - category: FeatureCatalogueCategory.ADMIN, - }; - }); - }); - - it('should not return items hidden by uiCapabilities', () => { - const mockPrivate = entityFn => entityFn(); - const mockInjector = () => null; - - // eslint-disable-next-line new-cap - const foo = FeatureCatalogueRegistryProvider(mockPrivate, mockInjector).inTitleOrder; - expect(foo).toEqual([ - { - id: 'item1', - title: 'foo', - description: 'this is foo', - icon: 'savedObjectsApp', - path: '/app/kibana#/management/kibana/objects', - showOnHomePage: true, - category: FeatureCatalogueCategory.ADMIN, - }, - { - id: 'item4', - title: 'secret', - description: 'this is a secret', - icon: 'savedObjectsApp', - path: '/app/kibana#/management/kibana/objects', - showOnHomePage: true, - category: FeatureCatalogueCategory.ADMIN, - }, - ]); - }); -}); diff --git a/src/legacy/ui/public/saved_objects/__tests__/saved_object.js b/src/legacy/ui/public/saved_objects/__tests__/saved_object.js index ace87a15f7b58..d027d8b6c99da 100644 --- a/src/legacy/ui/public/saved_objects/__tests__/saved_object.js +++ b/src/legacy/ui/public/saved_objects/__tests__/saved_object.js @@ -26,7 +26,7 @@ import { createSavedObjectClass } from '../saved_object'; import StubIndexPattern from 'test_utils/stub_index_pattern'; import { npStart } from 'ui/new_platform'; import { InvalidJSONProperty } from '../../../../../plugins/kibana_utils/public'; -import { npSetup } from '../../new_platform/new_platform.karma_mock'; +import { npSetup, npStart as npStartMock } from '../../new_platform/new_platform.karma_mock'; const getConfig = cfg => cfg; @@ -89,18 +89,12 @@ describe('Saved Object', function() { obj[fName].restore && obj[fName].restore(); } - beforeEach( - ngMock.module( - 'kibana', - // Use the native window.confirm instead of our specialized version to make testing - // this easier. - function($provide) { - const overrideConfirm = message => - window.confirm(message) ? Promise.resolve() : Promise.reject(); - $provide.decorator('confirmModalPromise', () => overrideConfirm); - } - ) - ); + beforeEach(() => { + // Use the native window.confirm instead of our specialized version to make testing + // this easier. + npStartMock.core.overlays.openModal = message => + window.confirm(message) ? Promise.resolve() : Promise.reject(); + }); beforeEach( ngMock.inject(function($window) { diff --git a/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts b/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts index d8227343159e6..7228e506accb9 100644 --- a/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts +++ b/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts @@ -22,7 +22,12 @@ import { identity } from 'lodash'; import { IAggConfig } from 'ui/agg_types'; import { npStart } from 'ui/new_platform'; import { SerializedFieldFormat } from 'src/plugins/expressions/public'; -import { fieldFormats } from '../../../../../../plugins/data/public'; +import { + fieldFormats, + IFieldFormat, + FieldFormatId, + FieldFormatsContentType, +} from '../../../../../../plugins/data/public'; import { Vis } from '../../../../../core_plugins/visualizations/public'; import { tabifyGetColumns } from '../../../agg_response/tabify/_get_columns'; @@ -45,10 +50,7 @@ const getConfig = (key: string, defaultOverride?: any): any => npStart.core.uiSettings.get(key, defaultOverride); const DefaultFieldFormat = fieldFormats.FieldFormat.from(identity); -const getFieldFormat = ( - id?: fieldFormats.IFieldFormatId, - params: object = {} -): fieldFormats.FieldFormat => { +const getFieldFormat = (id?: FieldFormatId, params: object = {}): IFieldFormat => { const fieldFormatsService = npStart.plugins.data.fieldFormats; if (id) { @@ -94,7 +96,7 @@ export const createFormat = (agg: IAggConfig): SerializedFieldFormat => { return formats[agg.type.name] ? formats[agg.type.name]() : format; }; -export type FormatFactory = (mapping?: SerializedFieldFormat) => fieldFormats.FieldFormat; +export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat; export const getFormat: FormatFactory = mapping => { if (!mapping) { @@ -133,7 +135,7 @@ export const getFormat: FormatFactory = mapping => { return new IpRangeFormat(); } else if (isTermsFieldFormat(mapping) && mapping.params) { const { params } = mapping; - const convert = (val: string, type: fieldFormats.ContentType) => { + const convert = (val: string, type: FieldFormatsContentType) => { const format = getFieldFormat(params.id, mapping.params); if (val === '__other__') { @@ -148,8 +150,8 @@ export const getFormat: FormatFactory = mapping => { return { convert, - getConverterFor: (type: fieldFormats.ContentType) => (val: string) => convert(val, type), - } as fieldFormats.FieldFormat; + getConverterFor: (type: FieldFormatsContentType) => (val: string) => convert(val, type), + } as IFieldFormat; } else { return getFieldFormat(id, mapping.params); } diff --git a/src/optimize/base_optimizer.js b/src/optimize/base_optimizer.js index d9df2a1955df3..539c55c969653 100644 --- a/src/optimize/base_optimizer.js +++ b/src/optimize/base_optimizer.js @@ -459,7 +459,7 @@ export default class BaseOptimizer { optimization: { minimizer: [ new TerserPlugin({ - parallel: this.getThreadLoaderPoolConfig().workers, + parallel: false, sourceMap: false, cache: false, extractComments: false, diff --git a/src/optimize/dynamic_dll_plugin/dll_config_model.js b/src/optimize/dynamic_dll_plugin/dll_config_model.js index 2e74cb6af86d4..9ca6071b8f515 100644 --- a/src/optimize/dynamic_dll_plugin/dll_config_model.js +++ b/src/optimize/dynamic_dll_plugin/dll_config_model.js @@ -214,16 +214,20 @@ function common(config) { return webpackMerge(generateDLL(config)); } -function optimized(config) { +function optimized() { return webpackMerge({ mode: 'production', optimization: { minimizer: [ new TerserPlugin({ - // Apply the same logic used to calculate the - // threadLoaderPool workers number to spawn - // the parallel processes on terser - parallel: config.threadLoaderPoolConfig.workers, + // NOTE: we should not enable that option for now + // Since 2.0.0 terser-webpack-plugin is using jest-worker + // to run tasks in a pool of workers. Currently it looks like + // is requiring too much memory and break on large entry points + // compilations (like this) one. Also the gain we have enabling + // that option was barely noticed. + // https://github.com/webpack-contrib/terser-webpack-plugin/issues/143 + parallel: false, sourceMap: false, cache: false, extractComments: false, @@ -250,5 +254,5 @@ export function configModel(rawConfig = {}) { return webpackMerge(common(config), unoptimized()); } - return webpackMerge(common(config), optimized(config)); + return webpackMerge(common(config), optimized()); } diff --git a/src/plugins/advanced_settings/kibana.json b/src/plugins/advanced_settings/kibana.json index 5fc1e916ae45f..bafb2caba32be 100644 --- a/src/plugins/advanced_settings/kibana.json +++ b/src/plugins/advanced_settings/kibana.json @@ -3,5 +3,5 @@ "version": "kibana", "server": false, "ui": true, - "requiredPlugins": [] + "requiredPlugins": ["home"] } diff --git a/src/plugins/advanced_settings/public/mocks.ts b/src/plugins/advanced_settings/public/mocks.ts index e147f57101aae..b44561959cbd3 100644 --- a/src/plugins/advanced_settings/public/mocks.ts +++ b/src/plugins/advanced_settings/public/mocks.ts @@ -25,9 +25,9 @@ const componentType = ComponentRegistry.componentType; export const advancedSettingsMock = { createSetupContract() { - return { register, componentType }; + return { component: { register, componentType } }; }, createStartContract() { - return { get, componentType }; + return { component: { get, componentType } }; }, }; diff --git a/src/plugins/advanced_settings/public/plugin.ts b/src/plugins/advanced_settings/public/plugin.ts index 692e515ca4e5e..bffd5a5157615 100644 --- a/src/plugins/advanced_settings/public/plugin.ts +++ b/src/plugins/advanced_settings/public/plugin.ts @@ -17,15 +17,31 @@ * under the License. */ +import { i18n } from '@kbn/i18n'; import { CoreSetup, CoreStart, Plugin } from 'kibana/public'; import { ComponentRegistry } from './component_registry'; import { AdvancedSettingsSetup, AdvancedSettingsStart } from './types'; +import { FeatureCatalogueCategory, HomePublicPluginSetup } from '../../home/public'; const component = new ComponentRegistry(); export class AdvancedSettingsPlugin implements Plugin { - public setup(core: CoreSetup) { + public setup(core: CoreSetup, { home }: { home: HomePublicPluginSetup }) { + home.featureCatalogue.register({ + id: 'advanced_settings', + title: i18n.translate('advancedSettings.advancedSettingsLabel', { + defaultMessage: 'Advanced Settings', + }), + description: i18n.translate('advancedSettings.advancedSettingsDescription', { + defaultMessage: 'Directly edit settings that control behavior in Kibana.', + }), + icon: 'advancedSettingsApp', + path: '/app/kibana#/management/kibana/settings', + showOnHomePage: false, + category: FeatureCatalogueCategory.ADMIN, + }); + return { component: component.setup, }; diff --git a/src/plugins/data/common/field_formats/content_types/html_content_type.ts b/src/plugins/data/common/field_formats/content_types/html_content_type.ts index 1b6ee9e63fad1..d4701200d99e0 100644 --- a/src/plugins/data/common/field_formats/content_types/html_content_type.ts +++ b/src/plugins/data/common/field_formats/content_types/html_content_type.ts @@ -17,10 +17,10 @@ * under the License. */ import { escape, isFunction } from 'lodash'; -import { IFieldFormat, HtmlContextTypeConvert } from '../types'; +import { IFieldFormat, HtmlContextTypeConvert, FieldFormatsContentType } from '../types'; import { asPrettyString, getHighlightHtml } from '../utils'; -export const HTML_CONTEXT_TYPE = 'html'; +export const HTML_CONTEXT_TYPE: FieldFormatsContentType = 'html'; const getConvertFn = ( format: IFieldFormat, diff --git a/src/plugins/data/common/field_formats/content_types/text_content_type.ts b/src/plugins/data/common/field_formats/content_types/text_content_type.ts index c91b7c6d1c18c..dc450086edc62 100644 --- a/src/plugins/data/common/field_formats/content_types/text_content_type.ts +++ b/src/plugins/data/common/field_formats/content_types/text_content_type.ts @@ -18,10 +18,10 @@ */ import { isFunction } from 'lodash'; -import { IFieldFormat, TextContextTypeConvert } from '../types'; +import { IFieldFormat, TextContextTypeConvert, FieldFormatsContentType } from '../types'; import { asPrettyString } from '../utils'; -export const TEXT_CONTEXT_TYPE = 'text'; +export const TEXT_CONTEXT_TYPE: FieldFormatsContentType = 'text'; export const setup = ( format: IFieldFormat, diff --git a/src/plugins/data/common/field_formats/converters/date_server.ts b/src/plugins/data/common/field_formats/converters/date_server.ts index 9be8f066539f1..216af133bb5f5 100644 --- a/src/plugins/data/common/field_formats/converters/date_server.ts +++ b/src/plugins/data/common/field_formats/converters/date_server.ts @@ -25,7 +25,7 @@ import { FieldFormat } from '../field_format'; import { TextContextTypeConvert, FIELD_FORMAT_IDS, - GetConfigFn, + FieldFormatsGetConfigFn, IFieldFormatMetaParams, } from '../types'; @@ -40,7 +40,7 @@ export class DateFormat extends FieldFormat { private memoizedPattern: string = ''; private timeZone: string = ''; - constructor(params: IFieldFormatMetaParams, getConfig?: GetConfigFn) { + constructor(params: IFieldFormatMetaParams, getConfig?: FieldFormatsGetConfigFn) { super(params, getConfig); this.memoizedConverter = memoize((val: any) => { diff --git a/src/plugins/data/common/field_formats/field_format.ts b/src/plugins/data/common/field_formats/field_format.ts index d605dcd2e78ac..49baa8c074da8 100644 --- a/src/plugins/data/common/field_formats/field_format.ts +++ b/src/plugins/data/common/field_formats/field_format.ts @@ -20,8 +20,8 @@ import { transform, size, cloneDeep, get, defaults } from 'lodash'; import { createCustomFieldFormat } from './converters/custom'; import { - GetConfigFn, - ContentType, + FieldFormatsGetConfigFn, + FieldFormatsContentType, IFieldFormatType, FieldFormatConvert, FieldFormatConvertFunction, @@ -29,12 +29,7 @@ import { TextContextTypeOptions, IFieldFormatMetaParams, } from './types'; -import { - htmlContentTypeSetup, - textContentTypeSetup, - TEXT_CONTEXT_TYPE, - HTML_CONTEXT_TYPE, -} from './content_types'; +import { htmlContentTypeSetup, textContentTypeSetup, TEXT_CONTEXT_TYPE } from './content_types'; import { HtmlContextTypeConvert, TextContextTypeConvert } from './types'; const DEFAULT_CONTEXT_TYPE = TEXT_CONTEXT_TYPE; @@ -90,9 +85,9 @@ export abstract class FieldFormat { public type: any = this.constructor; protected readonly _params: any; - protected getConfig: GetConfigFn | undefined; + protected getConfig: FieldFormatsGetConfigFn | undefined; - constructor(_params: IFieldFormatMetaParams = {}, getConfig?: GetConfigFn) { + constructor(_params: IFieldFormatMetaParams = {}, getConfig?: FieldFormatsGetConfigFn) { this._params = _params; if (getConfig) { @@ -112,7 +107,7 @@ export abstract class FieldFormat { */ convert( value: any, - contentType: ContentType = DEFAULT_CONTEXT_TYPE, + contentType: FieldFormatsContentType = DEFAULT_CONTEXT_TYPE, options?: HtmlContextTypeOptions | TextContextTypeOptions ): string { const converter = this.getConverterFor(contentType); @@ -131,7 +126,7 @@ export abstract class FieldFormat { * @public */ getConverterFor( - contentType: ContentType = DEFAULT_CONTEXT_TYPE + contentType: FieldFormatsContentType = DEFAULT_CONTEXT_TYPE ): FieldFormatConvertFunction | null { if (!this.convertObject) { this.convertObject = this.setupContentType(); @@ -210,8 +205,8 @@ export abstract class FieldFormat { setupContentType(): FieldFormatConvert { return { - [TEXT_CONTEXT_TYPE]: textContentTypeSetup(this, this.textConvert), - [HTML_CONTEXT_TYPE]: htmlContentTypeSetup(this, this.htmlConvert), + text: textContentTypeSetup(this, this.textConvert), + html: htmlContentTypeSetup(this, this.htmlConvert), }; } diff --git a/src/plugins/data/common/field_formats/field_formats_registry.test.ts b/src/plugins/data/common/field_formats/field_formats_registry.test.ts index 5f6f9fdf897ff..0b32a62744fb1 100644 --- a/src/plugins/data/common/field_formats/field_formats_registry.test.ts +++ b/src/plugins/data/common/field_formats/field_formats_registry.test.ts @@ -18,7 +18,7 @@ */ import { FieldFormatsRegistry } from './field_formats_registry'; import { BoolFormat, PercentFormat, StringFormat } from './converters'; -import { GetConfigFn, IFieldFormatType } from './types'; +import { FieldFormatsGetConfigFn, IFieldFormatType } from './types'; import { KBN_FIELD_TYPES } from '../../common'; const getValueOfPrivateField = (instance: any, field: string) => instance[field]; @@ -26,7 +26,7 @@ const getValueOfPrivateField = (instance: any, field: string) => instance[field] describe('FieldFormatsRegistry', () => { let fieldFormatsRegistry: FieldFormatsRegistry; let defaultMap = {}; - const getConfig = (() => defaultMap) as GetConfigFn; + const getConfig = (() => defaultMap) as FieldFormatsGetConfigFn; beforeEach(() => { fieldFormatsRegistry = new FieldFormatsRegistry(); diff --git a/src/plugins/data/common/field_formats/field_formats_registry.ts b/src/plugins/data/common/field_formats/field_formats_registry.ts index 9b85921b820c8..6e4880a221c46 100644 --- a/src/plugins/data/common/field_formats/field_formats_registry.ts +++ b/src/plugins/data/common/field_formats/field_formats_registry.ts @@ -23,24 +23,24 @@ import { forOwn, isFunction, memoize } from 'lodash'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '../../common'; import { - GetConfigFn, - IFieldFormatConfig, + FieldFormatsGetConfigFn, + FieldFormatConfig, FIELD_FORMAT_IDS, IFieldFormatType, - IFieldFormatId, + FieldFormatId, IFieldFormatMetaParams, } from './types'; import { baseFormatters } from './constants/base_formatters'; import { FieldFormat } from './field_format'; export class FieldFormatsRegistry { - protected fieldFormats: Map = new Map(); - protected defaultMap: Record = {}; + protected fieldFormats: Map = new Map(); + protected defaultMap: Record = {}; protected metaParamsOptions: Record = {}; - protected getConfig?: GetConfigFn; + protected getConfig?: FieldFormatsGetConfigFn; init( - getConfig: GetConfigFn, + getConfig: FieldFormatsGetConfigFn, metaParamsOptions: Record = {}, defaultFieldConverters: IFieldFormatType[] = baseFormatters ) { @@ -62,7 +62,7 @@ export class FieldFormatsRegistry { getDefaultConfig = ( fieldType: KBN_FIELD_TYPES, esTypes?: ES_FIELD_TYPES[] - ): IFieldFormatConfig => { + ): FieldFormatConfig => { const type = this.getDefaultTypeName(fieldType, esTypes); return ( @@ -73,10 +73,10 @@ export class FieldFormatsRegistry { /** * Get a derived FieldFormat class by its id. * - * @param {IFieldFormatId} formatId - the format id - * @return {FieldFormat | undefined} + * @param {FieldFormatId} formatId - the format id + * @return {IFieldFormatType | undefined} */ - getType = (formatId: IFieldFormatId): IFieldFormatType | undefined => { + getType = (formatId: FieldFormatId): IFieldFormatType | undefined => { const fieldFormat = this.fieldFormats.get(formatId); if (fieldFormat) { @@ -97,7 +97,7 @@ export class FieldFormatsRegistry { * * @param {KBN_FIELD_TYPES} fieldType * @param {ES_FIELD_TYPES[]} esTypes - Array of ES data types - * @return {FieldFormat | undefined} + * @return {IFieldFormatType | undefined} */ getDefaultType = ( fieldType: KBN_FIELD_TYPES, @@ -129,7 +129,7 @@ export class FieldFormatsRegistry { * * @param {KBN_FIELD_TYPES} fieldType * @param {ES_FIELD_TYPES[]} esTypes - * @return {ES_FIELD_TYPES | String} + * @return {ES_FIELD_TYPES | KBN_FIELD_TYPES} */ getDefaultTypeName = ( fieldType: KBN_FIELD_TYPES, @@ -143,11 +143,11 @@ export class FieldFormatsRegistry { /** * Get the singleton instance of the FieldFormat type by its id. * - * @param {IFieldFormatId} formatId - * @return {FIELD_FORMATS_INSTANCES[number]} + * @param {FieldFormatId} formatId + * @return {FieldFormat} */ getInstance = memoize( - (formatId: IFieldFormatId, params: Record = {}): FieldFormat => { + (formatId: FieldFormatId, params: Record = {}): FieldFormat => { const ConcreteFieldFormat = this.getType(formatId); if (!ConcreteFieldFormat) { @@ -156,7 +156,7 @@ export class FieldFormatsRegistry { return new ConcreteFieldFormat(params, this.getConfig); }, - (formatId: IFieldFormatId, params: Record) => + (formatId: FieldFormatId, params: Record) => JSON.stringify({ formatId, ...params, @@ -197,7 +197,7 @@ export class FieldFormatsRegistry { * Get filtered list of field formats by format type * * @param {KBN_FIELD_TYPES} fieldType - * @return {FieldFormat[]} + * @return {IFieldFormatType[]} */ getByFieldType(fieldType: KBN_FIELD_TYPES): IFieldFormatType[] { return [...this.fieldFormats.values()] @@ -238,7 +238,7 @@ export class FieldFormatsRegistry { * * @private * @param {IFieldFormatType} fieldFormat - field format type - * @return {FieldFormat | undefined} + * @return {IFieldFormatType | undefined} */ private fieldFormatMetaParamsDecorator = ( fieldFormat: IFieldFormatType @@ -250,7 +250,7 @@ export class FieldFormatsRegistry { static id = fieldFormat.id; static fieldType = fieldFormat.fieldType; - constructor(params: Record = {}, getConfig?: GetConfigFn) { + constructor(params: Record = {}, getConfig?: FieldFormatsGetConfigFn) { super(getMetaParams(params), getConfig); } }; diff --git a/src/plugins/data/common/field_formats/index.ts b/src/plugins/data/common/field_formats/index.ts index e54903375dcf1..0847ac0db745f 100644 --- a/src/plugins/data/common/field_formats/index.ts +++ b/src/plugins/data/common/field_formats/index.ts @@ -17,6 +17,42 @@ * under the License. */ -import * as fieldFormats from './static'; +import { FieldFormatsRegistry } from './field_formats_registry'; +type IFieldFormatsRegistry = PublicMethodsOf; -export { fieldFormats }; +export { FieldFormatsRegistry, IFieldFormatsRegistry }; +export { FieldFormat } from './field_format'; +export { baseFormatters } from './constants/base_formatters'; +export { + BoolFormat, + BytesFormat, + ColorFormat, + DateFormat, + DateNanosFormat, + DurationFormat, + IpFormat, + NumberFormat, + PercentFormat, + RelativeDateFormat, + SourceFormat, + StaticLookupFormat, + UrlFormat, + StringFormat, + TruncateFormat, +} from './converters'; + +export { getHighlightRequest } from './utils'; + +export { DEFAULT_CONVERTER_COLOR } from './constants/color_default'; +export { FIELD_FORMAT_IDS } from './types'; +export { HTML_CONTEXT_TYPE, TEXT_CONTEXT_TYPE } from './content_types'; + +export { + FieldFormatsGetConfigFn, + FieldFormatsContentType, + FieldFormatConfig, + FieldFormatId, + // Used in data plugin only + IFieldFormatType, + IFieldFormat, +} from './types'; diff --git a/src/plugins/data/common/field_formats/static.ts b/src/plugins/data/common/field_formats/static.ts deleted file mode 100644 index 186a0ff6ede5c..0000000000000 --- a/src/plugins/data/common/field_formats/static.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/** - * Everything the file exports is public - */ - -export { HTML_CONTEXT_TYPE, TEXT_CONTEXT_TYPE } from './content_types'; -export { FieldFormat } from './field_format'; -export { FieldFormatsRegistry } from './field_formats_registry'; -export { getHighlightRequest, asPrettyString, getHighlightHtml } from './utils'; - -export { baseFormatters } from './constants/base_formatters'; -export { DEFAULT_CONVERTER_COLOR } from './constants/color_default'; - -export { - BoolFormat, - BytesFormat, - ColorFormat, - DateFormat, - DateNanosFormat, - DurationFormat, - IpFormat, - NumberFormat, - PercentFormat, - RelativeDateFormat, - SourceFormat, - StaticLookupFormat, - UrlFormat, - StringFormat, - TruncateFormat, -} from './converters'; - -export { - GetConfigFn, - FIELD_FORMAT_IDS, - ContentType, - IFieldFormatConfig, - IFieldFormatType, - IFieldFormat, - IFieldFormatId, -} from './types'; diff --git a/src/plugins/data/common/field_formats/types.ts b/src/plugins/data/common/field_formats/types.ts index b6c10c9964f67..24aa92c67b694 100644 --- a/src/plugins/data/common/field_formats/types.ts +++ b/src/plugins/data/common/field_formats/types.ts @@ -20,7 +20,7 @@ import { FieldFormat } from './field_format'; /** @public **/ -export type ContentType = 'html' | 'text'; +export type FieldFormatsContentType = 'html' | 'text'; /** @internal **/ export interface HtmlContextTypeOptions { @@ -66,23 +66,26 @@ export enum FIELD_FORMAT_IDS { URL = 'url', } -export interface IFieldFormatConfig { - id: IFieldFormatId; +export interface FieldFormatConfig { + id: FieldFormatId; params: Record; es?: boolean; } -export type GetConfigFn = (key: string, defaultOverride?: T) => T; +export type FieldFormatsGetConfigFn = (key: string, defaultOverride?: T) => T; export type IFieldFormat = PublicMethodsOf; /** * @string id type is needed for creating custom converters. */ -export type IFieldFormatId = FIELD_FORMAT_IDS | string; +export type FieldFormatId = FIELD_FORMAT_IDS | string; -export type IFieldFormatType = (new (params?: any, getConfig?: GetConfigFn) => FieldFormat) & { - id: IFieldFormatId; +export type IFieldFormatType = (new ( + params?: any, + getConfig?: FieldFormatsGetConfigFn +) => FieldFormat) & { + id: FieldFormatId; fieldType: string | string[]; }; diff --git a/src/plugins/data/public/field_formats/field_formats_service.ts b/src/plugins/data/public/field_formats/field_formats_service.ts index 68df0aa254580..1c43ce2198645 100644 --- a/src/plugins/data/public/field_formats/field_formats_service.ts +++ b/src/plugins/data/public/field_formats/field_formats_service.ts @@ -18,10 +18,10 @@ */ import { CoreSetup } from 'src/core/public'; -import { fieldFormats } from '../../common/field_formats'; +import { FieldFormatsRegistry } from '../../common/field_formats'; export class FieldFormatsService { - private readonly fieldFormatsRegistry: fieldFormats.FieldFormatsRegistry = new fieldFormats.FieldFormatsRegistry(); + private readonly fieldFormatsRegistry: FieldFormatsRegistry = new FieldFormatsRegistry(); public setup(core: CoreSetup) { core.uiSettings.getUpdate$().subscribe(({ key, newValue }) => { @@ -49,7 +49,7 @@ export class FieldFormatsService { } /** @public */ -export type FieldFormatsSetup = Pick; +export type FieldFormatsSetup = Pick; /** @public */ -export type FieldFormatsStart = Omit; +export type FieldFormatsStart = Omit; diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 2fa6b8deae69d..d861a1ee0dd83 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -19,6 +19,60 @@ import { PluginInitializerContext } from '../../../core/public'; +/* + * Field Formatters helper namespace: + */ + +import { + FieldFormat, + FieldFormatsRegistry, // exported only for tests. Consider mock. + DEFAULT_CONVERTER_COLOR, + HTML_CONTEXT_TYPE, + TEXT_CONTEXT_TYPE, + FIELD_FORMAT_IDS, + BoolFormat, + BytesFormat, + ColorFormat, + DateFormat, + DateNanosFormat, + DurationFormat, + IpFormat, + NumberFormat, + PercentFormat, + RelativeDateFormat, + SourceFormat, + StaticLookupFormat, + UrlFormat, + StringFormat, + TruncateFormat, +} from '../common/field_formats'; + +export const fieldFormats = { + FieldFormat, + FieldFormatsRegistry, // exported only for tests. Consider mock. + + DEFAULT_CONVERTER_COLOR, + HTML_CONTEXT_TYPE, + TEXT_CONTEXT_TYPE, + FIELD_FORMAT_IDS, + + BoolFormat, + BytesFormat, + ColorFormat, + DateFormat, + DateNanosFormat, + DurationFormat, + IpFormat, + NumberFormat, + PercentFormat, + RelativeDateFormat, + SourceFormat, + StaticLookupFormat, + UrlFormat, + StringFormat, + TruncateFormat, +}; + export function plugin(initializerContext: PluginInitializerContext) { return new DataPublicPlugin(initializerContext); } @@ -42,9 +96,15 @@ export { // timefilter RefreshInterval, TimeRange, + // Field Formats + IFieldFormat, + IFieldFormatsRegistry, + FieldFormatsContentType, + FieldFormatsGetConfigFn, + FieldFormatConfig, + FieldFormatId, } from '../common'; export { autocomplete } from './autocomplete'; -export * from './field_formats'; export * from './index_patterns'; export * from './search'; export * from './query'; @@ -54,7 +114,6 @@ export { esFilters, esKuery, esQuery, - fieldFormats, // index patterns isFilterable, // kbn field types diff --git a/src/plugins/data/public/index_patterns/fields/field.ts b/src/plugins/data/public/index_patterns/fields/field.ts index 730a2f88c5eb7..f59fbefbea451 100644 --- a/src/plugins/data/public/index_patterns/fields/field.ts +++ b/src/plugins/data/public/index_patterns/fields/field.ts @@ -26,8 +26,8 @@ import { IFieldType, getKbnFieldType, IFieldSubType, - fieldFormats, shortenDottedString, + FieldFormat, } from '../../../common'; export type FieldSpec = Record; @@ -95,7 +95,7 @@ export class Field implements IFieldType { let format = spec.format; - if (!fieldFormats.FieldFormat.isInstanceOfFieldFormat(format)) { + if (!FieldFormat.isInstanceOfFieldFormat(format)) { const fieldFormatsService = getFieldFormats(); format = diff --git a/src/plugins/data/public/index_patterns/index_patterns/format_hit.ts b/src/plugins/data/public/index_patterns/index_patterns/format_hit.ts index f9e15c8650ce0..9b18fb98f3e02 100644 --- a/src/plugins/data/public/index_patterns/index_patterns/format_hit.ts +++ b/src/plugins/data/public/index_patterns/index_patterns/format_hit.ts @@ -19,7 +19,7 @@ import _ from 'lodash'; import { IndexPattern } from './index_pattern'; -import { fieldFormats } from '../../../common'; +import { FieldFormatsContentType } from '../../../common'; const formattedCache = new WeakMap(); const partialFormattedCache = new WeakMap(); @@ -31,7 +31,7 @@ export function formatHitProvider(indexPattern: IndexPattern, defaultFormat: any hit: Record, val: any, fieldName: string, - type: fieldFormats.ContentType = 'html' + type: FieldFormatsContentType = 'html' ) { const field = indexPattern.fields.getByName(fieldName); const format = field ? field.format : defaultFormat; diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts index 059df6d4eb0f8..103f9d385b3f9 100644 --- a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts +++ b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts @@ -31,7 +31,7 @@ import { setNotifications, setFieldFormats } from '../../services'; // Temporary disable eslint, will be removed after moving to new platform folder // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { notificationServiceMock } from '../../../../../core/public/notifications/notifications_service.mock'; -import { fieldFormats } from '../../../common/field_formats'; +import { FieldFormatsRegistry } from '../../../common/field_formats'; jest.mock('../../../../kibana_utils/public', () => { const originalModule = jest.requireActual('../../../../kibana_utils/public'); @@ -125,7 +125,7 @@ describe('IndexPattern', () => { setNotifications(notifications); setFieldFormats(({ getDefaultInstance: jest.fn(), - } as unknown) as fieldFormats.FieldFormatsRegistry); + } as unknown) as FieldFormatsRegistry); return create(indexPatternId).then((pattern: IndexPattern) => { indexPattern = pattern; diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index 726cd6cfb18f0..0a093644c3939 100644 --- a/src/plugins/data/public/mocks.ts +++ b/src/plugins/data/public/mocks.ts @@ -18,10 +18,10 @@ */ import { Plugin, - FieldFormatsStart, - FieldFormatsSetup, + DataPublicPluginSetup, + DataPublicPluginStart, IndexPatternsContract, - fieldFormats, + IFieldFormatsRegistry, } from '.'; import { searchSetupMock } from './search/mocks'; import { queryServiceMock } from './query/mocks'; @@ -35,7 +35,7 @@ const autocompleteMock: any = { hasQuerySuggestions: jest.fn(), }; -const fieldFormatsMock: PublicMethodsOf = { +const fieldFormatsMock: IFieldFormatsRegistry = { getByFieldType: jest.fn(), getDefaultConfig: jest.fn(), getDefaultInstance: jest.fn() as any, @@ -56,7 +56,7 @@ const createSetupContract = (): Setup => { const setupContract = { autocomplete: autocompleteMock, search: searchSetupMock, - fieldFormats: fieldFormatsMock as FieldFormatsSetup, + fieldFormats: fieldFormatsMock as DataPublicPluginSetup['fieldFormats'], query: querySetupMock, __LEGACY: { esClient: { @@ -84,7 +84,7 @@ const createStartContract = (): Start => { }, }, }, - fieldFormats: fieldFormatsMock as FieldFormatsStart, + fieldFormats: fieldFormatsMock as DataPublicPluginStart['fieldFormats'], query: queryStartMock, ui: { IndexPatternSelect: jest.fn(), diff --git a/src/plugins/data/public/search/search_source/search_source.ts b/src/plugins/data/public/search/search_source/search_source.ts index 7b1a4533dd1c6..1ebf9a8ca534c 100644 --- a/src/plugins/data/public/search/search_source/search_source.ts +++ b/src/plugins/data/public/search/search_source/search_source.ts @@ -73,11 +73,12 @@ import _ from 'lodash'; import { normalizeSortRequest } from './normalize_sort_request'; import { filterDocvalueFields } from './filter_docvalue_fields'; import { fieldWildcardFilter } from '../../../../kibana_utils/public'; -import { fieldFormats, esFilters, esQuery, SearchRequest } from '../..'; +import { esFilters, esQuery, SearchRequest } from '../..'; import { SearchSourceOptions, SearchSourceFields } from './types'; import { fetchSoon, FetchOptions, RequestFailure } from '../fetch'; import { getSearchService, getUiSettings, getInjectedMetadata } from '../../services'; +import { getHighlightRequest } from '../../../common'; export type ISearchSource = Pick; @@ -382,10 +383,7 @@ export class SearchSource { body.query = esQuery.buildEsQuery(index, query, filters, esQueryConfigs); if (highlightAll && body.query) { - body.highlight = fieldFormats.getHighlightRequest( - body.query, - getUiSettings().get('doc_table:highlight') - ); + body.highlight = getHighlightRequest(body.query, getUiSettings().get('doc_table:highlight')); delete searchRequest.highlightAll; } diff --git a/src/plugins/data/public/services.ts b/src/plugins/data/public/services.ts index 6a15893f573d8..2af87d84b780e 100644 --- a/src/plugins/data/public/services.ts +++ b/src/plugins/data/public/services.ts @@ -19,7 +19,7 @@ import { NotificationsStart } from 'src/core/public'; import { CoreSetup, CoreStart } from 'kibana/public'; -import { FieldFormatsStart } from '.'; +import { FieldFormatsStart } from './field_formats'; import { createGetterSetter } from '../../kibana_utils/public'; import { IndexPatternsContract } from './index_patterns'; import { DataPublicPluginStart } from './types'; diff --git a/src/plugins/data/public/ui/index.ts b/src/plugins/data/public/ui/index.ts index 0755363c9b16b..5a1ad9957d7d7 100644 --- a/src/plugins/data/public/ui/index.ts +++ b/src/plugins/data/public/ui/index.ts @@ -21,7 +21,7 @@ export { SuggestionsComponent } from './typeahead/suggestions_component'; export { IndexPatternSelect } from './index_pattern_select'; export { FilterBar } from './filter_bar'; export { QueryStringInput } from './query_string_input/query_string_input'; -export { SearchBar, SearchBarProps } from './search_bar'; +export { SearchBar, SearchBarProps, StatefulSearchBarProps } from './search_bar'; // @internal export { diff --git a/src/plugins/data/public/ui/search_bar/create_search_bar.tsx b/src/plugins/data/public/ui/search_bar/create_search_bar.tsx index 71d76f4db49e2..c24c20bd08fb8 100644 --- a/src/plugins/data/public/ui/search_bar/create_search_bar.tsx +++ b/src/plugins/data/public/ui/search_bar/create_search_bar.tsx @@ -132,6 +132,10 @@ export function createSearchBar({ core, storage, data }: StatefulSearchBarDeps) filterManager: data.query.filterManager, }); const { timeRange, refreshInterval } = useTimefilter({ + dateRangeFrom: props.dateRangeFrom, + dateRangeTo: props.dateRangeTo, + refreshInterval: props.refreshInterval, + isRefreshPaused: props.isRefreshPaused, timefilter: data.query.timefilter.timefilter, }); diff --git a/src/plugins/data/public/ui/search_bar/index.tsx b/src/plugins/data/public/ui/search_bar/index.tsx index 4aa7f5fe2b040..fbc9f4a41ebbf 100644 --- a/src/plugins/data/public/ui/search_bar/index.tsx +++ b/src/plugins/data/public/ui/search_bar/index.tsx @@ -18,3 +18,4 @@ */ export { SearchBar, SearchBarProps } from './search_bar'; +export { StatefulSearchBarProps } from './create_search_bar'; diff --git a/src/plugins/data/public/ui/search_bar/lib/use_timefilter.ts b/src/plugins/data/public/ui/search_bar/lib/use_timefilter.ts index 942902ebd7286..b56c717df4978 100644 --- a/src/plugins/data/public/ui/search_bar/lib/use_timefilter.ts +++ b/src/plugins/data/public/ui/search_bar/lib/use_timefilter.ts @@ -19,15 +19,27 @@ import { useState, useEffect } from 'react'; import { Subscription } from 'rxjs'; -import { DataPublicPluginStart } from 'src/plugins/data/public'; +import { DataPublicPluginStart, TimeRange, RefreshInterval } from 'src/plugins/data/public'; interface UseTimefilterProps { + dateRangeFrom?: string; + dateRangeTo?: string; + refreshInterval?: number; + isRefreshPaused?: boolean; timefilter: DataPublicPluginStart['query']['timefilter']['timefilter']; } export const useTimefilter = (props: UseTimefilterProps) => { - const [timeRange, setTimerange] = useState(props.timefilter.getTime()); - const [refreshInterval, setRefreshInterval] = useState(props.timefilter.getRefreshInterval()); + const initialTimeRange: TimeRange = { + from: props.dateRangeFrom || props.timefilter.getTime().from, + to: props.dateRangeTo || props.timefilter.getTime().to, + }; + const initialRefreshInterval: RefreshInterval = { + value: props.refreshInterval || props.timefilter.getRefreshInterval().value, + pause: props.isRefreshPaused || props.timefilter.getRefreshInterval().pause, + }; + const [timeRange, setTimerange] = useState(initialTimeRange); + const [refreshInterval, setRefreshInterval] = useState(initialRefreshInterval); useEffect(() => { const subscriptions = new Subscription(); diff --git a/src/plugins/data/public/ui/search_bar/search_bar.tsx b/src/plugins/data/public/ui/search_bar/search_bar.tsx index 2f0cdb322912b..8d2219bc5731f 100644 --- a/src/plugins/data/public/ui/search_bar/search_bar.tsx +++ b/src/plugins/data/public/ui/search_bar/search_bar.tsx @@ -47,13 +47,8 @@ interface SearchBarInjectedDeps { timeHistory: TimeHistoryContract; // Filter bar onFiltersUpdated?: (filters: esFilters.Filter[]) => void; - // Date picker - dateRangeFrom?: string; - dateRangeTo?: string; // Autorefresh onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; - isRefreshPaused?: boolean; - refreshInterval?: number; } export interface SearchBarOwnProps { @@ -69,6 +64,11 @@ export interface SearchBarOwnProps { showDatePicker?: boolean; showAutoRefreshOnly?: boolean; filters?: esFilters.Filter[]; + // Date picker + isRefreshPaused?: boolean; + refreshInterval?: number; + dateRangeFrom?: string; + dateRangeTo?: string; // Query bar - should be in SearchBarInjectedDeps query?: Query; // Show when user has privileges to save diff --git a/src/plugins/data/server/field_formats/field_formats_service.ts b/src/plugins/data/server/field_formats/field_formats_service.ts index 923904db9def0..a31e5927ab800 100644 --- a/src/plugins/data/server/field_formats/field_formats_service.ts +++ b/src/plugins/data/server/field_formats/field_formats_service.ts @@ -17,16 +17,15 @@ * under the License. */ import { has } from 'lodash'; -import { fieldFormats } from '../../common/field_formats'; +import { FieldFormatsRegistry, IFieldFormatType, baseFormatters } from '../../common/field_formats'; import { IUiSettingsClient } from '../../../../core/server'; export class FieldFormatsService { - private readonly fieldFormatClasses: fieldFormats.IFieldFormatType[] = - fieldFormats.baseFormatters; + private readonly fieldFormatClasses: IFieldFormatType[] = baseFormatters; public setup() { return { - register: (customFieldFormat: fieldFormats.IFieldFormatType) => + register: (customFieldFormat: IFieldFormatType) => this.fieldFormatClasses.push(customFieldFormat), }; } @@ -34,7 +33,7 @@ export class FieldFormatsService { public start() { return { fieldFormatServiceFactory: async (uiSettings: IUiSettingsClient) => { - const fieldFormatsRegistry = new fieldFormats.FieldFormatsRegistry(); + const fieldFormatsRegistry = new FieldFormatsRegistry(); const uiConfigs = await uiSettings.getAll(); const registeredUiSettings = uiSettings.getRegistered(); diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 45bd111a2ce4f..1dc8528dbba67 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -20,6 +20,50 @@ import { PluginInitializerContext } from '../../../core/server'; import { DataServerPlugin, DataPluginSetup, DataPluginStart } from './plugin'; +/* + * Field Formatters helper namespace: + */ + +import { + FieldFormat, + FieldFormatsRegistry, // exported only for tests. Consider mock. + BoolFormat, + BytesFormat, + ColorFormat, + DateFormat, + DateNanosFormat, + DurationFormat, + IpFormat, + NumberFormat, + PercentFormat, + RelativeDateFormat, + SourceFormat, + StaticLookupFormat, + UrlFormat, + StringFormat, + TruncateFormat, +} from '../common/field_formats'; + +export const fieldFormats = { + FieldFormat, + FieldFormatsRegistry, // exported only for tests. Consider mock. + + BoolFormat, + BytesFormat, + ColorFormat, + DateFormat, + DateNanosFormat, + DurationFormat, + IpFormat, + NumberFormat, + PercentFormat, + RelativeDateFormat, + SourceFormat, + StaticLookupFormat, + UrlFormat, + StringFormat, + TruncateFormat, +}; export function plugin(initializerContext: PluginInitializerContext) { return new DataServerPlugin(initializerContext); } @@ -35,7 +79,6 @@ export { esFilters, esKuery, esQuery, - fieldFormats, // kbn field types castEsToKbnFieldTypeName, getKbnFieldType, @@ -56,6 +99,9 @@ export { // utils parseInterval, isNestedField, + IFieldFormatsRegistry, + FieldFormatsGetConfigFn, + FieldFormatConfig, } from '../common'; /** diff --git a/src/legacy/ui/public/registry/feature_catalogue.d.ts b/src/plugins/home/public/mocks/index.ts similarity index 54% rename from src/legacy/ui/public/registry/feature_catalogue.d.ts rename to src/plugins/home/public/mocks/index.ts index 031c3efa6c5ad..dead50230ec85 100644 --- a/src/legacy/ui/public/registry/feature_catalogue.d.ts +++ b/src/plugins/home/public/mocks/index.ts @@ -17,26 +17,22 @@ * under the License. */ -import { I18nServiceType } from '@kbn/i18n/angular'; +import { featureCatalogueRegistryMock } from '../services/feature_catalogue/feature_catalogue_registry.mock'; +import { environmentServiceMock } from '../services/environment/environment.mock'; +import { configSchema } from '../../config'; -export enum FeatureCatalogueCategory { - ADMIN = 'admin', - DATA = 'data', - OTHER = 'other', -} +const createSetupContract = () => ({ + featureCatalogue: featureCatalogueRegistryMock.createSetup(), + environment: environmentServiceMock.createSetup(), + config: configSchema.validate({}), +}); -interface FeatureCatalogueObject { - id: string; - title: string; - description: string; - icon: string; - path: string; - showOnHomePage: boolean; - category: FeatureCatalogueCategory; -} +const createStartContract = () => ({ + featureCatalogue: featureCatalogueRegistryMock.createStart(), + environment: environmentServiceMock.createStart(), +}); -type FeatureCatalogueRegistryFunction = (i18n: I18nServiceType) => FeatureCatalogueObject; - -export const FeatureCatalogueRegistryProvider: { - register: (fn: FeatureCatalogueRegistryFunction) => void; +export const homePluginMock = { + createSetupContract, + createStartContract, }; diff --git a/src/plugins/kibana_legacy/public/angular_bootstrap/bind_html/bind_html.js b/src/plugins/kibana_legacy/public/angular_bootstrap/bind_html/bind_html.js new file mode 100755 index 0000000000000..77844a3dd1363 --- /dev/null +++ b/src/plugins/kibana_legacy/public/angular_bootstrap/bind_html/bind_html.js @@ -0,0 +1,17 @@ +/* eslint-disable */ + +import angular from 'angular'; + +export function initBindHtml() { + angular + .module('ui.bootstrap.bindHtml', []) + + .directive('bindHtmlUnsafe', function() { + return function(scope, element, attr) { + element.addClass('ng-binding').data('$binding', attr.bindHtmlUnsafe); + scope.$watch(attr.bindHtmlUnsafe, function bindHtmlUnsafeWatchAction(value) { + element.html(value || ''); + }); + }; + }); +} diff --git a/src/plugins/kibana_legacy/public/angular_bootstrap/index.ts b/src/plugins/kibana_legacy/public/angular_bootstrap/index.ts new file mode 100644 index 0000000000000..1f15107a02762 --- /dev/null +++ b/src/plugins/kibana_legacy/public/angular_bootstrap/index.ts @@ -0,0 +1,50 @@ +/* eslint-disable */ + +import { once } from 'lodash'; +import angular from 'angular'; + +// @ts-ignore +import { initBindHtml } from './bind_html/bind_html'; +// @ts-ignore +import { initBootstrapTooltip } from './tooltip/tooltip'; + +import tooltipPopup from './tooltip/tooltip_popup.html'; + +import tooltipUnsafePopup from './tooltip/tooltip_html_unsafe_popup.html'; + +export const initAngularBootstrap = once(() => { + /* + * angular-ui-bootstrap + * http://angular-ui.github.io/bootstrap/ + + * Version: 0.12.1 - 2015-02-20 + * License: MIT + */ + angular.module('ui.bootstrap', [ + 'ui.bootstrap.tpls', + 'ui.bootstrap.bindHtml', + 'ui.bootstrap.tooltip', + ]); + + angular.module('ui.bootstrap.tpls', [ + 'template/tooltip/tooltip-html-unsafe-popup.html', + 'template/tooltip/tooltip-popup.html', + ]); + + initBindHtml(); + initBootstrapTooltip(); + + angular.module('template/tooltip/tooltip-html-unsafe-popup.html', []).run([ + '$templateCache', + function($templateCache: any) { + $templateCache.put('template/tooltip/tooltip-html-unsafe-popup.html', tooltipUnsafePopup); + }, + ]); + + angular.module('template/tooltip/tooltip-popup.html', []).run([ + '$templateCache', + function($templateCache: any) { + $templateCache.put('template/tooltip/tooltip-popup.html', tooltipPopup); + }, + ]); +}); diff --git a/src/plugins/kibana_legacy/public/angular_bootstrap/tooltip/position.js b/src/plugins/kibana_legacy/public/angular_bootstrap/tooltip/position.js new file mode 100755 index 0000000000000..24c8a8c5979cd --- /dev/null +++ b/src/plugins/kibana_legacy/public/angular_bootstrap/tooltip/position.js @@ -0,0 +1,167 @@ +/* eslint-disable */ + +import angular from 'angular'; + +export function initBootstrapPosition() { + angular + .module('ui.bootstrap.position', []) + + /** + * A set of utility methods that can be use to retrieve position of DOM elements. + * It is meant to be used where we need to absolute-position DOM elements in + * relation to other, existing elements (this is the case for tooltips, popovers, + * typeahead suggestions etc.). + */ + .factory('$position', [ + '$document', + '$window', + function($document, $window) { + function getStyle(el, cssprop) { + if (el.currentStyle) { + //IE + return el.currentStyle[cssprop]; + } else if ($window.getComputedStyle) { + return $window.getComputedStyle(el)[cssprop]; + } + // finally try and get inline style + return el.style[cssprop]; + } + + /** + * Checks if a given element is statically positioned + * @param element - raw DOM element + */ + function isStaticPositioned(element) { + return (getStyle(element, 'position') || 'static') === 'static'; + } + + /** + * returns the closest, non-statically positioned parentOffset of a given element + * @param element + */ + const parentOffsetEl = function(element) { + const docDomEl = $document[0]; + let offsetParent = element.offsetParent || docDomEl; + while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent)) { + offsetParent = offsetParent.offsetParent; + } + return offsetParent || docDomEl; + }; + + return { + /** + * Provides read-only equivalent of jQuery's position function: + * http://api.jquery.com/position/ + */ + position: function(element) { + const elBCR = this.offset(element); + let offsetParentBCR = { top: 0, left: 0 }; + const offsetParentEl = parentOffsetEl(element[0]); + if (offsetParentEl != $document[0]) { + offsetParentBCR = this.offset(angular.element(offsetParentEl)); + offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop; + offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft; + } + + const boundingClientRect = element[0].getBoundingClientRect(); + return { + width: boundingClientRect.width || element.prop('offsetWidth'), + height: boundingClientRect.height || element.prop('offsetHeight'), + top: elBCR.top - offsetParentBCR.top, + left: elBCR.left - offsetParentBCR.left, + }; + }, + + /** + * Provides read-only equivalent of jQuery's offset function: + * http://api.jquery.com/offset/ + */ + offset: function(element) { + const boundingClientRect = element[0].getBoundingClientRect(); + return { + width: boundingClientRect.width || element.prop('offsetWidth'), + height: boundingClientRect.height || element.prop('offsetHeight'), + top: + boundingClientRect.top + + ($window.pageYOffset || $document[0].documentElement.scrollTop), + left: + boundingClientRect.left + + ($window.pageXOffset || $document[0].documentElement.scrollLeft), + }; + }, + + /** + * Provides coordinates for the targetEl in relation to hostEl + */ + positionElements: function(hostEl, targetEl, positionStr, appendToBody) { + const positionStrParts = positionStr.split('-'); + const pos0 = positionStrParts[0]; + const pos1 = positionStrParts[1] || 'center'; + + let hostElPos; + let targetElWidth; + let targetElHeight; + let targetElPos; + + hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl); + + targetElWidth = targetEl.prop('offsetWidth'); + targetElHeight = targetEl.prop('offsetHeight'); + + const shiftWidth = { + center: function() { + return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2; + }, + left: function() { + return hostElPos.left; + }, + right: function() { + return hostElPos.left + hostElPos.width; + }, + }; + + const shiftHeight = { + center: function() { + return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2; + }, + top: function() { + return hostElPos.top; + }, + bottom: function() { + return hostElPos.top + hostElPos.height; + }, + }; + + switch (pos0) { + case 'right': + targetElPos = { + top: shiftHeight[pos1](), + left: shiftWidth[pos0](), + }; + break; + case 'left': + targetElPos = { + top: shiftHeight[pos1](), + left: hostElPos.left - targetElWidth, + }; + break; + case 'bottom': + targetElPos = { + top: shiftHeight[pos0](), + left: shiftWidth[pos1](), + }; + break; + default: + targetElPos = { + top: hostElPos.top - targetElHeight, + left: shiftWidth[pos1](), + }; + break; + } + + return targetElPos; + }, + }; + }, + ]); +} diff --git a/src/plugins/kibana_legacy/public/angular_bootstrap/tooltip/tooltip.js b/src/plugins/kibana_legacy/public/angular_bootstrap/tooltip/tooltip.js new file mode 100755 index 0000000000000..05235fde9419b --- /dev/null +++ b/src/plugins/kibana_legacy/public/angular_bootstrap/tooltip/tooltip.js @@ -0,0 +1,423 @@ +/* eslint-disable */ + +import angular from 'angular'; + +import { initBootstrapPosition } from './position'; + +export function initBootstrapTooltip() { + initBootstrapPosition(); + /** + * The following features are still outstanding: animation as a + * function, placement as a function, inside, support for more triggers than + * just mouse enter/leave, html tooltips, and selector delegation. + */ + angular + .module('ui.bootstrap.tooltip', ['ui.bootstrap.position']) + + /** + * The $tooltip service creates tooltip- and popover-like directives as well as + * houses global options for them. + */ + .provider('$tooltip', function() { + // The default options tooltip and popover. + const defaultOptions = { + placement: 'top', + animation: true, + popupDelay: 0, + }; + + // Default hide triggers for each show trigger + const triggerMap = { + mouseenter: 'mouseleave', + click: 'click', + focus: 'blur', + }; + + // The options specified to the provider globally. + const globalOptions = {}; + + /** + * `options({})` allows global configuration of all tooltips in the + * application. + * + * var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) { + * // place tooltips left instead of top by default + * $tooltipProvider.options( { placement: 'left' } ); + * }); + */ + this.options = function(value) { + angular.extend(globalOptions, value); + }; + + /** + * This allows you to extend the set of trigger mappings available. E.g.: + * + * $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' ); + */ + this.setTriggers = function setTriggers(triggers) { + angular.extend(triggerMap, triggers); + }; + + /** + * This is a helper function for translating camel-case to snake-case. + */ + function snake_case(name) { + const regexp = /[A-Z]/g; + const separator = '-'; + return name.replace(regexp, function(letter, pos) { + return (pos ? separator : '') + letter.toLowerCase(); + }); + } + + /** + * Returns the actual instance of the $tooltip service. + * TODO support multiple triggers + */ + this.$get = [ + '$window', + '$compile', + '$timeout', + '$document', + '$position', + '$interpolate', + function($window, $compile, $timeout, $document, $position, $interpolate) { + return function $tooltip(type, prefix, defaultTriggerShow) { + const options = angular.extend({}, defaultOptions, globalOptions); + + /** + * Returns an object of show and hide triggers. + * + * If a trigger is supplied, + * it is used to show the tooltip; otherwise, it will use the `trigger` + * option passed to the `$tooltipProvider.options` method; else it will + * default to the trigger supplied to this directive factory. + * + * The hide trigger is based on the show trigger. If the `trigger` option + * was passed to the `$tooltipProvider.options` method, it will use the + * mapped trigger from `triggerMap` or the passed trigger if the map is + * undefined; otherwise, it uses the `triggerMap` value of the show + * trigger; else it will just use the show trigger. + */ + function getTriggers(trigger) { + const show = trigger || options.trigger || defaultTriggerShow; + const hide = triggerMap[show] || show; + return { + show: show, + hide: hide, + }; + } + + const directiveName = snake_case(type); + + const startSym = $interpolate.startSymbol(); + const endSym = $interpolate.endSymbol(); + const template = + '
' + + '
'; + + return { + restrict: 'EA', + compile: function(tElem, tAttrs) { + const tooltipLinker = $compile(template); + + return function link(scope, element, attrs) { + let tooltip; + let tooltipLinkedScope; + let transitionTimeout; + let popupTimeout; + let appendToBody = angular.isDefined(options.appendToBody) + ? options.appendToBody + : false; + let triggers = getTriggers(undefined); + const hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']); + let ttScope = scope.$new(true); + + const positionTooltip = function() { + const ttPosition = $position.positionElements( + element, + tooltip, + ttScope.placement, + appendToBody + ); + ttPosition.top += 'px'; + ttPosition.left += 'px'; + + // Now set the calculated positioning. + tooltip.css(ttPosition); + }; + + // By default, the tooltip is not open. + // TODO add ability to start tooltip opened + ttScope.isOpen = false; + + function toggleTooltipBind() { + if (!ttScope.isOpen) { + showTooltipBind(); + } else { + hideTooltipBind(); + } + } + + // Show the tooltip with delay if specified, otherwise show it immediately + function showTooltipBind() { + if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) { + return; + } + + prepareTooltip(); + + if (ttScope.popupDelay) { + // Do nothing if the tooltip was already scheduled to pop-up. + // This happens if show is triggered multiple times before any hide is triggered. + if (!popupTimeout) { + popupTimeout = $timeout(show, ttScope.popupDelay, false); + popupTimeout + .then(reposition => reposition()) + .catch(error => { + // if the timeout is canceled then the string `canceled` is thrown. To prevent + // this from triggering an 'unhandled promise rejection' in angular 1.5+ the + // $timeout service explicitly tells $q that the promise it generated is "handled" + // but that does not include down chain promises like the one created by calling + // `popupTimeout.then()`. Because of this we need to ignore the "canceled" string + // and only propagate real errors + if (error !== 'canceled') { + throw error; + } + }); + } + } else { + show()(); + } + } + + function hideTooltipBind() { + scope.$evalAsync(function() { + hide(); + }); + } + + // Show the tooltip popup element. + function show() { + popupTimeout = null; + + // If there is a pending remove transition, we must cancel it, lest the + // tooltip be mysteriously removed. + if (transitionTimeout) { + $timeout.cancel(transitionTimeout); + transitionTimeout = null; + } + + // Don't show empty tooltips. + if (!ttScope.content) { + return angular.noop; + } + + createTooltip(); + + // Set the initial positioning. + tooltip.css({ top: 0, left: 0, display: 'block' }); + ttScope.$digest(); + + positionTooltip(); + + // And show the tooltip. + ttScope.isOpen = true; + ttScope.$digest(); // digest required as $apply is not called + + // Return positioning function as promise callback for correct + // positioning after draw. + return positionTooltip; + } + + // Hide the tooltip popup element. + function hide() { + // First things first: we don't show it anymore. + ttScope.isOpen = false; + + //if tooltip is going to be shown after delay, we must cancel this + $timeout.cancel(popupTimeout); + popupTimeout = null; + + // And now we remove it from the DOM. However, if we have animation, we + // need to wait for it to expire beforehand. + // FIXME: this is a placeholder for a port of the transitions library. + if (ttScope.animation) { + if (!transitionTimeout) { + transitionTimeout = $timeout(removeTooltip, 500); + } + } else { + removeTooltip(); + } + } + + function createTooltip() { + // There can only be one tooltip element per directive shown at once. + if (tooltip) { + removeTooltip(); + } + tooltipLinkedScope = ttScope.$new(); + tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) { + if (appendToBody) { + $document.find('body').append(tooltip); + } else { + element.after(tooltip); + } + }); + } + + function removeTooltip() { + transitionTimeout = null; + if (tooltip) { + tooltip.remove(); + tooltip = null; + } + if (tooltipLinkedScope) { + tooltipLinkedScope.$destroy(); + tooltipLinkedScope = null; + } + } + + function prepareTooltip() { + prepPlacement(); + prepPopupDelay(); + } + + /** + * Observe the relevant attributes. + */ + attrs.$observe(type, function(val) { + ttScope.content = val; + + if (!val && ttScope.isOpen) { + hide(); + } + }); + + attrs.$observe(prefix + 'Title', function(val) { + ttScope.title = val; + }); + + function prepPlacement() { + const val = attrs[prefix + 'Placement']; + ttScope.placement = angular.isDefined(val) ? val : options.placement; + } + + function prepPopupDelay() { + const val = attrs[prefix + 'PopupDelay']; + const delay = parseInt(val, 10); + ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay; + } + + const unregisterTriggers = function() { + element.unbind(triggers.show, showTooltipBind); + element.unbind(triggers.hide, hideTooltipBind); + }; + + function prepTriggers() { + const val = attrs[prefix + 'Trigger']; + unregisterTriggers(); + + triggers = getTriggers(val); + + if (triggers.show === triggers.hide) { + element.bind(triggers.show, toggleTooltipBind); + } else { + element.bind(triggers.show, showTooltipBind); + element.bind(triggers.hide, hideTooltipBind); + } + } + + prepTriggers(); + + const animation = scope.$eval(attrs[prefix + 'Animation']); + ttScope.animation = angular.isDefined(animation) + ? !!animation + : options.animation; + + const appendToBodyVal = scope.$eval(attrs[prefix + 'AppendToBody']); + appendToBody = angular.isDefined(appendToBodyVal) + ? appendToBodyVal + : appendToBody; + + // if a tooltip is attached to we need to remove it on + // location change as its parent scope will probably not be destroyed + // by the change. + if (appendToBody) { + scope.$on( + '$locationChangeSuccess', + function closeTooltipOnLocationChangeSuccess() { + if (ttScope.isOpen) { + hide(); + } + } + ); + } + + // Make sure tooltip is destroyed and removed. + scope.$on('$destroy', function onDestroyTooltip() { + $timeout.cancel(transitionTimeout); + $timeout.cancel(popupTimeout); + unregisterTriggers(); + removeTooltip(); + ttScope = null; + }); + }; + }, + }; + }; + }, + ]; + }) + + .directive('tooltip', [ + '$tooltip', + function($tooltip) { + return $tooltip('tooltip', 'tooltip', 'mouseenter'); + }, + ]) + + .directive('tooltipPopup', function() { + return { + restrict: 'EA', + replace: true, + scope: { content: '@', placement: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/tooltip/tooltip-popup.html', + }; + }) + + .directive('tooltipHtmlUnsafe', [ + '$tooltip', + function($tooltip) { + return $tooltip('tooltipHtmlUnsafe', 'tooltip', 'mouseenter'); + }, + ]) + + .directive('tooltipHtmlUnsafePopup', function() { + return { + restrict: 'EA', + replace: true, + scope: { content: '@', placement: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html', + }; + }); +} diff --git a/src/legacy/ui/public/angular-bootstrap/tooltip/tooltip-html-unsafe-popup.html b/src/plugins/kibana_legacy/public/angular_bootstrap/tooltip/tooltip_html_unsafe_popup.html similarity index 100% rename from src/legacy/ui/public/angular-bootstrap/tooltip/tooltip-html-unsafe-popup.html rename to src/plugins/kibana_legacy/public/angular_bootstrap/tooltip/tooltip_html_unsafe_popup.html diff --git a/src/legacy/ui/public/angular-bootstrap/tooltip/tooltip-popup.html b/src/plugins/kibana_legacy/public/angular_bootstrap/tooltip/tooltip_popup.html similarity index 100% rename from src/legacy/ui/public/angular-bootstrap/tooltip/tooltip-popup.html rename to src/plugins/kibana_legacy/public/angular_bootstrap/tooltip/tooltip_popup.html diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_config.js b/src/plugins/kibana_legacy/public/dashboard_config.ts similarity index 69% rename from src/legacy/core_plugins/kibana/public/dashboard/dashboard_config.js rename to src/plugins/kibana_legacy/public/dashboard_config.ts index aa8333a1bafca..3c7670682ce25 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_config.js +++ b/src/plugins/kibana_legacy/public/dashboard_config.ts @@ -17,11 +17,13 @@ * under the License. */ -import { uiModules } from 'ui/modules'; -import { capabilities } from 'ui/capabilities'; +export interface DashboardConfig { + turnHideWriteControlsOn(): void; + getHideWriteControls(): boolean; +} -export function dashboardConfigProvider() { - let hideWriteControls = !capabilities.get().dashboard.showWriteControls; +export function getDashboardConfig(hideWriteControls: boolean): DashboardConfig { + let _hideWriteControls = hideWriteControls; return { /** @@ -29,16 +31,10 @@ export function dashboardConfigProvider() { * @type {boolean} */ turnHideWriteControlsOn() { - hideWriteControls = true; + _hideWriteControls = true; }, - $get() { - return { - getHideWriteControls() { - return hideWriteControls; - }, - }; + getHideWriteControls() { + return _hideWriteControls; }, }; } - -uiModules.get('kibana').provider('dashboardConfig', dashboardConfigProvider); diff --git a/src/plugins/kibana_legacy/public/index.ts b/src/plugins/kibana_legacy/public/index.ts index 6e7a3cf87b87c..19833d638fe4c 100644 --- a/src/plugins/kibana_legacy/public/index.ts +++ b/src/plugins/kibana_legacy/public/index.ts @@ -24,6 +24,8 @@ export const plugin = (initializerContext: PluginInitializerContext) => new KibanaLegacyPlugin(initializerContext); export * from './plugin'; + +export { initAngularBootstrap } from './angular_bootstrap'; export * from './angular'; export * from './notify'; export * from './utils'; diff --git a/src/plugins/kibana_legacy/public/mocks.ts b/src/plugins/kibana_legacy/public/mocks.ts index b6287dd9d9a55..aab3ab315f0c6 100644 --- a/src/plugins/kibana_legacy/public/mocks.ts +++ b/src/plugins/kibana_legacy/public/mocks.ts @@ -36,6 +36,10 @@ const createStartContract = (): Start => ({ config: { defaultAppId: 'home', }, + dashboardConfig: { + turnHideWriteControlsOn: jest.fn(), + getHideWriteControls: jest.fn(), + }, }); export const kibanaLegacyPluginMock = { diff --git a/src/plugins/kibana_legacy/public/plugin.ts b/src/plugins/kibana_legacy/public/plugin.ts index 7c4b3428cbb6d..86e56c44646c0 100644 --- a/src/plugins/kibana_legacy/public/plugin.ts +++ b/src/plugins/kibana_legacy/public/plugin.ts @@ -17,9 +17,16 @@ * under the License. */ -import { App, AppBase, PluginInitializerContext, AppUpdatableFields } from 'kibana/public'; +import { + App, + AppBase, + PluginInitializerContext, + AppUpdatableFields, + CoreStart, +} from 'kibana/public'; import { Observable } from 'rxjs'; import { ConfigSchema } from '../config'; +import { getDashboardConfig } from './dashboard_config'; interface ForwardDefinition { legacyAppId: string; @@ -104,7 +111,7 @@ export class KibanaLegacyPlugin { }; } - public start() { + public start({ application }: CoreStart) { return { /** * @deprecated @@ -117,6 +124,7 @@ export class KibanaLegacyPlugin { */ getForwards: () => this.forwards, config: this.initializerContext.config.get(), + dashboardConfig: getDashboardConfig(!application.capabilities.dashboard.showWriteControls), }; } } diff --git a/src/plugins/kibana_legacy/server/index.ts b/src/plugins/kibana_legacy/server/index.ts index ca8ad6410eec3..4d0fe8364a66c 100644 --- a/src/plugins/kibana_legacy/server/index.ts +++ b/src/plugins/kibana_legacy/server/index.ts @@ -28,7 +28,7 @@ export const config: PluginConfigDescriptor = { schema: configSchema, deprecations: ({ renameFromRoot }) => [ // TODO: Remove deprecation once defaultAppId is deleted - renameFromRoot('kibana.defaultAppId', 'kibanaLegacy.defaultAppId', true), + renameFromRoot('kibana.defaultAppId', 'kibana_legacy.defaultAppId', true), ], }; diff --git a/src/plugins/management/kibana.json b/src/plugins/management/kibana.json index 4bbf2039c8f38..1789b7cd5ddba 100644 --- a/src/plugins/management/kibana.json +++ b/src/plugins/management/kibana.json @@ -3,5 +3,5 @@ "version": "kibana", "server": false, "ui": true, - "requiredPlugins": ["kibanaLegacy"] + "requiredPlugins": ["kibanaLegacy", "home"] } diff --git a/src/plugins/management/public/mocks/index.ts b/src/plugins/management/public/mocks/index.ts index cc56928e8e529..6099a2cc32afc 100644 --- a/src/plugins/management/public/mocks/index.ts +++ b/src/plugins/management/public/mocks/index.ts @@ -17,10 +17,26 @@ * under the License. */ -const createStartContract = () => ({ +import { ManagementSetup, ManagementStart } from '../types'; + +const createSetupContract = (): DeeplyMockedKeys => ({ + sections: { + register: jest.fn(), + getSection: jest.fn(), + getAllSections: jest.fn(), + }, +}); + +const createStartContract = (): DeeplyMockedKeys => ({ legacy: {}, + sections: { + getSection: jest.fn(), + getAllSections: jest.fn(), + navigateToApp: jest.fn(), + }, }); export const managementPluginMock = { + createSetupContract, createStartContract, }; diff --git a/src/plugins/management/public/plugin.ts b/src/plugins/management/public/plugin.ts index ce6959ec31345..df2398412dac2 100644 --- a/src/plugins/management/public/plugin.ts +++ b/src/plugins/management/public/plugin.ts @@ -17,10 +17,12 @@ * under the License. */ +import { i18n } from '@kbn/i18n'; import { CoreSetup, CoreStart, Plugin } from 'kibana/public'; import { ManagementSetup, ManagementStart } from './types'; import { ManagementService } from './management_service'; import { KibanaLegacySetup } from '../../kibana_legacy/public'; +import { FeatureCatalogueCategory, HomePublicPluginSetup } from '../../home/public'; // @ts-ignore import { LegacyManagementAdapter } from './legacy'; @@ -28,7 +30,24 @@ export class ManagementPlugin implements Plugin {}, - get: () => { - return []; - }, -}; - const dataShim = { ui: { SearchBar: () =>
, @@ -76,12 +69,7 @@ describe('TopNavMenu', () => { it('Should render search bar', () => { const component = shallowWithIntl( - + ); expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(0); diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx index 849a4b033399e..cf39c82eff3ce 100644 --- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx +++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx @@ -24,10 +24,9 @@ import { I18nProvider } from '@kbn/i18n/react'; import { TopNavMenuData } from './top_nav_menu_data'; import { TopNavMenuItem } from './top_nav_menu_item'; -import { SearchBarProps, DataPublicPluginStart } from '../../../data/public'; +import { StatefulSearchBarProps, DataPublicPluginStart } from '../../../data/public'; -export type TopNavMenuProps = Partial & { - appName: string; +export type TopNavMenuProps = StatefulSearchBarProps & { config?: TopNavMenuData[]; showSearchBar?: boolean; data?: DataPublicPluginStart; diff --git a/x-pack/legacy/plugins/alerting/README.md b/x-pack/legacy/plugins/alerting/README.md index 4de45fe96a400..eb9df042f9254 100644 --- a/x-pack/legacy/plugins/alerting/README.md +++ b/x-pack/legacy/plugins/alerting/README.md @@ -23,6 +23,7 @@ Table of Contents - [`DELETE /api/alert/{id}`: Delete alert](#delete-apialertid-delete-alert) - [`GET /api/alert/_find`: Find alerts](#get-apialertfind-find-alerts) - [`GET /api/alert/{id}`: Get alert](#get-apialertid-get-alert) + - [`GET /api/alert/{id}/state`: Get alert state](#get-apialertidstate-get-alert-state) - [`GET /api/alert/types`: List alert types](#get-apialerttypes-list-alert-types) - [`PUT /api/alert/{id}`: Update alert](#put-apialertid-update-alert) - [`POST /api/alert/{id}/_enable`: Enable an alert](#post-apialertidenable-enable-an-alert) @@ -273,6 +274,14 @@ Params: |---|---|---| |id|The id of the alert you're trying to get.|string| +### `GET /api/alert/{id}/state`: Get alert state + +Params: + +|Property|Description|Type| +|---|---|---| +|id|The id of the alert whose state you're trying to get.|string| + ### `GET /api/alert/types`: List alert types No parameters. diff --git a/x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.test.ts b/x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.test.ts index 6a80f4d2de4cb..c5f93edfb74e5 100644 --- a/x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.test.ts +++ b/x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.test.ts @@ -192,7 +192,7 @@ describe('updateLastScheduledActions()', () => { state: {}, meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', }, }, @@ -216,3 +216,19 @@ describe('toJSON', () => { ); }); }); + +describe('toRaw', () => { + test('returns unserialised underlying state and meta', () => { + const raw = { + state: { foo: true }, + meta: { + lastScheduledActions: { + date: new Date(), + group: 'default', + }, + }, + }; + const alertInstance = new AlertInstance(raw); + expect(alertInstance.toRaw()).toEqual(raw); + }); +}); diff --git a/x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.ts b/x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.ts index a56e2077cdfd8..df67f7d2a1d9e 100644 --- a/x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.ts +++ b/x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.ts @@ -3,34 +3,41 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import * as t from 'io-ts'; import { State, Context } from '../types'; +import { DateFromString } from '../lib/types'; import { parseDuration } from '../lib'; -interface Meta { - lastScheduledActions?: { - group: string; - date: Date; - }; -} - interface ScheduledExecutionOptions { actionGroup: string; context: Context; state: State; } -interface ConstructorOptions { - state?: State; - meta?: Meta; -} +const metaSchema = t.partial({ + lastScheduledActions: t.type({ + group: t.string, + date: DateFromString, + }), +}); +type AlertInstanceMeta = t.TypeOf; + +const stateSchema = t.record(t.string, t.unknown); +type AlertInstanceState = t.TypeOf; + +export const rawAlertInstance = t.partial({ + state: stateSchema, + meta: metaSchema, +}); +export type RawAlertInstance = t.TypeOf; export class AlertInstance { private scheduledExecutionOptions?: ScheduledExecutionOptions; - private meta: Meta; - private state: State; + private meta: AlertInstanceMeta; + private state: AlertInstanceState; - constructor({ state = {}, meta = {} }: ConstructorOptions = {}) { + constructor({ state = {}, meta = {} }: RawAlertInstance = {}) { this.state = state; this.meta = meta; } @@ -48,7 +55,7 @@ export class AlertInstance { if ( this.meta.lastScheduledActions && this.meta.lastScheduledActions.group === actionGroup && - new Date(this.meta.lastScheduledActions.date).getTime() + throttleMills > Date.now() + this.meta.lastScheduledActions.date.getTime() + throttleMills > Date.now() ) { return true; } @@ -89,6 +96,10 @@ export class AlertInstance { * Used to serialize alert instance state */ toJSON() { + return rawAlertInstance.encode(this.toRaw()); + } + + toRaw(): RawAlertInstance { return { state: this.state, meta: this.meta, diff --git a/x-pack/legacy/plugins/alerting/server/alert_instance/create_alert_instance_factory.test.ts b/x-pack/legacy/plugins/alerting/server/alert_instance/create_alert_instance_factory.test.ts index 914f726ebbd78..03bc8b7cc3b14 100644 --- a/x-pack/legacy/plugins/alerting/server/alert_instance/create_alert_instance_factory.test.ts +++ b/x-pack/legacy/plugins/alerting/server/alert_instance/create_alert_instance_factory.test.ts @@ -40,7 +40,7 @@ test('reuses existing instances', () => { Object { "meta": Object { "lastScheduledActions": Object { - "date": 1970-01-01T00:00:00.000Z, + "date": "1970-01-01T00:00:00.000Z", "group": "default", }, }, diff --git a/x-pack/legacy/plugins/alerting/server/alert_instance/index.ts b/x-pack/legacy/plugins/alerting/server/alert_instance/index.ts index 40ee0874e805c..fc828096adf28 100644 --- a/x-pack/legacy/plugins/alerting/server/alert_instance/index.ts +++ b/x-pack/legacy/plugins/alerting/server/alert_instance/index.ts @@ -4,5 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export { AlertInstance } from './alert_instance'; +export { AlertInstance, RawAlertInstance, rawAlertInstance } from './alert_instance'; export { createAlertInstanceFactory } from './create_alert_instance_factory'; diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client.mock.ts b/x-pack/legacy/plugins/alerting/server/alerts_client.mock.ts index c7d359491680f..3189fa214d5f7 100644 --- a/x-pack/legacy/plugins/alerting/server/alerts_client.mock.ts +++ b/x-pack/legacy/plugins/alerting/server/alerts_client.mock.ts @@ -12,6 +12,7 @@ const createAlertsClientMock = () => { const mocked: jest.Mocked = { create: jest.fn(), get: jest.fn(), + getAlertState: jest.fn(), find: jest.fn(), delete: jest.fn(), update: jest.fn(), diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts b/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts index 56ccf08d6a44f..f9d1d97a521fe 100644 --- a/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts +++ b/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts @@ -1356,6 +1356,120 @@ describe('get()', () => { }); }); +describe('getAlertState()', () => { + test('calls saved objects client with given params', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + savedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + + taskManager.get.mockResolvedValueOnce({ + id: '1', + taskType: 'alerting:123', + scheduledAt: new Date(), + attempts: 1, + status: TaskStatus.Idle, + runAt: new Date(), + startedAt: null, + retryAt: null, + state: {}, + params: {}, + ownerId: null, + }); + + await alertsClient.getAlertState({ id: '1' }); + expect(savedObjectsClient.get).toHaveBeenCalledTimes(1); + expect(savedObjectsClient.get.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "alert", + "1", + ] + `); + }); + + test('gets the underlying task from TaskManager', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + + const scheduledTaskId = 'task-123'; + + savedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + enabled: true, + scheduledTaskId, + mutedInstanceIds: [], + muteAll: true, + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + + taskManager.get.mockResolvedValueOnce({ + id: scheduledTaskId, + taskType: 'alerting:123', + scheduledAt: new Date(), + attempts: 1, + status: TaskStatus.Idle, + runAt: new Date(), + startedAt: null, + retryAt: null, + state: {}, + params: { + alertId: '1', + }, + ownerId: null, + }); + + await alertsClient.getAlertState({ id: '1' }); + expect(taskManager.get).toHaveBeenCalledTimes(1); + expect(taskManager.get).toHaveBeenCalledWith(scheduledTaskId); + }); +}); + describe('find()', () => { test('calls saved objects client with given params', async () => { const alertsClient = new AlertsClient(alertsClientParams); diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client.ts b/x-pack/legacy/plugins/alerting/server/alerts_client.ts index 40125f3067ee3..f6841ed5a0e46 100644 --- a/x-pack/legacy/plugins/alerting/server/alerts_client.ts +++ b/x-pack/legacy/plugins/alerting/server/alerts_client.ts @@ -31,6 +31,7 @@ import { } from '../../../../plugins/security/server'; import { EncryptedSavedObjectsPluginStart } from '../../../../plugins/encrypted_saved_objects/server'; import { TaskManagerStartContract } from '../../../../plugins/task_manager/server'; +import { AlertTaskState, taskInstanceToAlertTaskInstance } from './task_runner/alert_task_instance'; type NormalizedAlertAction = Omit; export type CreateAPIKeyResult = @@ -204,6 +205,17 @@ export class AlertsClient { return this.getAlertFromRaw(result.id, result.attributes, result.updated_at, result.references); } + public async getAlertState({ id }: { id: string }): Promise { + const alert = await this.get({ id }); + if (alert.scheduledTaskId) { + const { state } = taskInstanceToAlertTaskInstance( + await this.taskManager.get(alert.scheduledTaskId), + alert + ); + return state; + } + } + public async find({ options = {} }: FindOptions = {}): Promise { const { page, diff --git a/x-pack/legacy/plugins/alerting/server/lib/types.test.ts b/x-pack/legacy/plugins/alerting/server/lib/types.test.ts new file mode 100644 index 0000000000000..517b66aa2faab --- /dev/null +++ b/x-pack/legacy/plugins/alerting/server/lib/types.test.ts @@ -0,0 +1,28 @@ +/* + * 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 { DateFromString } from './types'; +import { right, isLeft } from 'fp-ts/lib/Either'; + +describe('DateFromString', () => { + test('validated and parses a string into a Date', () => { + const date = new Date(1973, 10, 30); + expect(DateFromString.decode(date.toISOString())).toEqual(right(date)); + }); + + test('validated and returns a failure for an actual Date', () => { + const date = new Date(1973, 10, 30); + expect(isLeft(DateFromString.decode(date))).toEqual(true); + }); + + test('validated and returns a failure for an invalid Date string', () => { + expect(isLeft(DateFromString.decode('1234-23-45'))).toEqual(true); + }); + + test('validated and returns a failure for a null value', () => { + expect(isLeft(DateFromString.decode(null))).toEqual(true); + }); +}); diff --git a/x-pack/legacy/plugins/alerting/server/lib/types.ts b/x-pack/legacy/plugins/alerting/server/lib/types.ts new file mode 100644 index 0000000000000..6df593ab17ce8 --- /dev/null +++ b/x-pack/legacy/plugins/alerting/server/lib/types.ts @@ -0,0 +1,25 @@ +/* + * 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 * as t from 'io-ts'; +import { either } from 'fp-ts/lib/Either'; +// represents a Date from an ISO string +export const DateFromString = new t.Type( + 'DateFromString', + // detect the type + (value): value is Date => value instanceof Date, + (valueToDecode, context) => + either.chain( + // validate this is a string + t.string.validate(valueToDecode, context), + // decode + value => { + const decoded = new Date(value); + return isNaN(decoded.getTime()) ? t.failure(valueToDecode, context) : t.success(decoded); + } + ), + valueToEncode => valueToEncode.toISOString() +); diff --git a/x-pack/legacy/plugins/alerting/server/plugin.ts b/x-pack/legacy/plugins/alerting/server/plugin.ts index a4de7af376fb0..e3f7656002d18 100644 --- a/x-pack/legacy/plugins/alerting/server/plugin.ts +++ b/x-pack/legacy/plugins/alerting/server/plugin.ts @@ -25,6 +25,7 @@ import { deleteAlertRoute, findAlertRoute, getAlertRoute, + getAlertStateRoute, listAlertTypesRoute, updateAlertRoute, enableAlertRoute, @@ -92,6 +93,7 @@ export class Plugin { core.http.route(extendRouteWithLicenseCheck(deleteAlertRoute, this.licenseState)); core.http.route(extendRouteWithLicenseCheck(findAlertRoute, this.licenseState)); core.http.route(extendRouteWithLicenseCheck(getAlertRoute, this.licenseState)); + core.http.route(extendRouteWithLicenseCheck(getAlertStateRoute, this.licenseState)); core.http.route(extendRouteWithLicenseCheck(listAlertTypesRoute, this.licenseState)); core.http.route(extendRouteWithLicenseCheck(updateAlertRoute, this.licenseState)); core.http.route(extendRouteWithLicenseCheck(enableAlertRoute, this.licenseState)); diff --git a/x-pack/legacy/plugins/alerting/server/routes/get_alert_state.test.ts b/x-pack/legacy/plugins/alerting/server/routes/get_alert_state.test.ts new file mode 100644 index 0000000000000..9e3b3b6579ead --- /dev/null +++ b/x-pack/legacy/plugins/alerting/server/routes/get_alert_state.test.ts @@ -0,0 +1,73 @@ +/* + * 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 { createMockServer } from './_mock_server'; +import { getAlertStateRoute } from './get_alert_state'; +import { SavedObjectsErrorHelpers } from 'src/core/server'; + +const { server, alertsClient } = createMockServer(); +server.route(getAlertStateRoute); + +const mockedAlertState = { + alertTypeState: { + some: 'value', + }, + alertInstances: { + first_instance: { + state: {}, + meta: { + lastScheduledActions: { + group: 'first_group', + date: new Date(), + }, + }, + }, + second_instance: {}, + }, +}; + +beforeEach(() => jest.resetAllMocks()); + +test('gets alert state', async () => { + const request = { + method: 'GET', + url: '/api/alert/1/state', + }; + + alertsClient.getAlertState.mockResolvedValueOnce(mockedAlertState); + + const { statusCode } = await server.inject(request); + expect(statusCode).toBe(200); + expect(alertsClient.getAlertState).toHaveBeenCalledWith({ id: '1' }); +}); + +test('returns NO-CONTENT when alert exists but has no task state yet', async () => { + const request = { + method: 'GET', + url: '/api/alert/1/state', + }; + + alertsClient.getAlertState.mockResolvedValueOnce(undefined); + + const { statusCode } = await server.inject(request); + expect(statusCode).toBe(204); + expect(alertsClient.getAlertState).toHaveBeenCalledWith({ id: '1' }); +}); + +test('returns NOT-FOUND when alert is not found', async () => { + const request = { + method: 'GET', + url: '/api/alert/1/state', + }; + + alertsClient.getAlertState.mockRejectedValue( + SavedObjectsErrorHelpers.createGenericNotFoundError('alert', '1') + ); + + const { statusCode } = await server.inject(request); + expect(statusCode).toBe(404); + expect(alertsClient.getAlertState).toHaveBeenCalledWith({ id: '1' }); +}); diff --git a/x-pack/legacy/plugins/alerting/server/routes/get_alert_state.ts b/x-pack/legacy/plugins/alerting/server/routes/get_alert_state.ts new file mode 100644 index 0000000000000..12136a975bb19 --- /dev/null +++ b/x-pack/legacy/plugins/alerting/server/routes/get_alert_state.ts @@ -0,0 +1,35 @@ +/* + * 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 Joi from 'joi'; +import Hapi from 'hapi'; + +interface GetAlertStateRequest extends Hapi.Request { + params: { + id: string; + }; +} + +export const getAlertStateRoute = { + method: 'GET', + path: '/api/alert/{id}/state', + options: { + tags: ['access:alerting-read'], + validate: { + params: Joi.object() + .keys({ + id: Joi.string().required(), + }) + .required(), + }, + }, + async handler(request: GetAlertStateRequest, h: Hapi.ResponseToolkit) { + const { id } = request.params; + const alertsClient = request.getAlertsClient!(); + const state = await alertsClient.getAlertState({ id }); + return state ? state : h.response().code(204); + }, +}; diff --git a/x-pack/legacy/plugins/alerting/server/routes/index.ts b/x-pack/legacy/plugins/alerting/server/routes/index.ts index 02cba8adc9db2..7ec901ae685c4 100644 --- a/x-pack/legacy/plugins/alerting/server/routes/index.ts +++ b/x-pack/legacy/plugins/alerting/server/routes/index.ts @@ -8,6 +8,7 @@ export { createAlertRoute } from './create'; export { deleteAlertRoute } from './delete'; export { findAlertRoute } from './find'; export { getAlertRoute } from './get'; +export { getAlertStateRoute } from './get_alert_state'; export { listAlertTypesRoute } from './list_alert_types'; export { updateAlertRoute } from './update'; export { enableAlertRoute } from './enable'; diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/alert_task_instance.test.ts b/x-pack/legacy/plugins/alerting/server/task_runner/alert_task_instance.test.ts new file mode 100644 index 0000000000000..9cbe91a4dbced --- /dev/null +++ b/x-pack/legacy/plugins/alerting/server/task_runner/alert_task_instance.test.ts @@ -0,0 +1,229 @@ +/* + * 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 { ConcreteTaskInstance, TaskStatus } from '../../../../../plugins/task_manager/server'; +import { AlertTaskInstance, taskInstanceToAlertTaskInstance } from './alert_task_instance'; +import uuid from 'uuid'; +import { SanitizedAlert } from '../types'; + +const alert: SanitizedAlert = { + id: 'alert-123', + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + actions: [], + enabled: true, + name: '', + tags: [], + consumer: '', + createdBy: null, + updatedBy: null, + createdAt: new Date(), + updatedAt: new Date(), + apiKeyOwner: null, + throttle: null, + muteAll: false, + mutedInstanceIds: [], +}; + +describe('Alert Task Instance', () => { + test(`validates that a TaskInstance has valid Alert Task State`, () => { + const lastScheduledActionsDate = new Date(); + const taskInstance: ConcreteTaskInstance = { + id: uuid.v4(), + attempts: 0, + status: TaskStatus.Running, + version: '123', + runAt: new Date(), + scheduledAt: new Date(), + startedAt: new Date(), + retryAt: new Date(Date.now() + 5 * 60 * 1000), + state: { + alertTypeState: { + some: 'value', + }, + alertInstances: { + first_instance: { + state: {}, + meta: { + lastScheduledActions: { + group: 'first_group', + date: lastScheduledActionsDate.toISOString(), + }, + }, + }, + second_instance: {}, + }, + }, + taskType: 'alerting:test', + params: { + alertId: '1', + }, + ownerId: null, + }; + + const alertTaskInsatnce: AlertTaskInstance = taskInstanceToAlertTaskInstance(taskInstance); + + expect(alertTaskInsatnce).toEqual({ + ...taskInstance, + state: { + alertTypeState: { + some: 'value', + }, + alertInstances: { + first_instance: { + state: {}, + meta: { + lastScheduledActions: { + group: 'first_group', + date: lastScheduledActionsDate, + }, + }, + }, + second_instance: {}, + }, + }, + }); + }); + + test(`throws if state is invalid`, () => { + const taskInstance: ConcreteTaskInstance = { + id: '215ee69b-1df9-428e-ab1a-ccf274f8fa5b', + attempts: 0, + status: TaskStatus.Running, + version: '123', + runAt: new Date(), + scheduledAt: new Date(), + startedAt: new Date(), + retryAt: new Date(Date.now() + 5 * 60 * 1000), + state: { + alertTypeState: { + some: 'value', + }, + alertInstances: { + first_instance: 'invalid', + second_instance: {}, + }, + }, + taskType: 'alerting:test', + params: { + alertId: '1', + }, + ownerId: null, + }; + + expect(() => taskInstanceToAlertTaskInstance(taskInstance)).toThrowErrorMatchingInlineSnapshot( + `"Task \\"215ee69b-1df9-428e-ab1a-ccf274f8fa5b\\" has invalid state at .alertInstances.first_instance"` + ); + }); + + test(`throws with Alert id when alert is present and state is invalid`, () => { + const taskInstance: ConcreteTaskInstance = { + id: '215ee69b-1df9-428e-ab1a-ccf274f8fa5b', + attempts: 0, + status: TaskStatus.Running, + version: '123', + runAt: new Date(), + scheduledAt: new Date(), + startedAt: new Date(), + retryAt: new Date(Date.now() + 5 * 60 * 1000), + state: { + alertTypeState: { + some: 'value', + }, + alertInstances: { + first_instance: 'invalid', + second_instance: {}, + }, + }, + taskType: 'alerting:test', + params: { + alertId: '1', + }, + ownerId: null, + }; + + expect(() => + taskInstanceToAlertTaskInstance(taskInstance, alert) + ).toThrowErrorMatchingInlineSnapshot( + `"Task \\"215ee69b-1df9-428e-ab1a-ccf274f8fa5b\\" (underlying Alert \\"alert-123\\") has invalid state at .alertInstances.first_instance"` + ); + }); + + test(`allows an initial empty state`, () => { + const taskInstance: ConcreteTaskInstance = { + id: uuid.v4(), + attempts: 0, + status: TaskStatus.Running, + version: '123', + runAt: new Date(), + scheduledAt: new Date(), + startedAt: new Date(), + retryAt: new Date(Date.now() + 5 * 60 * 1000), + state: {}, + taskType: 'alerting:test', + params: { + alertId: '1', + }, + ownerId: null, + }; + + const alertTaskInsatnce: AlertTaskInstance = taskInstanceToAlertTaskInstance(taskInstance); + + expect(alertTaskInsatnce).toEqual(taskInstance); + }); + + test(`validates that a TaskInstance has valid Params`, () => { + const taskInstance: ConcreteTaskInstance = { + id: uuid.v4(), + attempts: 0, + status: TaskStatus.Running, + version: '123', + runAt: new Date(), + scheduledAt: new Date(), + startedAt: new Date(), + retryAt: new Date(Date.now() + 5 * 60 * 1000), + state: {}, + taskType: 'alerting:test', + params: { + alertId: '1', + }, + ownerId: null, + }; + + const alertTaskInsatnce: AlertTaskInstance = taskInstanceToAlertTaskInstance( + taskInstance, + alert + ); + + expect(alertTaskInsatnce).toEqual(taskInstance); + }); + + test(`throws if params are invalid`, () => { + const taskInstance: ConcreteTaskInstance = { + id: '215ee69b-1df9-428e-ab1a-ccf274f8fa5b', + attempts: 0, + status: TaskStatus.Running, + version: '123', + runAt: new Date(), + scheduledAt: new Date(), + startedAt: new Date(), + retryAt: new Date(Date.now() + 5 * 60 * 1000), + state: {}, + taskType: 'alerting:test', + params: {}, + ownerId: null, + }; + + expect(() => + taskInstanceToAlertTaskInstance(taskInstance, alert) + ).toThrowErrorMatchingInlineSnapshot( + `"Task \\"215ee69b-1df9-428e-ab1a-ccf274f8fa5b\\" (underlying Alert \\"alert-123\\") has an invalid param at .0.alertId"` + ); + }); +}); diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/alert_task_instance.ts b/x-pack/legacy/plugins/alerting/server/task_runner/alert_task_instance.ts new file mode 100644 index 0000000000000..33b416fe8e2da --- /dev/null +++ b/x-pack/legacy/plugins/alerting/server/task_runner/alert_task_instance.ts @@ -0,0 +1,66 @@ +/* + * 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 * as t from 'io-ts'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { fold } from 'fp-ts/lib/Either'; +import { ConcreteTaskInstance } from '../../../../../plugins/task_manager/server'; +import { SanitizedAlert } from '../types'; +import { DateFromString } from '../lib/types'; +import { AlertInstance, rawAlertInstance } from '../alert_instance'; + +export interface AlertTaskInstance extends ConcreteTaskInstance { + state: AlertTaskState; +} + +export const alertStateSchema = t.partial({ + alertTypeState: t.record(t.string, t.unknown), + alertInstances: t.record(t.string, rawAlertInstance), + previousStartedAt: t.union([t.null, DateFromString]), +}); +export type AlertInstances = Record; +export type AlertTaskState = t.TypeOf; + +const alertParamsSchema = t.intersection([ + t.type({ + alertId: t.string, + }), + t.partial({ + spaceId: t.string, + }), +]); +export type AlertTaskParams = t.TypeOf; + +const enumerateErrorFields = (e: t.Errors) => + `${e.map(({ context }) => context.map(({ key }) => key).join('.'))}`; + +export function taskInstanceToAlertTaskInstance( + taskInstance: ConcreteTaskInstance, + alert?: SanitizedAlert +): AlertTaskInstance { + return { + ...taskInstance, + params: pipe( + alertParamsSchema.decode(taskInstance.params), + fold((e: t.Errors) => { + throw new Error( + `Task "${taskInstance.id}" ${ + alert ? `(underlying Alert "${alert.id}") ` : '' + }has an invalid param at ${enumerateErrorFields(e)}` + ); + }, t.identity) + ), + state: pipe( + alertStateSchema.decode(taskInstance.state), + fold((e: t.Errors) => { + throw new Error( + `Task "${taskInstance.id}" ${ + alert ? `(underlying Alert "${alert.id}") ` : '' + }has invalid state at ${enumerateErrorFields(e)}` + ); + }, t.identity) + ), + }; +} diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/legacy/plugins/alerting/server/task_runner/task_runner.ts index 0f643e3d3121c..1466d3ccd274b 100644 --- a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/legacy/plugins/alerting/server/task_runner/task_runner.ts @@ -10,25 +10,32 @@ import { SavedObject } from '../../../../../../src/core/server'; import { TaskRunnerContext } from './task_runner_factory'; import { ConcreteTaskInstance } from '../../../../../plugins/task_manager/server'; import { createExecutionHandler } from './create_execution_handler'; -import { AlertInstance, createAlertInstanceFactory } from '../alert_instance'; +import { AlertInstance, createAlertInstanceFactory, RawAlertInstance } from '../alert_instance'; import { getNextRunAt } from './get_next_run_at'; import { validateAlertTypeParams } from '../lib'; -import { AlertType, RawAlert, IntervalSchedule, Services, State, AlertInfoParams } from '../types'; +import { AlertType, RawAlert, IntervalSchedule, Services, AlertInfoParams } from '../types'; import { promiseResult, map, Resultable, asOk, asErr, resolveErr } from '../lib/result_type'; - -type AlertInstances = Record; +import { + AlertTaskState, + AlertInstances, + taskInstanceToAlertTaskInstance, +} from './alert_task_instance'; const FALLBACK_RETRY_INTERVAL: IntervalSchedule = { interval: '5m' }; interface AlertTaskRunResult { - state: State; + state: AlertTaskState; runAt: Date; } +interface AlertTaskInstance extends ConcreteTaskInstance { + state: AlertTaskState; +} + export class TaskRunner { private context: TaskRunnerContext; private logger: Logger; - private taskInstance: ConcreteTaskInstance; + private taskInstance: AlertTaskInstance; private alertType: AlertType; constructor( @@ -39,7 +46,7 @@ export class TaskRunner { this.context = context; this.logger = context.logger; this.alertType = alertType; - this.taskInstance = taskInstance; + this.taskInstance = taskInstanceToAlertTaskInstance(taskInstance); } async getApiKeyForAlertPermissions(alertId: string, spaceId: string) { @@ -128,7 +135,7 @@ export class TaskRunner { alertInfoParams: AlertInfoParams, executionHandler: ReturnType, spaceId: string - ): Promise { + ): Promise { const { params, throttle, @@ -145,9 +152,9 @@ export class TaskRunner { } = this.taskInstance; const namespace = this.context.spaceIdToNamespace(spaceId); - const alertInstances = mapValues( + const alertInstances = mapValues( alertRawInstances, - alert => new AlertInstance(alert) + rawAlertInstance => new AlertInstance(rawAlertInstance) ); const updatedAlertTypeState = await this.alertType.executor({ @@ -159,7 +166,7 @@ export class TaskRunner { params, state: alertTypeState, startedAt: this.taskInstance.startedAt!, - previousStartedAt: previousStartedAt && new Date(previousStartedAt), + previousStartedAt: previousStartedAt ? new Date(previousStartedAt) : null, spaceId, namespace, name, @@ -171,7 +178,7 @@ export class TaskRunner { // Cleanup alert instances that are no longer scheduling actions to avoid over populating the alertInstances object const instancesWithScheduledActions = pick( alertInstances, - alertInstance => alertInstance.hasScheduledActions() + (alertInstance: AlertInstance) => alertInstance.hasScheduledActions() ); if (!muteAll) { @@ -192,8 +199,11 @@ export class TaskRunner { } return { - alertTypeState: updatedAlertTypeState, - alertInstances: instancesWithScheduledActions, + alertTypeState: updatedAlertTypeState || undefined, + alertInstances: mapValues( + instancesWithScheduledActions, + alertInstance => alertInstance.toRaw() + ), }; } @@ -239,7 +249,7 @@ export class TaskRunner { ); return { - state: await promiseResult( + state: await promiseResult( this.validateAndExecuteAlert(services, apiKey, attributes, references) ), runAt: asOk( @@ -264,9 +274,9 @@ export class TaskRunner { const { state, runAt } = await errorAsAlertTaskRunResult(this.loadAlertAttributesAndRun()); return { - state: map( + state: map( state, - (stateUpdates: State) => { + (stateUpdates: AlertTaskState) => { return { ...stateUpdates, previousStartedAt, diff --git a/x-pack/legacy/plugins/alerting/server/types.ts b/x-pack/legacy/plugins/alerting/server/types.ts index 9c4a64ff02105..5aef3b1337a88 100644 --- a/x-pack/legacy/plugins/alerting/server/types.ts +++ b/x-pack/legacy/plugins/alerting/server/types.ts @@ -31,7 +31,7 @@ export interface AlertServices extends Services { export interface AlertExecutorOptions { alertId: string; startedAt: Date; - previousStartedAt?: Date; + previousStartedAt: Date | null; services: AlertServices; params: Record; state: State; diff --git a/x-pack/legacy/plugins/canvas/i18n/index.ts b/x-pack/legacy/plugins/canvas/i18n/index.ts index a671d0ccdb49f..864311d34aca0 100644 --- a/x-pack/legacy/plugins/canvas/i18n/index.ts +++ b/x-pack/legacy/plugins/canvas/i18n/index.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { i18n } from '@kbn/i18n'; - export * from './capabilities'; export * from './components'; export * from './constants'; @@ -19,8 +17,3 @@ export * from './tags'; export * from './transitions'; export * from './ui'; export * from './units'; - -export const getAppDescription = () => - i18n.translate('xpack.canvas.appDescription', { - defaultMessage: 'Showcase your data in a pixel-perfect way.', - }); diff --git a/x-pack/legacy/plugins/canvas/index.js b/x-pack/legacy/plugins/canvas/index.js index ebd4f35db8175..b357ec9c0b61e 100644 --- a/x-pack/legacy/plugins/canvas/index.js +++ b/x-pack/legacy/plugins/canvas/index.js @@ -36,7 +36,7 @@ export function canvas(kibana) { // window.onerror override 'plugins/canvas/lib/window_error_handler.js', ], - home: ['plugins/canvas/register_feature'], + home: ['plugins/canvas/legacy_register_feature'], mappings, migrations, savedObjectsManagement: { diff --git a/x-pack/legacy/plugins/canvas/public/feature_catalogue_entry.ts b/x-pack/legacy/plugins/canvas/public/feature_catalogue_entry.ts new file mode 100644 index 0000000000000..f610bd0299832 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/feature_catalogue_entry.ts @@ -0,0 +1,20 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; + +export const featureCatalogueEntry = { + id: 'canvas', + title: 'Canvas', + description: i18n.translate('xpack.canvas.appDescription', { + defaultMessage: 'Showcase your data in a pixel-perfect way.', + }), + icon: 'canvasApp', + path: '/app/canvas', + showOnHomePage: true, + category: FeatureCatalogueCategory.DATA, +}; diff --git a/x-pack/legacy/plugins/canvas/public/legacy.ts b/x-pack/legacy/plugins/canvas/public/legacy.ts index cbd2aa54627ee..c16bc124747c6 100644 --- a/x-pack/legacy/plugins/canvas/public/legacy.ts +++ b/x-pack/legacy/plugins/canvas/public/legacy.ts @@ -22,7 +22,9 @@ const shimCoreSetup = { const shimCoreStart = { ...npStart.core, }; -const shimSetupPlugins = {}; +const shimSetupPlugins = { + home: npSetup.plugins.home, +}; const shimStartPlugins: CanvasStartDeps = { ...npStart.plugins, diff --git a/x-pack/legacy/plugins/spaces/common/model/types.ts b/x-pack/legacy/plugins/canvas/public/legacy_register_feature.ts similarity index 54% rename from x-pack/legacy/plugins/spaces/common/model/types.ts rename to x-pack/legacy/plugins/canvas/public/legacy_register_feature.ts index 58c36da33dbd7..00f788f267d4b 100644 --- a/x-pack/legacy/plugins/spaces/common/model/types.ts +++ b/x-pack/legacy/plugins/canvas/public/legacy_register_feature.ts @@ -4,4 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -export type GetSpacePurpose = 'any' | 'copySavedObjectsIntoSpace'; +import { npSetup } from 'ui/new_platform'; +import { featureCatalogueEntry } from './feature_catalogue_entry'; + +const { + plugins: { home }, +} = npSetup; + +home.featureCatalogue.register(featureCatalogueEntry); diff --git a/x-pack/legacy/plugins/canvas/public/plugin.tsx b/x-pack/legacy/plugins/canvas/public/plugin.tsx index 7928d46067908..a24fd758808ba 100644 --- a/x-pack/legacy/plugins/canvas/public/plugin.tsx +++ b/x-pack/legacy/plugins/canvas/public/plugin.tsx @@ -10,6 +10,7 @@ import { Chrome } from 'ui/chrome'; import { i18n } from '@kbn/i18n'; import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { CoreSetup, CoreStart, Plugin } from '../../../../../src/core/public'; +import { HomePublicPluginSetup } from '../../../../../src/plugins/home/public'; // @ts-ignore: Untyped Local import { CapabilitiesStrings } from '../i18n'; const { ReadOnlyBadge: strings } = CapabilitiesStrings; @@ -27,6 +28,7 @@ import { getDocumentationLinks } from './lib/documentation_links'; // @ts-ignore: untyped local import { initClipboard } from './lib/clipboard'; +import { featureCatalogueEntry } from './feature_catalogue_entry'; export { CoreStart }; /** @@ -34,7 +36,9 @@ export { CoreStart }; * @internal */ // This interface will be built out as we require other plugins for setup -export interface CanvasSetupDeps {} // eslint-disable-line @typescript-eslint/no-empty-interface +export interface CanvasSetupDeps { + home: HomePublicPluginSetup; +} export interface CanvasStartDeps { __LEGACY: { absoluteToParsedUrl: (url: string, basePath: string) => any; @@ -79,6 +83,9 @@ export class CanvasPlugin return renderApp(coreStart, depsStart, params, canvasStore); }, }); + + plugins.home.featureCatalogue.register(featureCatalogueEntry); + return {}; } diff --git a/x-pack/legacy/plugins/canvas/public/register_feature.js b/x-pack/legacy/plugins/canvas/public/register_feature.js deleted file mode 100644 index 8d78498de34b2..0000000000000 --- a/x-pack/legacy/plugins/canvas/public/register_feature.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - FeatureCatalogueRegistryProvider, - FeatureCatalogueCategory, -} from 'ui/registry/feature_catalogue'; - -import { getAppDescription } from '../i18n'; - -FeatureCatalogueRegistryProvider.register(() => { - return { - id: 'canvas', - title: 'Canvas', - description: getAppDescription(), - icon: 'canvasApp', - path: '/app/canvas', - showOnHomePage: true, - category: FeatureCatalogueCategory.DATA, - }; -}); diff --git a/x-pack/legacy/plugins/cross_cluster_replication/__jest__/client_integration/auto_follow_pattern_list.test.js b/x-pack/legacy/plugins/cross_cluster_replication/__jest__/client_integration/auto_follow_pattern_list.test.js index 1003569733d91..88d8f98b973bd 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/__jest__/client_integration/auto_follow_pattern_list.test.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/__jest__/client_integration/auto_follow_pattern_list.test.js @@ -286,7 +286,7 @@ describe('', () => { actions.clickAutoFollowPatternAt(0); find('autoFollowPatternActionMenuButton').simulate('click'); expect(exists('autoFollowPatternDetail.closeFlyoutButton')).toBe(true); - expect(actions.getPatternsActionMenuItemText(0)).toEqual('Resume pattern'); + expect(actions.getPatternsActionMenuItemText(0)).toEqual('Resume replication'); expect(actions.getPatternsActionMenuItemText(1)).toEqual('Edit pattern'); expect(actions.getPatternsActionMenuItemText(2)).toEqual('Delete pattern'); }); diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/app/components/auto_follow_pattern_action_menu/auto_follow_pattern_action_menu.tsx b/x-pack/legacy/plugins/cross_cluster_replication/public/app/components/auto_follow_pattern_action_menu/auto_follow_pattern_action_menu.tsx index 7c129eac9cbd9..12654e56bde97 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/app/components/auto_follow_pattern_action_menu/auto_follow_pattern_action_menu.tsx +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/app/components/auto_follow_pattern_action_menu/auto_follow_pattern_action_menu.tsx @@ -68,7 +68,7 @@ const AutoFollowPatternActionMenuUI: FunctionComponent = ({ ? patterns[0].active ? { name: i18n.translate('xpack.crossClusterReplication.pauseAutoFollowPatternsLabel', { - defaultMessage: 'Pause {total, plural, one {pattern} other {patterns}}', + defaultMessage: 'Pause {total, plural, one {replication} other {replications}}', values: { total: patterns.length }, }), icon: , @@ -79,7 +79,7 @@ const AutoFollowPatternActionMenuUI: FunctionComponent = ({ } : { name: i18n.translate('xpack.crossClusterReplication.resumeAutoFollowPatternsLabel', { - defaultMessage: 'Resume {total, plural, one {pattern} other {patterns}}', + defaultMessage: 'Resume {total, plural, one {replication} other {replications}}', values: { total: patterns.length }, }), icon: , diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.js b/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.js index 08b1770e39963..956a9f10d810b 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.js @@ -180,13 +180,13 @@ export class AutoFollowPatternTable extends PureComponent { ? i18n.translate( 'xpack.crossClusterReplication.autoFollowPatternList.table.actionPauseDescription', { - defaultMessage: 'Pause auto-follow pattern', + defaultMessage: 'Pause replication', } ) : i18n.translate( 'xpack.crossClusterReplication.autoFollowPatternList.table.actionResumeDescription', { - defaultMessage: 'Resume auto-follow pattern', + defaultMessage: 'Resume replication', } ); diff --git a/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js b/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js index 44c4b81c8ad93..8ca023aa90cf1 100644 --- a/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js +++ b/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js @@ -40,9 +40,7 @@ import { localApplicationService } from 'plugins/kibana/local_application_servic import { showAppRedirectNotification } from 'ui/notify'; import { DashboardConstants, createDashboardEditUrl } from 'plugins/kibana/dashboard'; -uiModules - .get('kibana') - .config(dashboardConfigProvider => dashboardConfigProvider.turnHideWriteControlsOn()); +npStart.plugins.kibanaLegacy.dashboardConfig.turnHideWriteControlsOn(); localApplicationService.attachToAngular(routes); diff --git a/x-pack/legacy/plugins/graph/index.ts b/x-pack/legacy/plugins/graph/index.ts index f798fa5e9f39d..143d07cfdbd57 100644 --- a/x-pack/legacy/plugins/graph/index.ts +++ b/x-pack/legacy/plugins/graph/index.ts @@ -61,6 +61,7 @@ export const graph: LegacyPluginInitializer = kibana => { navLinkId: 'graph', app: ['graph', 'kibana'], catalogue: ['graph'], + validLicenses: ['platinum', 'enterprise', 'trial'], privileges: { all: { savedObject: { diff --git a/x-pack/legacy/plugins/graph/public/app.js b/x-pack/legacy/plugins/graph/public/app.js index 38a601daa178e..7010e1fa773ea 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/legacy/plugins/graph/public/app.js @@ -49,6 +49,7 @@ export function initGraphApp(angularModule, deps) { storage, canEditDrillDownUrls, graphSavePolicy, + overlays, } = deps; const app = angularModule; @@ -162,7 +163,7 @@ export function initGraphApp(angularModule, deps) { }); //======== Controller for basic UI ================== - app.controller('graphuiPlugin', function($scope, $route, $location, confirmModal) { + app.controller('graphuiPlugin', function($scope, $route, $location) { function handleError(err) { const toastTitle = i18n.translate('xpack.graph.errorToastTitle', { defaultMessage: 'Graph Error', @@ -382,23 +383,29 @@ export function initGraphApp(angularModule, deps) { return; } const confirmModalOptions = { - onConfirm: callback, - onCancel: () => {}, confirmButtonText: i18n.translate('xpack.graph.leaveWorkspace.confirmButtonLabel', { defaultMessage: 'Leave anyway', }), title: i18n.translate('xpack.graph.leaveWorkspace.modalTitle', { defaultMessage: 'Unsaved changes', }), + 'data-test-subj': 'confirmModal', ...options, }; - confirmModal( - text || - i18n.translate('xpack.graph.leaveWorkspace.confirmText', { - defaultMessage: 'If you leave now, you will lose unsaved changes.', - }), - confirmModalOptions - ); + + overlays + .openConfirm( + text || + i18n.translate('xpack.graph.leaveWorkspace.confirmText', { + defaultMessage: 'If you leave now, you will lose unsaved changes.', + }), + confirmModalOptions + ) + .then(isConfirmed => { + if (isConfirmed) { + callback(); + } + }); } $scope.confirmWipeWorkspace = canWipeWorkspace; diff --git a/x-pack/legacy/plugins/graph/public/application.ts b/x-pack/legacy/plugins/graph/public/application.ts index 8f486ab6ad51a..80a797b7f0724 100644 --- a/x-pack/legacy/plugins/graph/public/application.ts +++ b/x-pack/legacy/plugins/graph/public/application.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiConfirmModal } from '@elastic/eui'; - // inner angular imports // these are necessary to bootstrap the local angular. // They can stay even after NP cutover @@ -20,12 +18,12 @@ import { SavedObjectsClientContract, ToastsStart, IUiSettingsClient, + OverlayStart, } from 'kibana/public'; import { configureAppAngularModule, createTopNavDirective, createTopNavHelper, - confirmModalFactory, addAppRedirectMessageToUrl, } from './legacy_imports'; // @ts-ignore @@ -64,6 +62,7 @@ export interface GraphDependencies { storage: Storage; canEditDrillDownUrls: boolean; graphSavePolicy: string; + overlays: OverlayStart; } export const renderApp = ({ appBasePath, element, ...deps }: GraphDependencies) => { @@ -120,24 +119,15 @@ function mountGraphApp(appBasePath: string, element: HTMLElement) { function createLocalAngularModule(navigation: NavigationStart) { createLocalI18nModule(); createLocalTopNavModule(navigation); - createLocalConfirmModalModule(); const graphAngularModule = angular.module(moduleName, [ ...thirdPartyAngularDependencies, 'graphI18n', 'graphTopNav', - 'graphConfirmModal', ]); return graphAngularModule; } -function createLocalConfirmModalModule() { - angular - .module('graphConfirmModal', ['react']) - .factory('confirmModal', confirmModalFactory) - .directive('confirmModal', reactDirective => reactDirective(EuiConfirmModal)); -} - function createLocalTopNavModule(navigation: NavigationStart) { angular .module('graphTopNav', ['react']) diff --git a/x-pack/legacy/plugins/graph/public/legacy_imports.ts b/x-pack/legacy/plugins/graph/public/legacy_imports.ts index f1839d62a0667..27184f5701235 100644 --- a/x-pack/legacy/plugins/graph/public/legacy_imports.ts +++ b/x-pack/legacy/plugins/graph/public/legacy_imports.ts @@ -4,15 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import 'ui/angular-bootstrap'; import 'ace'; export { SavedObject, SavedObjectKibanaServices } from 'ui/saved_objects/types'; // @ts-ignore export { createTopNavDirective, createTopNavHelper } from 'ui/kbn_top_nav/kbn_top_nav'; // @ts-ignore -export { confirmModalFactory } from 'ui/modals/confirm_modal'; -// @ts-ignore export { addAppRedirectMessageToUrl } from 'ui/notify'; export { createSavedObjectClass } from 'ui/saved_objects/saved_object'; export { configureAppAngularModule } from '../../../../../src/plugins/kibana_legacy/public'; diff --git a/x-pack/legacy/plugins/graph/public/plugin.ts b/x-pack/legacy/plugins/graph/public/plugin.ts index ab610d76be101..b4ca4bf423181 100644 --- a/x-pack/legacy/plugins/graph/public/plugin.ts +++ b/x-pack/legacy/plugins/graph/public/plugin.ts @@ -10,6 +10,7 @@ import { Plugin as DataPlugin } from 'src/plugins/data/public'; import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { LicensingPluginSetup } from '../../../../plugins/licensing/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../../src/plugins/navigation/public'; +import { initAngularBootstrap } from '../../../../../src/plugins/kibana_legacy/public'; export interface GraphPluginStartDependencies { npData: ReturnType; @@ -26,6 +27,7 @@ export class GraphPlugin implements Plugin { private savedObjectsClient: SavedObjectsClientContract | null = null; setup(core: CoreSetup, { licensing }: GraphPluginSetupDependencies) { + initAngularBootstrap(); core.application.register({ id: 'graph', title: 'Graph', @@ -50,6 +52,7 @@ export class GraphPlugin implements Plugin { config: contextCore.uiSettings, toastNotifications: contextCore.notifications.toasts, indexPatterns: this.npDataStart!.indexPatterns, + overlays: contextCore.overlays, }); }, }); diff --git a/x-pack/legacy/plugins/grokdebugger/public/register_feature.js b/x-pack/legacy/plugins/grokdebugger/public/register_feature.js deleted file mode 100644 index 18021ed0f752d..0000000000000 --- a/x-pack/legacy/plugins/grokdebugger/public/register_feature.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - FeatureCatalogueRegistryProvider, - FeatureCatalogueCategory, -} from 'ui/registry/feature_catalogue'; - -import { i18n } from '@kbn/i18n'; - -FeatureCatalogueRegistryProvider.register(() => { - return { - id: 'grokdebugger', - title: i18n.translate('xpack.grokDebugger.registryProviderTitle', { - defaultMessage: '{grokLogParsingTool} Debugger', - values: { - grokLogParsingTool: 'Grok', - }, - }), - description: i18n.translate('xpack.grokDebugger.registryProviderDescription', { - defaultMessage: - 'Simulate and debug {grokLogParsingTool} patterns for data transformation on ingestion.', - values: { - grokLogParsingTool: 'grok', - }, - }), - icon: 'grokApp', - path: '/app/kibana#/dev_tools/grokdebugger', - showOnHomePage: false, - category: FeatureCatalogueCategory.ADMIN, - }; -}); diff --git a/x-pack/legacy/plugins/grokdebugger/public/register_feature.ts b/x-pack/legacy/plugins/grokdebugger/public/register_feature.ts new file mode 100644 index 0000000000000..97d2e53ce7836 --- /dev/null +++ b/x-pack/legacy/plugins/grokdebugger/public/register_feature.ts @@ -0,0 +1,34 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { npSetup } from 'ui/new_platform'; +import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; + +const { + plugins: { home }, +} = npSetup; + +home.featureCatalogue.register({ + id: 'grokdebugger', + title: i18n.translate('xpack.grokDebugger.registryProviderTitle', { + defaultMessage: '{grokLogParsingTool} Debugger', + values: { + grokLogParsingTool: 'Grok', + }, + }), + description: i18n.translate('xpack.grokDebugger.registryProviderDescription', { + defaultMessage: + 'Simulate and debug {grokLogParsingTool} patterns for data transformation on ingestion.', + values: { + grokLogParsingTool: 'grok', + }, + }), + icon: 'grokApp', + path: '/app/kibana#/dev_tools/grokdebugger', + showOnHomePage: false, + category: FeatureCatalogueCategory.ADMIN, +}); diff --git a/x-pack/legacy/plugins/infra/index.ts b/x-pack/legacy/plugins/infra/index.ts index d9abadcb5125c..4ab2cde082498 100644 --- a/x-pack/legacy/plugins/infra/index.ts +++ b/x-pack/legacy/plugins/infra/index.ts @@ -42,7 +42,7 @@ export function infra(kibana: any) { url: `/app/${APP_ID}#/infrastructure`, }, styleSheetPaths: resolve(__dirname, 'public/index.scss'), - home: ['plugins/infra/register_feature'], + home: ['plugins/infra/legacy_register_feature'], links: [ { description: i18n.translate('xpack.infra.linkInfrastructureDescription', { diff --git a/x-pack/legacy/plugins/infra/public/app.ts b/x-pack/legacy/plugins/infra/public/app.ts index 4b14e168eb768..7a13d3a59cc0d 100644 --- a/x-pack/legacy/plugins/infra/public/app.ts +++ b/x-pack/legacy/plugins/infra/public/app.ts @@ -9,7 +9,7 @@ // actually mount and run our application. Once in the NP this won't be an issue // as the NP will look for an export named "plugin" and run that from the index file. -import { npStart } from 'ui/new_platform'; +import { npStart, npSetup } from 'ui/new_platform'; import { PluginInitializerContext } from 'kibana/public'; import chrome from 'ui/chrome'; // @ts-ignore @@ -50,5 +50,7 @@ const checkForRoot = () => { }; checkForRoot().then(() => { - plugin({} as PluginInitializerContext).start(core, plugins, __LEGACY); + const pluginInstance = plugin({} as PluginInitializerContext); + pluginInstance.setup(npSetup.core, { home: npSetup.plugins.home }); + pluginInstance.start(core, plugins, __LEGACY); }); diff --git a/x-pack/legacy/plugins/infra/public/feature_catalogue_entry.ts b/x-pack/legacy/plugins/infra/public/feature_catalogue_entry.ts new file mode 100644 index 0000000000000..6442083234f2c --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/feature_catalogue_entry.ts @@ -0,0 +1,41 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; + +const APP_ID = 'infra'; + +export const featureCatalogueEntries = { + metrics: { + id: 'infraops', + title: i18n.translate('xpack.infra.registerFeatures.infraOpsTitle', { + defaultMessage: 'Metrics', + }), + description: i18n.translate('xpack.infra.registerFeatures.infraOpsDescription', { + defaultMessage: + 'Explore infrastructure metrics and logs for common servers, containers, and services.', + }), + icon: 'metricsApp', + path: `/app/${APP_ID}#infrastructure`, + showOnHomePage: true, + category: FeatureCatalogueCategory.DATA, + }, + logs: { + id: 'infralogging', + title: i18n.translate('xpack.infra.registerFeatures.logsTitle', { + defaultMessage: 'Logs', + }), + description: i18n.translate('xpack.infra.registerFeatures.logsDescription', { + defaultMessage: + 'Stream logs in real time or scroll through historical views in a console-like experience.', + }), + icon: 'logsApp', + path: `/app/${APP_ID}#logs`, + showOnHomePage: true, + category: FeatureCatalogueCategory.DATA, + }, +}; diff --git a/x-pack/legacy/plugins/infra/public/legacy_register_feature.ts b/x-pack/legacy/plugins/infra/public/legacy_register_feature.ts new file mode 100644 index 0000000000000..7b10a1e062f75 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/legacy_register_feature.ts @@ -0,0 +1,15 @@ +/* + * 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 { npSetup } from 'ui/new_platform'; +import { featureCatalogueEntries } from './feature_catalogue_entry'; + +const { + plugins: { home }, +} = npSetup; + +home.featureCatalogue.register(featureCatalogueEntries.metrics); +home.featureCatalogue.register(featureCatalogueEntries.logs); diff --git a/x-pack/legacy/plugins/infra/public/new_platform_plugin.ts b/x-pack/legacy/plugins/infra/public/new_platform_plugin.ts index 78594afcc8ada..f438b65794653 100644 --- a/x-pack/legacy/plugins/infra/public/new_platform_plugin.ts +++ b/x-pack/legacy/plugins/infra/public/new_platform_plugin.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { CoreStart, PluginInitializerContext } from 'kibana/public'; +import { CoreStart, CoreSetup, PluginInitializerContext } from 'kibana/public'; import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; import ApolloClient from 'apollo-client'; import { ApolloLink } from 'apollo-link'; @@ -13,12 +13,23 @@ import { startApp } from './apps/start_app'; import { InfraFrontendLibs } from './lib/lib'; import introspectionQueryResultData from './graphql/introspection.json'; import { InfraKibanaObservableApiAdapter } from './lib/adapters/observable_api/kibana_observable_api'; +import { HomePublicPluginSetup } from '../../../../../src/plugins/home/public'; +import { featureCatalogueEntries } from './feature_catalogue_entry'; type ClientPlugins = any; type LegacyDeps = any; +interface InfraPluginSetupDependencies { + home: HomePublicPluginSetup; +} export class Plugin { constructor(context: PluginInitializerContext) {} + + setup(core: CoreSetup, { home }: InfraPluginSetupDependencies) { + home.featureCatalogue.register(featureCatalogueEntries.metrics); + home.featureCatalogue.register(featureCatalogueEntries.logs); + } + start(core: CoreStart, plugins: ClientPlugins, __LEGACY: LegacyDeps) { startApp(this.composeLibs(core, plugins, __LEGACY), core, plugins); } diff --git a/x-pack/legacy/plugins/infra/public/register_feature.ts b/x-pack/legacy/plugins/infra/public/register_feature.ts deleted file mode 100644 index bf56db77e360f..0000000000000 --- a/x-pack/legacy/plugins/infra/public/register_feature.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { I18nServiceType } from '@kbn/i18n/angular'; -import { - FeatureCatalogueCategory, - FeatureCatalogueRegistryProvider, -} from 'ui/registry/feature_catalogue'; - -const APP_ID = 'infra'; - -FeatureCatalogueRegistryProvider.register((i18n: I18nServiceType) => ({ - id: 'infraops', - title: i18n('xpack.infra.registerFeatures.infraOpsTitle', { - defaultMessage: 'Metrics', - }), - description: i18n('xpack.infra.registerFeatures.infraOpsDescription', { - defaultMessage: - 'Explore infrastructure metrics and logs for common servers, containers, and services.', - }), - icon: 'metricsApp', - path: `/app/${APP_ID}#infrastructure`, - showOnHomePage: true, - category: FeatureCatalogueCategory.DATA, -})); - -FeatureCatalogueRegistryProvider.register((i18n: I18nServiceType) => ({ - id: 'infralogging', - title: i18n('xpack.infra.registerFeatures.logsTitle', { - defaultMessage: 'Logs', - }), - description: i18n('xpack.infra.registerFeatures.logsDescription', { - defaultMessage: - 'Stream logs in real time or scroll through historical views in a console-like experience.', - }), - icon: 'logsApp', - path: `/app/${APP_ID}#logs`, - showOnHomePage: true, - category: FeatureCatalogueCategory.DATA, -})); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.test.tsx index 62e2e628c254f..46a8304cc395e 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.test.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.test.tsx @@ -11,7 +11,7 @@ import { FieldItem, FieldItemProps } from './field_item'; import { coreMock } from 'src/core/public/mocks'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { npStart } from 'ui/new_platform'; -import { FieldFormatsStart } from '../../../../../../src/plugins/data/public'; +import { DataPublicPluginStart } from '../../../../../../src/plugins/data/public'; import { IndexPattern } from './types'; jest.mock('ui/new_platform'); @@ -87,7 +87,7 @@ describe('IndexPattern Field Item', () => { getDefaultInstance: jest.fn(() => ({ convert: jest.fn((s: unknown) => JSON.stringify(s)), })), - } as unknown) as FieldFormatsStart; + } as unknown) as DataPublicPluginStart['fieldFormats']; }); it('should request field stats without a time field, if the index pattern has none', async () => { diff --git a/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_expression.test.tsx b/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_expression.test.tsx index a9a48c46f5bd0..1e0fce9f538b4 100644 --- a/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_expression.test.tsx +++ b/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_expression.test.tsx @@ -9,7 +9,7 @@ import { LensMultiTable } from '../types'; import React from 'react'; import { shallow } from 'enzyme'; import { MetricConfig } from './types'; -import { fieldFormats } from '../../../../../../src/plugins/data/public'; +import { IFieldFormat } from '../../../../../../src/plugins/data/public'; function sampleArgs() { const data: LensMultiTable = { @@ -55,9 +55,7 @@ describe('metric_expression', () => { const { data, args } = sampleArgs(); expect( - shallow( - x as fieldFormats.FieldFormat} /> - ) + shallow( x as IFieldFormat} />) ).toMatchInlineSnapshot(` { x as fieldFormats.FieldFormat} + formatFactory={x => x as IFieldFormat} /> ) ).toMatchInlineSnapshot(` diff --git a/x-pack/legacy/plugins/logstash/public/lib/register_home_feature/register_home_feature.js b/x-pack/legacy/plugins/logstash/public/lib/register_home_feature.ts old mode 100755 new mode 100644 similarity index 63% rename from x-pack/legacy/plugins/logstash/public/lib/register_home_feature/register_home_feature.js rename to x-pack/legacy/plugins/logstash/public/lib/register_home_feature.ts index ee26cea54f977..e943656120d5e --- a/x-pack/legacy/plugins/logstash/public/lib/register_home_feature/register_home_feature.js +++ b/x-pack/legacy/plugins/logstash/public/lib/register_home_feature.ts @@ -4,20 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - FeatureCatalogueRegistryProvider, - FeatureCatalogueCategory, -} from 'ui/registry/feature_catalogue'; - import { i18n } from '@kbn/i18n'; +import { npSetup } from 'ui/new_platform'; +// @ts-ignore +import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; +import { FeatureCatalogueCategory } from '../../../../../../src/plugins/home/public'; +// @ts-ignore +import { PLUGIN } from '../../common/constants'; + +const { + plugins: { home }, +} = npSetup; -FeatureCatalogueRegistryProvider.register($injector => { - const licenseService = $injector.get('logstashLicenseService'); - if (!licenseService.enableLinks) { - return; - } +const enableLinks = Boolean(xpackInfo.get(`features.${PLUGIN.ID}.enableLinks`)); - return { +if (enableLinks) { + home.featureCatalogue.register({ id: 'management_logstash', title: i18n.translate('xpack.logstash.homeFeature.logstashPipelinesTitle', { defaultMessage: 'Logstash Pipelines', @@ -29,5 +31,5 @@ FeatureCatalogueRegistryProvider.register($injector => { path: '/app/kibana#/management/logstash/pipelines', showOnHomePage: true, category: FeatureCatalogueCategory.ADMIN, - }; -}); + }); +} diff --git a/x-pack/legacy/plugins/maps/common/constants.js b/x-pack/legacy/plugins/maps/common/constants.ts similarity index 98% rename from x-pack/legacy/plugins/maps/common/constants.js rename to x-pack/legacy/plugins/maps/common/constants.ts index 2570341aa5756..ab9a696fa3a17 100644 --- a/x-pack/legacy/plugins/maps/common/constants.js +++ b/x-pack/legacy/plugins/maps/common/constants.ts @@ -33,7 +33,7 @@ export const INDEX_SETTINGS_API_PATH = `${GIS_API_PATH}/indexSettings`; export const MAP_BASE_URL = `/${MAP_APP_PATH}#/${MAP_SAVED_OBJECT_TYPE}`; -export function createMapPath(id) { +export function createMapPath(id: string) { return `${MAP_BASE_URL}/${id}`; } diff --git a/x-pack/legacy/plugins/maps/common/i18n_getters.js b/x-pack/legacy/plugins/maps/common/i18n_getters.ts similarity index 90% rename from x-pack/legacy/plugins/maps/common/i18n_getters.js rename to x-pack/legacy/plugins/maps/common/i18n_getters.ts index 578d0cd4780e9..0008a119f1c7c 100644 --- a/x-pack/legacy/plugins/maps/common/i18n_getters.js +++ b/x-pack/legacy/plugins/maps/common/i18n_getters.ts @@ -6,6 +6,7 @@ import { i18n } from '@kbn/i18n'; +import { $Values } from '@kbn/utility-types'; import { ES_SPATIAL_RELATIONS } from './constants'; export function getAppTitle() { @@ -26,7 +27,7 @@ export function getUrlLabel() { }); } -export function getEsSpatialRelationLabel(spatialRelation) { +export function getEsSpatialRelationLabel(spatialRelation: $Values) { switch (spatialRelation) { case ES_SPATIAL_RELATIONS.INTERSECTS: return i18n.translate('xpack.maps.common.esSpatialRelation.intersectsLabel', { @@ -40,6 +41,7 @@ export function getEsSpatialRelationLabel(spatialRelation) { return i18n.translate('xpack.maps.common.esSpatialRelation.withinLabel', { defaultMessage: 'within', }); + // @ts-ignore case ES_SPATIAL_RELATIONS.CONTAINS: return i18n.translate('xpack.maps.common.esSpatialRelation.containsLabel', { defaultMessage: 'contains', diff --git a/x-pack/legacy/plugins/maps/index.js b/x-pack/legacy/plugins/maps/index.js index 4f679905fc352..247dc8115c5c3 100644 --- a/x-pack/legacy/plugins/maps/index.js +++ b/x-pack/legacy/plugins/maps/index.js @@ -54,7 +54,7 @@ export function maps(kibana) { }, embeddableFactories: ['plugins/maps/embeddable/map_embeddable_factory'], inspectorViews: ['plugins/maps/inspector/views/register_views'], - home: ['plugins/maps/register_feature'], + home: ['plugins/maps/legacy_register_feature'], styleSheetPaths: `${__dirname}/public/index.scss`, savedObjectSchemas: { 'maps-telemetry': { diff --git a/x-pack/legacy/plugins/maps/public/feature_catalogue_entry.ts b/x-pack/legacy/plugins/maps/public/feature_catalogue_entry.ts new file mode 100644 index 0000000000000..fdda76b4e1212 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/feature_catalogue_entry.ts @@ -0,0 +1,21 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { APP_ID, APP_ICON } from '../common/constants'; +import { getAppTitle } from '../common/i18n_getters'; +import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; + +export const featureCatalogueEntry = { + id: APP_ID, + title: getAppTitle(), + description: i18n.translate('xpack.maps.feature.appDescription', { + defaultMessage: 'Explore geospatial data from Elasticsearch and the Elastic Maps Service', + }), + icon: APP_ICON, + path: '/app/maps', + showOnHomePage: true, + category: FeatureCatalogueCategory.DATA, +}; diff --git a/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/index.js b/x-pack/legacy/plugins/maps/public/legacy_register_feature.ts similarity index 53% rename from x-pack/legacy/plugins/ml/server/models/file_data_visualizer/index.js rename to x-pack/legacy/plugins/maps/public/legacy_register_feature.ts index 3bda5599e7181..00f788f267d4b 100644 --- a/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/index.js +++ b/x-pack/legacy/plugins/maps/public/legacy_register_feature.ts @@ -4,5 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -export { fileDataVisualizerProvider } from './file_data_visualizer'; -export { importDataProvider } from './import_data'; +import { npSetup } from 'ui/new_platform'; +import { featureCatalogueEntry } from './feature_catalogue_entry'; + +const { + plugins: { home }, +} = npSetup; + +home.featureCatalogue.register(featureCatalogueEntry); diff --git a/x-pack/legacy/plugins/maps/public/plugin.ts b/x-pack/legacy/plugins/maps/public/plugin.ts index e5f765a11d219..e2af53d59671f 100644 --- a/x-pack/legacy/plugins/maps/public/plugin.ts +++ b/x-pack/legacy/plugins/maps/public/plugin.ts @@ -11,6 +11,9 @@ import { wrapInI18nContext } from 'ui/i18n'; import { MapListing } from './components/map_listing'; // @ts-ignore import { setLicenseId, setInspector } from './kibana_services'; +import { HomePublicPluginSetup } from '../../../../../src/plugins/home/public'; +import { LicensingPluginSetup } from '../../../../plugins/licensing/public'; +import { featureCatalogueEntry } from './feature_catalogue_entry'; /** * These are the interfaces with your public contracts. You should export these @@ -20,14 +23,20 @@ import { setLicenseId, setInspector } from './kibana_services'; export type MapsPluginSetup = ReturnType; export type MapsPluginStart = ReturnType; +interface MapsPluginSetupDependencies { + __LEGACY: any; + np: { + licensing?: LicensingPluginSetup; + home: HomePublicPluginSetup; + }; +} + /** @internal */ export class MapsPlugin implements Plugin { - public setup(core: any, plugins: any) { - const { - __LEGACY: { uiModules }, - np: { licensing }, - } = plugins; - + public setup( + core: any, + { __LEGACY: { uiModules }, np: { licensing, home } }: MapsPluginSetupDependencies + ) { uiModules .get('app/maps', ['ngRoute', 'react']) .directive('mapListing', function(reactDirective: any) { @@ -35,8 +44,10 @@ export class MapsPlugin implements Plugin { }); if (licensing) { - licensing.license$.subscribe(({ uid }: { uid: string }) => setLicenseId(uid)); + licensing.license$.subscribe(({ uid }) => setLicenseId(uid)); } + + home.featureCatalogue.register(featureCatalogueEntry); } public start(core: CoreStart, plugins: any) { diff --git a/x-pack/legacy/plugins/maps/public/register_feature.js b/x-pack/legacy/plugins/maps/public/register_feature.js deleted file mode 100644 index afd7fb061500d..0000000000000 --- a/x-pack/legacy/plugins/maps/public/register_feature.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - FeatureCatalogueRegistryProvider, - FeatureCatalogueCategory, -} from 'ui/registry/feature_catalogue'; -import { i18n } from '@kbn/i18n'; -import { APP_ID, APP_ICON } from '../common/constants'; -import { getAppTitle } from '../common/i18n_getters'; - -FeatureCatalogueRegistryProvider.register(() => { - return { - id: APP_ID, - title: getAppTitle(), - description: i18n.translate('xpack.maps.feature.appDescription', { - defaultMessage: 'Explore geospatial data from Elasticsearch and the Elastic Maps Service', - }), - icon: APP_ICON, - path: '/app/maps', - showOnHomePage: true, - category: FeatureCatalogueCategory.DATA, - }; -}); diff --git a/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx b/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx index 2cb58f9c9d81c..1e24bfec6de5e 100644 --- a/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx +++ b/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx @@ -218,7 +218,10 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, searchQuery }) } return ( - + @@ -337,6 +340,7 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, searchQuery }) = React.memo( : searchError; return ( - + diff --git a/x-pack/legacy/plugins/ml/server/lib/spaces_utils.ts b/x-pack/legacy/plugins/ml/server/lib/spaces_utils.ts index 115e7fe6ba434..92373bae4ea1d 100644 --- a/x-pack/legacy/plugins/ml/server/lib/spaces_utils.ts +++ b/x-pack/legacy/plugins/ml/server/lib/spaces_utils.ts @@ -5,8 +5,8 @@ */ import { Request } from 'hapi'; +import { Space } from '../../../../../plugins/spaces/server'; import { LegacySpacesPlugin } from '../../../spaces'; -import { Space } from '../../../spaces/common/model/space'; interface GetActiveSpaceResponse { valid: boolean; diff --git a/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.js b/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.js deleted file mode 100644 index 28bb7c24cf12e..0000000000000 --- a/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.js +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Boom from 'boom'; -import fs from 'fs'; -import os from 'os'; -const util = require('util'); -// const readFile = util.promisify(fs.readFile); -const readdir = util.promisify(fs.readdir); -const writeFile = util.promisify(fs.writeFile); - -export function fileDataVisualizerProvider(callWithRequest) { - async function analyzeFile(data, overrides) { - let cached = false; - let results = []; - - try { - results = await callWithRequest('ml.fileStructure', { body: data, ...overrides }); - if (false) { - // disabling caching for now - cached = await cacheData(data); - } - } catch (error) { - const err = error.message !== undefined ? error.message : error; - throw Boom.badRequest(err); - } - - const { hasOverrides, reducedOverrides } = formatOverrides(overrides); - - return { - ...(hasOverrides && { overrides: reducedOverrides }), - cached, - results, - }; - } - - async function cacheData(data) { - const outputPath = `${os.tmpdir()}/kibana-ml`; - const tempFile = 'es-ml-tempFile'; - const tempFilePath = `${outputPath}/${tempFile}`; - - try { - createOutputDir(outputPath); - await deleteOutputFiles(outputPath); - await writeFile(tempFilePath, data); - return true; - } catch (error) { - return false; - } - } - - function createOutputDir(dir) { - if (fs.existsSync(dir) === false) { - fs.mkdirSync(dir); - } - } - - async function deleteOutputFiles(outputPath) { - const files = await readdir(outputPath); - files.forEach(f => { - fs.unlinkSync(`${outputPath}/${f}`); - }); - } - - return { - analyzeFile, - }; -} - -function formatOverrides(overrides) { - let hasOverrides = false; - - const reducedOverrides = Object.keys(overrides).reduce((p, c) => { - if (overrides[c] !== '') { - p[c] = overrides[c]; - hasOverrides = true; - } - return p; - }, {}); - - if (reducedOverrides.column_names !== undefined) { - reducedOverrides.column_names = reducedOverrides.column_names.split(','); - } - - if (reducedOverrides.has_header_row !== undefined) { - reducedOverrides.has_header_row = reducedOverrides.has_header_row === 'true'; - } - - if (reducedOverrides.should_trim_fields !== undefined) { - reducedOverrides.should_trim_fields = reducedOverrides.should_trim_fields === 'true'; - } - - return { - reducedOverrides, - hasOverrides, - }; -} diff --git a/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.ts b/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.ts new file mode 100644 index 0000000000000..fd5b5221393fc --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.ts @@ -0,0 +1,103 @@ +/* + * 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 Boom from 'boom'; +import { RequestHandlerContext } from 'kibana/server'; + +export type InputData = any[]; + +export interface InputOverrides { + [key: string]: string; +} + +export type FormattedOverrides = InputOverrides & { + column_names: string[]; + has_header_row: boolean; + should_trim_fields: boolean; +}; + +export interface AnalysisResult { + results: { + charset: string; + has_header_row: boolean; + has_byte_order_marker: boolean; + format: string; + field_stats: { + [fieldName: string]: { + count: number; + cardinality: number; + top_hits: Array<{ count: number; value: any }>; + }; + }; + sample_start: string; + num_messages_analyzed: number; + mappings: { + [fieldName: string]: { + type: string; + }; + }; + quote: string; + delimiter: string; + need_client_timezone: boolean; + num_lines_analyzed: number; + column_names: string[]; + }; + overrides?: FormattedOverrides; +} + +export function fileDataVisualizerProvider(context: RequestHandlerContext) { + async function analyzeFile(data: any, overrides: any): Promise { + let results = []; + + try { + results = await context.ml!.mlClient.callAsCurrentUser('ml.fileStructure', { + body: data, + ...overrides, + }); + } catch (error) { + const err = error.message !== undefined ? error.message : error; + throw Boom.badRequest(err); + } + + const { hasOverrides, reducedOverrides } = formatOverrides(overrides); + + return { + ...(hasOverrides && { overrides: reducedOverrides }), + results, + }; + } + + return { + analyzeFile, + }; +} + +function formatOverrides(overrides: InputOverrides) { + let hasOverrides = false; + + const reducedOverrides: FormattedOverrides = Object.keys(overrides).reduce((acc, overrideKey) => { + const overrideValue: string = overrides[overrideKey]; + if (overrideValue !== '') { + if (overrideKey === 'column_names') { + acc.column_names = overrideValue.split(','); + } else if (overrideKey === 'has_header_row') { + acc.has_header_row = overrideValue === 'true'; + } else if (overrideKey === 'should_trim_fields') { + acc.should_trim_fields = overrideValue === 'true'; + } else { + acc[overrideKey] = overrideValue; + } + + hasOverrides = true; + } + return acc; + }, {} as FormattedOverrides); + + return { + reducedOverrides, + hasOverrides, + }; +} diff --git a/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/import_data.js b/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/import_data.ts similarity index 71% rename from x-pack/legacy/plugins/ml/server/models/file_data_visualizer/import_data.js rename to x-pack/legacy/plugins/ml/server/models/file_data_visualizer/import_data.ts index 644a137fbc092..008efb43a6c07 100644 --- a/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/import_data.js +++ b/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/import_data.ts @@ -4,10 +4,43 @@ * you may not use this file except in compliance with the Elastic License. */ +import { RequestHandlerContext } from 'kibana/server'; import { INDEX_META_DATA_CREATED_BY } from '../../../common/constants/file_datavisualizer'; +import { InputData } from './file_data_visualizer'; -export function importDataProvider(callWithRequest) { - async function importData(id, index, settings, mappings, ingestPipeline, data) { +export interface Settings { + pipeline?: string; + index: string; + body: any[]; + [key: string]: any; +} + +export interface Mappings { + [key: string]: any; +} + +export interface InjectPipeline { + id: string; + pipeline: any; +} + +interface Failure { + item: number; + reason: string; + doc: any; +} + +export function importDataProvider(context: RequestHandlerContext) { + const callAsCurrentUser = context.ml!.mlClient.callAsCurrentUser; + + async function importData( + id: string, + index: string, + settings: Settings, + mappings: Mappings, + ingestPipeline: InjectPipeline, + data: InputData + ) { let createdIndex; let createdPipelineId; const docCount = data.length; @@ -35,7 +68,7 @@ export function importDataProvider(callWithRequest) { createdPipelineId = pipelineId; } - let failures = []; + let failures: Failure[] = []; if (data.length) { const resp = await indexData(index, createdPipelineId, data); if (resp.success === false) { @@ -72,8 +105,8 @@ export function importDataProvider(callWithRequest) { } } - async function createIndex(index, settings, mappings) { - const body = { + async function createIndex(index: string, settings: Settings, mappings: Mappings) { + const body: { mappings: Mappings; settings?: Settings } = { mappings: { _meta: { created_by: INDEX_META_DATA_CREATED_BY, @@ -86,10 +119,10 @@ export function importDataProvider(callWithRequest) { body.settings = settings; } - await callWithRequest('indices.create', { index, body }); + await callAsCurrentUser('indices.create', { index, body }); } - async function indexData(index, pipelineId, data) { + async function indexData(index: string, pipelineId: string, data: InputData) { try { const body = []; for (let i = 0; i < data.length; i++) { @@ -97,12 +130,12 @@ export function importDataProvider(callWithRequest) { body.push(data[i]); } - const settings = { index, body }; + const settings: Settings = { index, body }; if (pipelineId !== undefined) { settings.pipeline = pipelineId; } - const resp = await callWithRequest('bulk', settings); + const resp = await callAsCurrentUser('bulk', settings); if (resp.errors) { throw resp; } else { @@ -113,7 +146,7 @@ export function importDataProvider(callWithRequest) { }; } } catch (error) { - let failures = []; + let failures: Failure[] = []; let ingestError = false; if (error.errors !== undefined && Array.isArray(error.items)) { // an expected error where some or all of the bulk request @@ -134,11 +167,11 @@ export function importDataProvider(callWithRequest) { } } - async function createPipeline(id, pipeline) { - return await callWithRequest('ingest.putPipeline', { id, body: pipeline }); + async function createPipeline(id: string, pipeline: any) { + return await callAsCurrentUser('ingest.putPipeline', { id, body: pipeline }); } - function getFailures(items, data) { + function getFailures(items: any[], data: InputData): Failure[] { const failures = []; for (let i = 0; i < items.length; i++) { const item = items[i]; diff --git a/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/index.ts b/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/index.ts new file mode 100644 index 0000000000000..94529dc111696 --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/index.ts @@ -0,0 +1,14 @@ +/* + * 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. + */ + +export { + fileDataVisualizerProvider, + InputOverrides, + InputData, + AnalysisResult, +} from './file_data_visualizer'; + +export { importDataProvider, Settings, InjectPipeline, Mappings } from './import_data'; diff --git a/x-pack/legacy/plugins/ml/server/new_platform/data_analytics_schema.ts b/x-pack/legacy/plugins/ml/server/new_platform/data_analytics_schema.ts index f5d72c51dc070..21454fa884b82 100644 --- a/x-pack/legacy/plugins/ml/server/new_platform/data_analytics_schema.ts +++ b/x-pack/legacy/plugins/ml/server/new_platform/data_analytics_schema.ts @@ -13,8 +13,16 @@ export const dataAnalyticsJobConfigSchema = { results_field: schema.maybe(schema.string()), }), source: schema.object({ - index: schema.string(), + index: schema.oneOf([schema.string(), schema.arrayOf(schema.string())]), + query: schema.maybe(schema.any()), + _source: schema.maybe( + schema.object({ + includes: schema.maybe(schema.arrayOf(schema.maybe(schema.string()))), + excludes: schema.maybe(schema.arrayOf(schema.maybe(schema.string()))), + }) + ), }), + allow_lazy_start: schema.maybe(schema.boolean()), analysis: schema.any(), analyzed_fields: schema.any(), model_memory_limit: schema.string(), diff --git a/x-pack/legacy/plugins/ml/server/routes/apidoc.json b/x-pack/legacy/plugins/ml/server/routes/apidoc.json index 574065446827d..1be31e2316228 100644 --- a/x-pack/legacy/plugins/ml/server/routes/apidoc.json +++ b/x-pack/legacy/plugins/ml/server/routes/apidoc.json @@ -3,7 +3,6 @@ "version": "0.1.0", "description": "ML Kibana API", "title": "ML Kibana API", - "url" : "/api/ml/", "order": [ "DataFrameAnalytics", "GetDataFrameAnalytics", @@ -34,6 +33,9 @@ "ForecastAnomalyDetector", "GetOverallBuckets", "GetCategories", + "FileDataVisualizer", + "AnalyzeFile", + "ImportFile" "ResultsService", "GetAnomaliesTableData", "GetCategoryDefinition", diff --git a/x-pack/legacy/plugins/ml/server/routes/data_frame_analytics.ts b/x-pack/legacy/plugins/ml/server/routes/data_frame_analytics.ts index 67fa2fba46f1a..f134820adbb48 100644 --- a/x-pack/legacy/plugins/ml/server/routes/data_frame_analytics.ts +++ b/x-pack/legacy/plugins/ml/server/routes/data_frame_analytics.ts @@ -156,7 +156,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti params: schema.object({ analyticsId: schema.string(), }), - body: schema.object({ ...dataAnalyticsJobConfigSchema }), + body: schema.object(dataAnalyticsJobConfigSchema), }, }, licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { diff --git a/x-pack/legacy/plugins/ml/server/routes/file_data_visualizer.js b/x-pack/legacy/plugins/ml/server/routes/file_data_visualizer.js deleted file mode 100644 index fc6a0ff756928..0000000000000 --- a/x-pack/legacy/plugins/ml/server/routes/file_data_visualizer.js +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { callWithRequestFactory } from '../client/call_with_request_factory'; -import { wrapError } from '../client/errors'; -import { fileDataVisualizerProvider, importDataProvider } from '../models/file_data_visualizer'; -import { MAX_BYTES } from '../../common/constants/file_datavisualizer'; - -import { incrementFileDataVisualizerIndexCreationCount } from '../lib/ml_telemetry/ml_telemetry'; - -function analyzeFiles(callWithRequest, data, overrides) { - const { analyzeFile } = fileDataVisualizerProvider(callWithRequest); - return analyzeFile(data, overrides); -} - -function importData(callWithRequest, id, index, settings, mappings, ingestPipeline, data) { - const { importData: importDataFunc } = importDataProvider(callWithRequest); - return importDataFunc(id, index, settings, mappings, ingestPipeline, data); -} - -export function fileDataVisualizerRoutes({ - commonRouteConfig, - elasticsearchPlugin, - route, - savedObjects, -}) { - route({ - method: 'POST', - path: '/api/ml/file_data_visualizer/analyze_file', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - const data = request.payload; - - return analyzeFiles(callWithRequest, data, request.query).catch(wrapError); - }, - config: { - ...commonRouteConfig, - payload: { maxBytes: MAX_BYTES }, - }, - }); - - route({ - method: 'POST', - path: '/api/ml/file_data_visualizer/import', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - const { id } = request.query; - const { index, data, settings, mappings, ingestPipeline } = request.payload; - - // `id` being `undefined` tells us that this is a new import due to create a new index. - // follow-up import calls to just add additional data will include the `id` of the created - // index, we'll ignore those and don't increment the counter. - if (id === undefined) { - incrementFileDataVisualizerIndexCreationCount(elasticsearchPlugin, savedObjects); - } - - return importData(callWithRequest, id, index, settings, mappings, ingestPipeline, data).catch( - wrapError - ); - }, - config: { - ...commonRouteConfig, - payload: { maxBytes: MAX_BYTES }, - }, - }); -} diff --git a/x-pack/legacy/plugins/ml/server/routes/file_data_visualizer.ts b/x-pack/legacy/plugins/ml/server/routes/file_data_visualizer.ts new file mode 100644 index 0000000000000..95f2a9fe7298f --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/routes/file_data_visualizer.ts @@ -0,0 +1,159 @@ +/* + * 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 { schema } from '@kbn/config-schema'; +import { RequestHandlerContext } from 'kibana/server'; +import { MAX_BYTES } from '../../common/constants/file_datavisualizer'; +import { wrapError } from '../client/error_wrapper'; +import { + InputOverrides, + InputData, + fileDataVisualizerProvider, + importDataProvider, + Settings, + InjectPipeline, + Mappings, +} from '../models/file_data_visualizer'; + +import { licensePreRoutingFactory } from '../new_platform/licence_check_pre_routing_factory'; +import { RouteInitialization } from '../new_platform/plugin'; +import { incrementFileDataVisualizerIndexCreationCount } from '../lib/ml_telemetry'; + +function analyzeFiles(context: RequestHandlerContext, data: InputData, overrides: InputOverrides) { + const { analyzeFile } = fileDataVisualizerProvider(context); + return analyzeFile(data, overrides); +} + +function importData( + context: RequestHandlerContext, + id: string, + index: string, + settings: Settings, + mappings: Mappings, + ingestPipeline: InjectPipeline, + data: InputData +) { + const { importData: importDataFunc } = importDataProvider(context); + return importDataFunc(id, index, settings, mappings, ingestPipeline, data); +} + +/** + * Routes for the file data visualizer. + */ +export function fileDataVisualizerRoutes({ + router, + xpackMainPlugin, + savedObjects, + elasticsearchPlugin, +}: RouteInitialization) { + /** + * @apiGroup FileDataVisualizer + * + * @api {post} /api/ml/file_data_visualizer/analyze_file Analyze file data + * @apiName AnalyzeFile + * @apiDescription Performs analysis of the file data. + */ + router.post( + { + path: '/api/ml/file_data_visualizer/analyze_file', + validate: { + body: schema.any(), + query: schema.maybe( + schema.object({ + charset: schema.maybe(schema.string()), + column_names: schema.maybe(schema.string()), + delimiter: schema.maybe(schema.string()), + explain: schema.maybe(schema.string()), + format: schema.maybe(schema.string()), + grok_pattern: schema.maybe(schema.string()), + has_header_row: schema.maybe(schema.string()), + line_merge_size_limit: schema.maybe(schema.string()), + lines_to_sample: schema.maybe(schema.string()), + quote: schema.maybe(schema.string()), + should_trim_fields: schema.maybe(schema.string()), + timeout: schema.maybe(schema.string()), + timestamp_field: schema.maybe(schema.string()), + timestamp_format: schema.maybe(schema.string()), + }) + ), + }, + options: { + body: { + accepts: ['text/*', 'application/json'], + maxBytes: MAX_BYTES, + }, + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const result = await analyzeFiles(context, request.body, request.query); + return response.ok({ body: result }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup FileDataVisualizer + * + * @api {post} /api/ml/file_data_visualizer/import Import file data + * @apiName ImportFile + * @apiDescription Imports file data into elasticsearch index. + */ + router.post( + { + path: '/api/ml/file_data_visualizer/import', + validate: { + query: schema.object({ + id: schema.maybe(schema.string()), + }), + body: schema.object({ + index: schema.maybe(schema.string()), + data: schema.arrayOf(schema.any()), + settings: schema.maybe(schema.any()), + mappings: schema.any(), + ingestPipeline: schema.object({ + id: schema.maybe(schema.string()), + pipeline: schema.maybe(schema.any()), + }), + }), + }, + options: { + body: { + accepts: ['application/json'], + maxBytes: MAX_BYTES, + }, + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const { id } = request.query; + const { index, data, settings, mappings, ingestPipeline } = request.body; + + // `id` being `undefined` tells us that this is a new import due to create a new index. + // follow-up import calls to just add additional data will include the `id` of the created + // index, we'll ignore those and don't increment the counter. + if (id === undefined) { + await incrementFileDataVisualizerIndexCreationCount(elasticsearchPlugin, savedObjects!); + } + + const result = await importData( + context, + id, + index, + settings, + mappings, + ingestPipeline, + data + ); + return response.ok({ body: result }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); +} diff --git a/x-pack/legacy/plugins/monitoring/public/register_feature.js b/x-pack/legacy/plugins/monitoring/public/register_feature.js deleted file mode 100644 index f275662bfb077..0000000000000 --- a/x-pack/legacy/plugins/monitoring/public/register_feature.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import chrome from 'ui/chrome'; -import { i18n } from '@kbn/i18n'; -import { - FeatureCatalogueRegistryProvider, - FeatureCatalogueCategory, -} from 'ui/registry/feature_catalogue'; - -if (chrome.getInjected('monitoringUiEnabled')) { - FeatureCatalogueRegistryProvider.register(() => { - return { - id: 'monitoring', - title: i18n.translate('xpack.monitoring.monitoringTitle', { - defaultMessage: 'Monitoring', - }), - description: i18n.translate('xpack.monitoring.monitoringDescription', { - defaultMessage: 'Track the real-time health and performance of your Elastic Stack.', - }), - icon: 'monitoringApp', - path: '/app/monitoring', - showOnHomePage: true, - category: FeatureCatalogueCategory.ADMIN, - }; - }); -} diff --git a/x-pack/legacy/plugins/monitoring/public/register_feature.ts b/x-pack/legacy/plugins/monitoring/public/register_feature.ts new file mode 100644 index 0000000000000..9b72e01a19394 --- /dev/null +++ b/x-pack/legacy/plugins/monitoring/public/register_feature.ts @@ -0,0 +1,30 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import chrome from 'ui/chrome'; +import { npSetup } from 'ui/new_platform'; +import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; + +const { + plugins: { home }, +} = npSetup; + +if (chrome.getInjected('monitoringUiEnabled')) { + home.featureCatalogue.register({ + id: 'monitoring', + title: i18n.translate('xpack.monitoring.monitoringTitle', { + defaultMessage: 'Monitoring', + }), + description: i18n.translate('xpack.monitoring.monitoringDescription', { + defaultMessage: 'Track the real-time health and performance of your Elastic Stack.', + }), + icon: 'monitoringApp', + path: '/app/monitoring', + showOnHomePage: true, + category: FeatureCatalogueCategory.ADMIN, + }); +} diff --git a/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.test.ts b/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.test.ts index 2fec949f5692e..ec00ece9e6ee2 100644 --- a/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.test.ts +++ b/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.test.ts @@ -63,6 +63,7 @@ const alertExecutorOptions: LicenseExpirationAlertExecutorOptions = { spaceId: '', name: '', tags: [], + previousStartedAt: null, createdBy: null, updatedBy: null, }; diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.test.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.test.ts index d1fa44773972f..9ab434e6a058b 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.test.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.test.ts @@ -6,7 +6,10 @@ import expect from '@kbn/expect'; -import { fieldFormats } from '../../../../../../../../src/plugins/data/server'; +import { + fieldFormats, + FieldFormatsGetConfigFn, +} from '../../../../../../../../src/plugins/data/server'; import { fieldFormatMapFactory } from './field_format_map'; type ConfigValue = { number: { id: string; params: {} } } | string; @@ -29,7 +32,7 @@ describe('field format map', function() { number: { id: 'number', params: {} }, }; configMock['format:number:defaultPattern'] = '0,0.[000]'; - const getConfig = ((key: string) => configMock[key]) as fieldFormats.GetConfigFn; + const getConfig = ((key: string) => configMock[key]) as FieldFormatsGetConfigFn; const testValue = '4000'; const fieldFormatsRegistry = new fieldFormats.FieldFormatsRegistry(); diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.ts index dba97b508f93e..e1459e195d9f6 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.ts @@ -5,7 +5,10 @@ */ import _ from 'lodash'; -import { fieldFormats } from '../../../../../../../../src/plugins/data/server'; +import { + FieldFormatConfig, + IFieldFormatsRegistry, +} from '../../../../../../../../src/plugins/data/server'; interface IndexPatternSavedObject { attributes: { @@ -25,7 +28,7 @@ interface IndexPatternSavedObject { */ export function fieldFormatMapFactory( indexPatternSavedObject: IndexPatternSavedObject, - fieldFormatsRegistry: fieldFormats.FieldFormatsRegistry + fieldFormatsRegistry: IFieldFormatsRegistry ) { const formatsMap = new Map(); @@ -33,7 +36,7 @@ export function fieldFormatMapFactory( if (_.has(indexPatternSavedObject, 'attributes.fieldFormatMap')) { const fieldFormatMap = JSON.parse(indexPatternSavedObject.attributes.fieldFormatMap); Object.keys(fieldFormatMap).forEach(fieldName => { - const formatConfig: fieldFormats.IFieldFormatConfig = fieldFormatMap[fieldName]; + const formatConfig: FieldFormatConfig = fieldFormatMap[fieldName]; if (!_.isEmpty(formatConfig)) { formatsMap.set( diff --git a/x-pack/legacy/plugins/reporting/public/register_feature.js b/x-pack/legacy/plugins/reporting/public/register_feature.js deleted file mode 100644 index 98de06fa16e33..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/register_feature.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - FeatureCatalogueRegistryProvider, - FeatureCatalogueCategory, -} from 'ui/registry/feature_catalogue'; - -import { i18n } from '@kbn/i18n'; - -FeatureCatalogueRegistryProvider.register(() => { - return { - id: 'reporting', - title: i18n.translate('xpack.reporting.registerFeature.reportingTitle', { - defaultMessage: 'Reporting', - }), - description: i18n.translate('xpack.reporting.registerFeature.reportingDescription', { - defaultMessage: 'Manage your reports generated from Discover, Visualize, and Dashboard.', - }), - icon: 'reportingApp', - path: '/app/kibana#/management/kibana/reporting', - showOnHomePage: false, - category: FeatureCatalogueCategory.ADMIN, - }; -}); diff --git a/x-pack/legacy/plugins/reporting/public/register_feature.ts b/x-pack/legacy/plugins/reporting/public/register_feature.ts new file mode 100644 index 0000000000000..4e8d32facfcec --- /dev/null +++ b/x-pack/legacy/plugins/reporting/public/register_feature.ts @@ -0,0 +1,27 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { npSetup } from 'ui/new_platform'; +import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; + +const { + plugins: { home }, +} = npSetup; + +home.featureCatalogue.register({ + id: 'reporting', + title: i18n.translate('xpack.reporting.registerFeature.reportingTitle', { + defaultMessage: 'Reporting', + }), + description: i18n.translate('xpack.reporting.registerFeature.reportingDescription', { + defaultMessage: 'Manage your reports generated from Discover, Visualize, and Dashboard.', + }), + icon: 'reportingApp', + path: '/app/kibana#/management/kibana/reporting', + showOnHomePage: false, + category: FeatureCatalogueCategory.ADMIN, +}); diff --git a/x-pack/legacy/plugins/reporting/public/share_context_menu/register_reporting.tsx b/x-pack/legacy/plugins/reporting/public/share_context_menu/register_reporting.tsx index 8e0da6a69225e..4153c7cdbdb0b 100644 --- a/x-pack/legacy/plugins/reporting/public/share_context_menu/register_reporting.tsx +++ b/x-pack/legacy/plugins/reporting/public/share_context_menu/register_reporting.tsx @@ -8,16 +8,14 @@ import { i18n } from '@kbn/i18n'; import moment from 'moment-timezone'; // @ts-ignore: implicit any for JS file import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; -import { npSetup } from 'ui/new_platform'; +import { npSetup, npStart } from 'ui/new_platform'; import React from 'react'; -import chrome from 'ui/chrome'; import { ScreenCapturePanelContent } from '../components/screen_capture_panel_content'; import { ShareContext } from '../../../../../../src/plugins/share/public'; const { core } = npSetup; async function reportingProvider() { - const injector = await chrome.dangerouslyGetActiveInjector(); const getShareMenuItems = ({ objectType, objectId, @@ -31,7 +29,10 @@ async function reportingProvider() { } // Dashboard only mode does not currently support reporting // https://github.com/elastic/kibana/issues/18286 - if (objectType === 'dashboard' && injector.get('dashboardConfig').getHideWriteControls()) { + if ( + objectType === 'dashboard' && + npStart.plugins.kibanaLegacy.dashboardConfig.getHideWriteControls() + ) { return []; } diff --git a/x-pack/legacy/plugins/security/index.js b/x-pack/legacy/plugins/security/index.js index 9016398463b5f..fd89c40f010b7 100644 --- a/x-pack/legacy/plugins/security/index.js +++ b/x-pack/legacy/plugins/security/index.js @@ -86,6 +86,7 @@ export const security = kibana => tenant: server.newPlatform.setup.core.http.basePath.serverBasePath, }, enableSpaceAwarePrivileges: server.config().get('xpack.spaces.enabled'), + logoutUrl: `${server.newPlatform.setup.core.http.basePath.serverBasePath}/logout`, }; }, }, diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/overview/overview.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/overview/overview.spec.ts index be66fdc86be36..64002aadc86d8 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/overview/overview.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/overview/overview.spec.ts @@ -4,40 +4,29 @@ * you may not use this file except in compliance with the Elastic License. */ -import { OVERVIEW_PAGE } from '../../lib/urls'; -import { clearFetch, stubApi } from '../../lib/fixtures/helpers'; -import { - HOST_STATS, - NETWORK_STATS, - OVERVIEW_HOST_STATS, - OVERVIEW_NETWORK_STATS, - STAT_AUDITD, -} from '../../lib/overview/selectors'; +import { OVERVIEW_PAGE } from '../../../urls/navigation'; +import { HOST_STATS, NETWORK_STATS } from '../../../screens/overview'; +import { expandHostStats, expandNetworkStats } from '../../../tasks/overview'; import { loginAndWaitForPage } from '../../lib/util/helpers'; describe('Overview Page', () => { - beforeEach(() => { - clearFetch(); - stubApi('overview'); + before(() => { + cy.stubSIEMapi('overview'); loginAndWaitForPage(OVERVIEW_PAGE); }); - it('Host and Network stats render with correct values', () => { - cy.get(OVERVIEW_HOST_STATS) - .find('button') - .invoke('click'); - - cy.get(OVERVIEW_NETWORK_STATS) - .find('button') - .invoke('click'); - - cy.get(STAT_AUDITD.domId); + it('Host stats render with correct values', () => { + expandHostStats(); HOST_STATS.forEach(stat => { cy.get(stat.domId) .invoke('text') .should('eq', stat.value); }); + }); + + it('Network stats render with correct values', () => { + expandNetworkStats(); NETWORK_STATS.forEach(stat => { cy.get(stat.domId) diff --git a/x-pack/legacy/plugins/siem/cypress/screens/overview.ts b/x-pack/legacy/plugins/siem/cypress/screens/overview.ts new file mode 100644 index 0000000000000..95facc8974400 --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/screens/overview.ts @@ -0,0 +1,144 @@ +/* + * 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. + */ + +// Host Stats +export const STAT_AUDITD = { + value: '123', + domId: '[data-test-subj="host-stat-auditbeatAuditd"]', +}; +export const ENDGAME_DNS = { + value: '391', + domId: '[data-test-subj="host-stat-endgameDns"]', +}; +export const ENDGAME_FILE = { + value: '392', + domId: '[data-test-subj="host-stat-endgameFile"]', +}; +export const ENDGAME_IMAGE_LOAD = { + value: '393', + domId: '[data-test-subj="host-stat-endgameImageLoad"]', +}; +export const ENDGAME_NETWORK = { + value: '394', + domId: '[data-test-subj="host-stat-endgameNetwork"]', +}; +export const ENDGAME_PROCESS = { + value: '395', + domId: '[data-test-subj="host-stat-endgameProcess"]', +}; +export const ENDGAME_REGISTRY = { + value: '396', + domId: '[data-test-subj="host-stat-endgameRegistry"]', +}; +export const ENDGAME_SECURITY = { + value: '397', + domId: '[data-test-subj="host-stat-endgameSecurity"]', +}; +export const STAT_FILEBEAT = { + value: '890', + domId: '[data-test-subj="host-stat-filebeatSystemModule"]', +}; +export const STAT_FIM = { + value: '345', + domId: '[data-test-subj="host-stat-auditbeatFIM"]', +}; +export const STAT_LOGIN = { + value: '456', + domId: '[data-test-subj="host-stat-auditbeatLogin"]', +}; +export const STAT_PACKAGE = { + value: '567', + domId: '[data-test-subj="host-stat-auditbeatPackage"]', +}; +export const STAT_PROCESS = { + value: '678', + domId: '[data-test-subj="host-stat-auditbeatProcess"]', +}; +export const STAT_USER = { + value: '789', + domId: '[data-test-subj="host-stat-auditbeatUser"]', +}; +export const STAT_WINLOGBEAT_SECURITY = { + value: '70', + domId: '[data-test-subj="host-stat-winlogbeatSecurity"]', +}; +export const STAT_WINLOGBEAT_MWSYSMON_OPERATIONAL = { + value: '30', + domId: '[data-test-subj="host-stat-winlogbeatMWSysmonOperational"]', +}; + +export const HOST_STATS = [ + STAT_AUDITD, + ENDGAME_DNS, + ENDGAME_FILE, + ENDGAME_IMAGE_LOAD, + ENDGAME_NETWORK, + ENDGAME_PROCESS, + ENDGAME_REGISTRY, + ENDGAME_SECURITY, + STAT_FILEBEAT, + STAT_FIM, + STAT_LOGIN, + STAT_PACKAGE, + STAT_PROCESS, + STAT_USER, + STAT_WINLOGBEAT_SECURITY, + STAT_WINLOGBEAT_MWSYSMON_OPERATIONAL, +]; + +// Network Stats +export const STAT_SOCKET = { + value: '578,502', + domId: '[data-test-subj="network-stat-auditbeatSocket"]', +}; +export const STAT_CISCO = { + value: '999', + domId: '[data-test-subj="network-stat-filebeatCisco"]', +}; +export const STAT_NETFLOW = { + value: '2,544', + domId: '[data-test-subj="network-stat-filebeatNetflow"]', +}; +export const STAT_PANW = { + value: '678', + domId: '[data-test-subj="network-stat-filebeatPanw"]', +}; +export const STAT_SURICATA = { + value: '303,699', + domId: '[data-test-subj="network-stat-filebeatSuricata"]', +}; +export const STAT_ZEEK = { + value: '71,129', + domId: '[data-test-subj="network-stat-filebeatZeek"]', +}; +export const STAT_DNS = { + value: '1,090', + domId: '[data-test-subj="network-stat-packetbeatDNS"]', +}; +export const STAT_FLOW = { + value: '722,153', + domId: '[data-test-subj="network-stat-packetbeatFlow"]', +}; +export const STAT_TLS = { + value: '340', + domId: '[data-test-subj="network-stat-packetbeatTLS"]', +}; + +export const NETWORK_STATS = [ + STAT_SOCKET, + STAT_CISCO, + STAT_NETFLOW, + STAT_PANW, + STAT_SURICATA, + STAT_ZEEK, + STAT_DNS, + STAT_FLOW, + STAT_TLS, +]; + +export const OVERVIEW_HOST_STATS = '[data-test-subj="overview-hosts-stats"]'; + +export const OVERVIEW_NETWORK_STATS = '[data-test-subj="overview-network-stats"]'; diff --git a/x-pack/legacy/plugins/siem/cypress/support/commands.js b/x-pack/legacy/plugins/siem/cypress/support/commands.js index 9a2e54b102c5e..e697dbce0f249 100644 --- a/x-pack/legacy/plugins/siem/cypress/support/commands.js +++ b/x-pack/legacy/plugins/siem/cypress/support/commands.js @@ -29,3 +29,13 @@ // // -- This is will overwrite an existing command -- // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) + +Cypress.Commands.add('stubSIEMapi', function(dataFileName) { + cy.on('window:before:load', win => { + // @ts-ignore no null, this is a temp hack see issue above + win.fetch = null; + }); + cy.server(); + cy.fixture(dataFileName).as(`${dataFileName}JSON`); + cy.route('POST', 'api/siem/graphql', `@${dataFileName}JSON`); +}); diff --git a/x-pack/legacy/plugins/spaces/common/index.ts b/x-pack/legacy/plugins/siem/cypress/support/index.d.ts similarity index 65% rename from x-pack/legacy/plugins/spaces/common/index.ts rename to x-pack/legacy/plugins/siem/cypress/support/index.d.ts index 8961c9c5ccf79..5d5173170a9f9 100644 --- a/x-pack/legacy/plugins/spaces/common/index.ts +++ b/x-pack/legacy/plugins/siem/cypress/support/index.d.ts @@ -4,5 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -export { isReservedSpace } from './is_reserved_space'; -export { MAX_SPACE_INITIALS } from './constants'; +declare namespace Cypress { + interface Chainable { + stubSIEMapi(dataFileName: string): Chainable; + } +} diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/overview.ts b/x-pack/legacy/plugins/siem/cypress/tasks/overview.ts new file mode 100644 index 0000000000000..0ca4059a90097 --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/tasks/overview.ts @@ -0,0 +1,21 @@ +/* + * 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 { OVERVIEW_HOST_STATS, OVERVIEW_NETWORK_STATS } from '../screens/overview'; + +export const expand = (statType: string) => { + cy.get(statType) + .find('button') + .invoke('click'); +}; + +export const expandHostStats = () => { + expand(OVERVIEW_HOST_STATS); +}; + +export const expandNetworkStats = () => { + expand(OVERVIEW_NETWORK_STATS); +}; diff --git a/x-pack/legacy/plugins/siem/cypress/urls/navigation.ts b/x-pack/legacy/plugins/siem/cypress/urls/navigation.ts index 4675829df839a..35db3003ac436 100644 --- a/x-pack/legacy/plugins/siem/cypress/urls/navigation.ts +++ b/x-pack/legacy/plugins/siem/cypress/urls/navigation.ts @@ -5,3 +5,4 @@ */ export const TIMELINES_PAGE = '/app/siem#/timelines'; +export const OVERVIEW_PAGE = '/app/siem#/overview'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_dns_directly_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_dns_directly_to_the_internet.json index 72e2ca1490417..56c11c236eecb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_dns_directly_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_dns_directly_to_the_internet.json @@ -4,10 +4,7 @@ "Exclude DNS servers from this rule as this is expected behavior. Endpoints usually query local DNS servers defined in their DHCP scopes, but this may be overridden if a user configures their endpoint to use a remote DNS server. This is uncommon in managed enterprise networks because it could break intranet name resolution when split horizon DNS is utilized. Some consumer VPN services and browser plug-ins may send DNS traffic to remote Internet destinations. In that case, such devices or networks can be excluded from this rule when this is expected behavior." ], "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -42,5 +39,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ftp_file_transfer_protocol_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ftp_file_transfer_protocol_activity_to_the_internet.json index 54daf8a2091a7..a3a692596090c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ftp_file_transfer_protocol_activity_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ftp_file_transfer_protocol_activity_to_the_internet.json @@ -4,10 +4,7 @@ "FTP servers should be excluded from this rule as this is expected behavior. Some business workflows may use FTP for data exchange. These workflows often have expected characteristics such as users, sources, and destinations. FTP activity involving an unusual source or destination may be more suspicious. FTP activity involving a production server that has no known associated FTP workflow or business requirement is often suspicious." ], "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -53,5 +50,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_irc_internet_relay_chat_protocol_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_irc_internet_relay_chat_protocol_activity_to_the_internet.json index d01006a225886..0b5259d3417f5 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_irc_internet_relay_chat_protocol_activity_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_irc_internet_relay_chat_protocol_activity_to_the_internet.json @@ -4,10 +4,7 @@ "IRC activity may be normal behavior for developers and engineers but is unusual for non-engineering end users. IRC activity involving an unusual source or destination may be more suspicious. IRC activity involving a production server is often suspicious. Because these ports are in the ephemeral range, this rule may false under certain conditions, such as when a NAT-ed web server replies to a client which has used a port in the range by coincidence. In this case, these servers can be excluded. Some legacy applications may use these ports, but this is very uncommon and usually only appears in local traffic using private IPs, which does not match this rule's conditions." ], "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -53,5 +50,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_nat_traversal_port_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_nat_traversal_port_activity.json index c66a9e9d77fe4..675fd588a1834 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_nat_traversal_port_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_nat_traversal_port_activity.json @@ -4,10 +4,7 @@ "Some networks may utilize these protocols but usage that is unfamiliar to local network administrators can be unexpected and suspicious. Because this port is in the ephemeral range, this rule may false under certain conditions, such as when an application server with a public IP address replies to a client which has used a UDP port in the range by coincidence. This is uncommon but such servers can be excluded." ], "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -38,5 +35,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_26_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_26_activity.json index 7b26141898532..bc00383f94528 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_26_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_26_activity.json @@ -4,10 +4,7 @@ "Servers that process email traffic may cause false positives and should be excluded from this rule as this is expected behavior." ], "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -57,5 +54,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_8000_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_8000_activity_to_the_internet.json index 7551119cd5a84..f418648bebdb9 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_8000_activity_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_8000_activity_to_the_internet.json @@ -4,10 +4,7 @@ "Because this port is in the ephemeral range, this rule may false under certain conditions, such as when a NATed web server replies to a client which has used a port in the range by coincidence. In this case, such servers can be excluded. Some applications may use this port but this is very uncommon and usually appears in local traffic using private IPs, which this rule does not match. Some cloud environments, particularly development environments, may use this port when VPNs or direct connects are not in use and cloud instances are accessed across the Internet." ], "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -38,5 +35,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_pptp_point_to_point_tunneling_protocol_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_pptp_point_to_point_tunneling_protocol_activity.json index eeb38f756c67a..2321b813a1552 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_pptp_point_to_point_tunneling_protocol_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_pptp_point_to_point_tunneling_protocol_activity.json @@ -4,10 +4,7 @@ "Some networks may utilize PPTP protocols but this is uncommon as more modern VPN technologies are available. Usage that is unfamiliar to local network administrators can be unexpected and suspicious. Torrenting applications may use this port. Because this port is in the ephemeral range, this rule may false under certain conditions, such as when an application server replies to a client that used this port by coincidence. This is uncommon but such servers can be excluded." ], "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -38,5 +35,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_proxy_port_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_proxy_port_activity_to_the_internet.json index 981a7bdffcfed..58bba5b3fa712 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_proxy_port_activity_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_proxy_port_activity_to_the_internet.json @@ -4,10 +4,7 @@ "Some proxied applications may use these ports but this usually occurs in local traffic using private IPs\n which this rule does not match. Proxies are widely used as a security technology but in enterprise environments\n this is usually local traffic which this rule does not match. Internet proxy services using these ports can be\n white-listed if desired. Some screen recording applications may use these ports. Proxy port activity involving\n an unusual source or destination may be more suspicious. Some cloud environments may use this port when VPNs or\n direct connects are not in use and cloud instances are accessed across the Internet. Because these ports are in\n the ephemeral range, this rule may false under certain conditions such as when a NATed web server replies to a\n client which has used a port in the range by coincidence. In this case, such servers can be excluded if desired." ], "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -38,5 +35,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_from_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_from_the_internet.json index 504df93f2f8ed..03e507753cd22 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_from_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_from_the_internet.json @@ -4,10 +4,7 @@ " Some network security policies allow RDP directly from the Internet but usage that is unfamiliar to\n server or network owners can be unexpected and suspicious. RDP services may be exposed directly to the\n Internet in some networks such as cloud environments. In such cases, only RDP gateways, bastions or jump\n servers may be expected expose RDP directly to the Internet and can be exempted from this rule. RDP may\n be required by some work-flows such as remote access and support for specialized software products and\n servers. Such work-flows are usually known and not unexpected." ], "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -68,5 +65,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_to_the_internet.json index 2d9fa6ba06dfd..af2279b2d9008 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_to_the_internet.json @@ -4,10 +4,7 @@ "RDP connections may be made directly to Internet destinations in order to access\n Windows cloud server instances but such connections are usually made only by engineers.\n In such cases, only RDP gateways, bastions or jump servers may be expected Internet\n destinations and can be exempted from this rule. RDP may be required by some work-flows\n such as remote access and support for specialized software products and servers. Such\n work-flows are usually known and not unexpected. Usage that is unfamiliar to server or\n network owners can be unexpected and suspicious." ], "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -53,5 +50,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_from_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_from_the_internet.json index d50c79db81ba5..4539d639a593a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_from_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_from_the_internet.json @@ -1,10 +1,7 @@ { "description": "This rule detects network events that may indicate the use of RPC traffic\nfrom the Internet. RPC is commonly used by system administrators to remotely\ncontrol a system for maintenance or to use shared resources. It should almost\nnever be directly exposed to the Internet, as it is frequently targeted and\nexploited by threat actors as an initial access or back-door vector.\n", "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -35,5 +32,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_to_the_internet.json index ade7b661a7909..dd1b57572bcb3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_to_the_internet.json @@ -1,10 +1,7 @@ { "description": "This rule detects network events that may indicate the use of RPC traffic\nto the Internet. RPC is commonly used by system administrators to remotely\ncontrol a system for maintenance or to use shared resources. It should almost\nnever be directly exposed to the Internet, as it is frequently targeted and\nexploited by threat actors as an initial access or back-door vector.\n", "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -35,5 +32,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smb_windows_file_sharing_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smb_windows_file_sharing_activity_to_the_internet.json index 62c2fafb7404f..8b97df2182992 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smb_windows_file_sharing_activity_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smb_windows_file_sharing_activity_to_the_internet.json @@ -1,10 +1,7 @@ { "description": "This rule detects network events that may indicate the use of Windows\nfile sharing (also called SMB or CIFS) traffic to the Internet. SMB is commonly\nused within networks to share files, printers, and other system resources amongst\ntrusted systems. It should almost never be directly exposed to the Internet, as\nit is frequently targeted and exploited by threat actors as an initial access\nor back-door vector or for data exfiltration.\n", "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -50,5 +47,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smtp_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smtp_to_the_internet.json index 02fca5603910e..c6aa5eef372f4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smtp_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smtp_to_the_internet.json @@ -4,10 +4,7 @@ "NATed servers that process email traffic may false and should be excluded from this rule as this is expected behavior for them. Consumer and personal devices may send email traffic to remote Internet destinations. In this case, such devices or networks can be excluded from this rule if this is expected behavior." ], "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -53,5 +50,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_sql_server_port_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_sql_server_port_activity_to_the_internet.json index 67e6a08ddf791..f11d9705bbda4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_sql_server_port_activity_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_sql_server_port_activity_to_the_internet.json @@ -4,10 +4,7 @@ "Because these ports are in the ephemeral range, this rule may false under certain conditions\n such as when a NATed web server replies to a client which has used a port in the range by\n coincidence. In this case, such servers can be excluded if desired. Some cloud environments may\n use this port when VPNs or direct connects are not in use and database instances are accessed\n directly across the Internet." ], "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -38,5 +35,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_from_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_from_the_internet.json index 052600a0db68a..a95447fc088df 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_from_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_from_the_internet.json @@ -4,10 +4,7 @@ "Some network security policies allow SSH directly from the Internet but usage that is\n unfamiliar to server or network owners can be unexpected and suspicious. SSH services may\n be exposed directly to the Internet in some networks such as cloud environments. In such\n cases, only SSH gateways, bastions or jump servers may be expected expose SSH directly to\n the Internet and can be exempted from this rule. SSH may be required by some work-flows\n such as remote access and support for specialized software products and servers. Such\n work-flows are usually known and not unexpected." ], "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -68,5 +65,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_to_the_internet.json index e3c3135c9240d..b17d35f96324d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_to_the_internet.json @@ -4,10 +4,7 @@ "SSH connections may be made directly to Internet destinations in order to access Linux\n cloud server instances but such connections are usually made only by engineers. In such cases,\n only SSH gateways, bastions or jump servers may be expected Internet destinations and can be\n exempted from this rule. SSH may be required by some work-flows such as remote access and support\n for specialized software products and servers. Such work-flows are usually known and not unexpected.\n Usage that is unfamiliar to server or network owners can be unexpected and suspicious." ], "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -38,5 +35,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_telnet_port_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_telnet_port_activity.json index c05791c8a107d..99813595013cf 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_telnet_port_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_telnet_port_activity.json @@ -4,10 +4,7 @@ "IoT (Internet of Things) devices and networks may use telnet and can be excluded if\n desired. Some business work-flows may use Telnet for administration of older devices. These\n often have a predictable behavior. Telnet activity involving an unusual source or destination\n may be more suspicious. Telnet activity involving a production server that has no known\n associated Telnet work-flow or business requirement is often suspicious." ], "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -68,5 +65,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_tor_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_tor_activity_to_the_internet.json index 64397716eded2..47960f879dfb6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_tor_activity_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_tor_activity_to_the_internet.json @@ -4,10 +4,7 @@ "Tor client activity is uncommon in managed enterprise networks but may be common\n in unmanaged or public networks where few security policies apply. Because these ports\n are in the ephemeral range, this rule may false under certain conditions such as when a\n NATed web server replies to a client which has used one of these ports by coincidence.\n In this case, such servers can be excluded if desired." ], "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -53,5 +50,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_from_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_from_the_internet.json index dc4fbb281f762..d9195a2d2e98c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_from_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_from_the_internet.json @@ -4,10 +4,7 @@ "VNC connections may be received directly to Linux cloud server instances but\n such connections are usually made only by engineers. VNC is less common than SSH\n or RDP but may be required by some work-flows such as remote access and support\n for specialized software products or servers. Such work-flows are usually known\n and not unexpected. Usage that is unfamiliar to server or network owners can be\n unexpected and suspicious." ], "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -53,5 +50,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_to_the_internet.json index 7da5db39d9bfe..57131e28ee9a7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_to_the_internet.json @@ -4,10 +4,7 @@ "VNC connections may be made directly to Linux cloud server instances but such\n connections are usually made only by engineers. VNC is less common than SSH or RDP\n but may be required by some work flows such as remote access and support for\n specialized software products or servers. Such work-flows are usually known and not\n unexpected. Usage that is unfamiliar to server or network owners can be unexpected\n and suspicious." ], "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" + "filebeat-*" ], "language": "kuery", "max_signals": 100, @@ -38,5 +35,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/legacy/plugins/spaces/common/constants.ts b/x-pack/legacy/plugins/spaces/common/constants.ts deleted file mode 100644 index 11882ca2f1b3a..0000000000000 --- a/x-pack/legacy/plugins/spaces/common/constants.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export const DEFAULT_SPACE_ID = `default`; - -/** - * The minimum number of spaces required to show a search control. - */ -export const SPACE_SEARCH_COUNT_THRESHOLD = 8; - -/** - * The maximum number of characters allowed in the Space Avatar's initials - */ -export const MAX_SPACE_INITIALS = 2; - -/** - * The type name used within the Monitoring index to publish spaces stats. - * @type {string} - */ -export const KIBANA_SPACES_STATS_TYPE = 'spaces'; - -/** - * The path to enter a space. - */ -export const ENTER_SPACE_PATH = '/spaces/enter'; diff --git a/x-pack/legacy/plugins/spaces/common/is_reserved_space.test.ts b/x-pack/legacy/plugins/spaces/common/is_reserved_space.test.ts deleted file mode 100644 index dd1372183ed8a..0000000000000 --- a/x-pack/legacy/plugins/spaces/common/is_reserved_space.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { isReservedSpace } from './is_reserved_space'; -import { Space } from './model/space'; - -test('it returns true for reserved spaces', () => { - const space: Space = { - id: '', - name: '', - disabledFeatures: [], - _reserved: true, - }; - - expect(isReservedSpace(space)).toEqual(true); -}); - -test('it returns false for non-reserved spaces', () => { - const space: Space = { - id: '', - name: '', - disabledFeatures: [], - }; - - expect(isReservedSpace(space)).toEqual(false); -}); - -test('it handles empty input', () => { - // @ts-ignore - expect(isReservedSpace()).toEqual(false); -}); diff --git a/x-pack/legacy/plugins/spaces/common/is_reserved_space.ts b/x-pack/legacy/plugins/spaces/common/is_reserved_space.ts deleted file mode 100644 index 788ef80c194ce..0000000000000 --- a/x-pack/legacy/plugins/spaces/common/is_reserved_space.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { get } from 'lodash'; -import { Space } from './model/space'; - -/** - * Returns whether the given Space is reserved or not. - * - * @param space the space - * @returns boolean - */ -export function isReservedSpace(space?: Partial | null): boolean { - return get(space, '_reserved', false); -} diff --git a/x-pack/legacy/plugins/spaces/common/model/space.ts b/x-pack/legacy/plugins/spaces/common/model/space.ts deleted file mode 100644 index c44ce41ec51c0..0000000000000 --- a/x-pack/legacy/plugins/spaces/common/model/space.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export interface Space { - id: string; - name: string; - description?: string; - color?: string; - initials?: string; - disabledFeatures: string[]; - _reserved?: boolean; - imageUrl?: string; -} diff --git a/x-pack/legacy/plugins/spaces/index.ts b/x-pack/legacy/plugins/spaces/index.ts index 2e6b878794777..ab3388ae96475 100644 --- a/x-pack/legacy/plugins/spaces/index.ts +++ b/x-pack/legacy/plugins/spaces/index.ts @@ -17,7 +17,7 @@ import { wrapError } from './server/lib/errors'; import { migrateToKibana660 } from './server/lib/migrations'; // @ts-ignore import { watchStatusAndLicenseToInitialize } from '../../server/lib/watch_status_and_license_to_initialize'; -import { initSpaceSelectorView, initEnterSpaceView } from './server/routes/views'; +import { initEnterSpaceView } from './server/routes/views'; export interface LegacySpacesPlugin { getSpaceId: (request: Legacy.Request) => ReturnType; @@ -50,15 +50,7 @@ export const spaces = (kibana: Record) => uiExports: { styleSheetPaths: resolve(__dirname, 'public/index.scss'), managementSections: [], - apps: [ - { - id: 'space_selector', - title: 'Spaces', - main: 'plugins/spaces/space_selector', - url: 'space_selector', - hidden: true, - }, - ], + apps: [], hacks: ['plugins/spaces/legacy'], mappings, migrations: { @@ -131,11 +123,9 @@ export const spaces = (kibana: Record) => create: (pluginId: string) => new AuditLogger(server, pluginId, server.config(), server.plugins.xpack_main.info), }, - xpackMain: server.plugins.xpack_main, }); initEnterSpaceView(server); - initSpaceSelectorView(server); watchStatusAndLicenseToInitialize(server.plugins.xpack_main, this, async () => { await createDefaultSpace(); diff --git a/x-pack/legacy/plugins/spaces/public/__mocks__/xpack_info.ts b/x-pack/legacy/plugins/spaces/public/__mocks__/xpack_info.ts deleted file mode 100644 index e3467b88dbc61..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/__mocks__/xpack_info.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -jest.mock('../../../xpack_main/public/services/xpack_info', () => { - return { - xpackInfo: { - get: jest.fn().mockImplementation((key: string) => { - if (key === 'features.security.showLinks') { - return true; - } - throw new Error(`unexpected key: ${key}`); - }), - }, - }; -}); diff --git a/x-pack/legacy/plugins/spaces/public/index.scss b/x-pack/legacy/plugins/spaces/public/index.scss index 26269f1d31aa3..bb3481f96bc1e 100644 --- a/x-pack/legacy/plugins/spaces/public/index.scss +++ b/x-pack/legacy/plugins/spaces/public/index.scss @@ -1,16 +1,4 @@ // Import the EUI global scope so we can use EUI constants @import 'src/legacy/ui/public/styles/_styling_constants'; -/* Spaces plugin styles */ - -// Prefix all styles with "spc" to avoid conflicts. -// Examples -// spcChart -// spcChart__legend -// spcChart__legend--small -// spcChart__legend-isLoading - -@import './management/index'; -@import './nav_control/index'; -@import './space_selector/index'; -@import './copy_saved_objects_to_space/index'; +@import '../../../../plugins/spaces/public/index'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/spaces/public/legacy.ts b/x-pack/legacy/plugins/spaces/public/legacy.ts index 200cae5498595..c6740dae81717 100644 --- a/x-pack/legacy/plugins/spaces/public/legacy.ts +++ b/x-pack/legacy/plugins/spaces/public/legacy.ts @@ -4,23 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ -import { npSetup, npStart } from 'ui/new_platform'; +import { SavedObjectsManagementAction } from 'src/legacy/core_plugins/management/public'; +import { npSetup } from 'ui/new_platform'; +import routes from 'ui/routes'; +import { SpacesPluginSetup } from '../../../../plugins/spaces/public'; import { setup as managementSetup } from '../../../../../src/legacy/core_plugins/management/public/legacy'; -import { plugin } from '.'; -import { SpacesPlugin, PluginsSetup, PluginsStart } from './plugin'; -import './management/legacy_page_routes'; -const spacesPlugin: SpacesPlugin = plugin(); - -const pluginsSetup: PluginsSetup = { - home: npSetup.plugins.home, - management: managementSetup, - advancedSettings: npSetup.plugins.advancedSettings, +const legacyAPI = { + registerSavedObjectsManagementAction: (action: SavedObjectsManagementAction) => { + managementSetup.savedObjects.registry.register(action); + }, }; -const pluginsStart: PluginsStart = { - management: npStart.plugins.management, -}; +const spaces = (npSetup.plugins as any).spaces as SpacesPluginSetup; +if (spaces) { + spaces.registerLegacyAPI(legacyAPI); -export const setup = spacesPlugin.setup(npSetup.core, pluginsSetup); -export const start = spacesPlugin.start(npStart.core, pluginsStart); + routes.when('/management/spaces/list', { redirectTo: '/management/kibana/spaces' }); + routes.when('/management/spaces/create', { redirectTo: '/management/kibana/spaces/create' }); + routes.when('/management/spaces/edit/:spaceId', { + redirectTo: '/management/kibana/spaces/edit/:spaceId', + }); +} diff --git a/x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/__snapshots__/secure_space_message.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/__snapshots__/secure_space_message.test.tsx.snap deleted file mode 100644 index bce57527cd55a..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/__snapshots__/secure_space_message.test.tsx.snap +++ /dev/null @@ -1,33 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SecureSpaceMessage doesn't render if security is not enabled 1`] = `""`; - -exports[`SecureSpaceMessage renders if security is enabled 1`] = ` - - - -

- - - , - } - } - /> -

-
-
-`; diff --git a/x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/secure_space_message.test.tsx b/x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/secure_space_message.test.tsx deleted file mode 100644 index b43010fe5f326..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/secure_space_message.test.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import React from 'react'; -import { shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { SecureSpaceMessage } from './secure_space_message'; - -let mockShowLinks: boolean = true; -jest.mock('../../../../../xpack_main/public/services/xpack_info', () => { - return { - xpackInfo: { - get: jest.fn().mockImplementation((key: string) => { - if (key === 'features.security.showLinks') { - return mockShowLinks; - } - throw new Error(`unexpected key: ${key}`); - }), - }, - }; -}); - -describe('SecureSpaceMessage', () => { - it(`doesn't render if security is not enabled`, () => { - mockShowLinks = false; - expect(shallowWithIntl()).toMatchSnapshot(); - }); - - it(`renders if security is enabled`, () => { - mockShowLinks = true; - expect(shallowWithIntl()).toMatchSnapshot(); - }); -}); diff --git a/x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/secure_space_message.tsx b/x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/secure_space_message.tsx deleted file mode 100644 index 746b7e2ac4c98..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/secure_space_message.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { EuiHorizontalRule, EuiLink, EuiText } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; -import React, { Fragment } from 'react'; -// @ts-ignore -import { xpackInfo } from '../../../../../xpack_main/public/services/xpack_info'; - -export const SecureSpaceMessage = ({}) => { - const showSecurityLinks = xpackInfo.get('features.security.showLinks'); - - if (showSecurityLinks) { - const rolesLinkTextAriaLabel = i18n.translate( - 'xpack.spaces.management.secureSpaceMessage.rolesLinkTextAriaLabel', - { defaultMessage: 'Roles management page' } - ); - return ( - - - -

- - - - ), - }} - /> -

-
-
- ); - } - return null; -}; diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap deleted file mode 100644 index 5879ff621d64a..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap +++ /dev/null @@ -1,324 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`EnabledFeatures renders as expected 1`] = ` - - - - - - - - - - } -> - - - -

- -

-
- - -

- -

-

- - - , - } - } - /> -

-
-
- - - -
-
-`; diff --git a/x-pack/legacy/plugins/spaces/public/management/legacy_page_routes.tsx b/x-pack/legacy/plugins/spaces/public/management/legacy_page_routes.tsx deleted file mode 100644 index 8cf4a129e5b8f..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/management/legacy_page_routes.tsx +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -// @ts-ignore -import template from 'plugins/spaces/management/template.html'; -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { I18nContext } from 'ui/i18n'; -// @ts-ignore -import routes from 'ui/routes'; -import { MANAGEMENT_BREADCRUMB } from 'ui/management/breadcrumbs'; -import { npStart } from 'ui/new_platform'; -import { ManageSpacePage } from './edit_space'; -import { SpacesGridPage } from './spaces_grid'; - -import { start as spacesNPStart } from '../legacy'; -import { Space } from '../../common/model/space'; - -const reactRootNodeId = 'manageSpacesReactRoot'; - -function getListBreadcrumbs() { - return [ - MANAGEMENT_BREADCRUMB, - { - text: 'Spaces', - href: '#/management/spaces/list', - }, - ]; -} - -function getCreateBreadcrumbs() { - return [ - ...getListBreadcrumbs(), - { - text: 'Create', - }, - ]; -} - -function getEditBreadcrumbs(space?: Space) { - return [ - ...getListBreadcrumbs(), - { - text: space ? space.name : '...', - }, - ]; -} - -routes.when('/management/spaces/list', { - template, - k7Breadcrumbs: getListBreadcrumbs, - requireUICapability: 'management.kibana.spaces', - controller($scope: any) { - $scope.$$postDigest(() => { - const domNode = document.getElementById(reactRootNodeId); - - const { spacesManager } = spacesNPStart; - - render( - - - , - domNode - ); - - // unmount react on controller destroy - $scope.$on('$destroy', () => { - if (domNode) { - unmountComponentAtNode(domNode); - } - }); - }); - }, -}); - -routes.when('/management/spaces/create', { - template, - k7Breadcrumbs: getCreateBreadcrumbs, - requireUICapability: 'management.kibana.spaces', - controller($scope: any) { - $scope.$$postDigest(() => { - const domNode = document.getElementById(reactRootNodeId); - - const { spacesManager } = spacesNPStart; - - render( - - - , - domNode - ); - - // unmount react on controller destroy - $scope.$on('$destroy', () => { - if (domNode) { - unmountComponentAtNode(domNode); - } - }); - }); - }, -}); - -routes.when('/management/spaces/edit', { - redirectTo: '/management/spaces/list', -}); - -routes.when('/management/spaces/edit/:spaceId', { - template, - k7Breadcrumbs: () => getEditBreadcrumbs(), - requireUICapability: 'management.kibana.spaces', - controller($scope: any, $route: any) { - $scope.$$postDigest(async () => { - const domNode = document.getElementById(reactRootNodeId); - - const { spaceId } = $route.current.params; - - const { spacesManager } = await spacesNPStart; - - render( - - { - npStart.core.chrome.setBreadcrumbs(getEditBreadcrumbs(space)); - }} - capabilities={npStart.core.application.capabilities} - /> - , - domNode - ); - - // unmount react on controller destroy - $scope.$on('$destroy', () => { - if (domNode) { - unmountComponentAtNode(domNode); - } - }); - }); - }, -}); diff --git a/x-pack/legacy/plugins/spaces/public/management/management_service.test.ts b/x-pack/legacy/plugins/spaces/public/management/management_service.test.ts deleted file mode 100644 index fbd39db6969bd..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/management/management_service.test.ts +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { ManagementService } from '.'; - -const mockSections = { - getSection: jest.fn(), - getAllSections: jest.fn(), - navigateToApp: jest.fn(), -}; - -describe('ManagementService', () => { - describe('#start', () => { - it('registers the spaces management page under the kibana section', () => { - const mockKibanaSection = { - hasItem: jest.fn().mockReturnValue(false), - register: jest.fn(), - }; - - const managementStart = { - legacy: { - getSection: jest.fn().mockReturnValue(mockKibanaSection), - }, - sections: mockSections, - }; - - const deps = { - managementStart, - }; - - const service = new ManagementService(); - service.start(deps); - - expect(deps.managementStart.legacy.getSection).toHaveBeenCalledTimes(1); - expect(deps.managementStart.legacy.getSection).toHaveBeenCalledWith('kibana'); - - expect(mockKibanaSection.register).toHaveBeenCalledTimes(1); - expect(mockKibanaSection.register).toHaveBeenCalledWith('spaces', { - name: 'spacesManagementLink', - order: 10, - display: 'Spaces', - url: `#/management/spaces/list`, - }); - }); - - it('will not register the spaces management page twice', () => { - const mockKibanaSection = { - hasItem: jest.fn().mockReturnValue(true), - register: jest.fn(), - }; - - const managementStart = { - legacy: { - getSection: jest.fn().mockReturnValue(mockKibanaSection), - }, - sections: mockSections, - }; - - const deps = { - managementStart, - }; - - const service = new ManagementService(); - service.start(deps); - - expect(mockKibanaSection.register).toHaveBeenCalledTimes(0); - }); - - it('will not register the spaces management page if the kibana section is missing', () => { - const managementStart = { - legacy: { - getSection: jest.fn().mockReturnValue(undefined), - }, - sections: mockSections, - }; - - const deps = { - managementStart, - }; - - const service = new ManagementService(); - service.start(deps); - - expect(deps.managementStart.legacy.getSection).toHaveBeenCalledTimes(1); - }); - }); - - describe('#stop', () => { - it('deregisters the spaces management page', () => { - const mockKibanaSection = { - hasItem: jest - .fn() - .mockReturnValueOnce(false) - .mockReturnValueOnce(true), - register: jest.fn(), - deregister: jest.fn(), - }; - - const managementStart = { - legacy: { - getSection: jest.fn().mockReturnValue(mockKibanaSection), - }, - sections: mockSections, - }; - - const deps = { - managementStart, - }; - - const service = new ManagementService(); - service.start(deps); - - service.stop(); - - expect(mockKibanaSection.register).toHaveBeenCalledTimes(1); - expect(mockKibanaSection.deregister).toHaveBeenCalledTimes(1); - expect(mockKibanaSection.deregister).toHaveBeenCalledWith('spaces'); - }); - }); -}); diff --git a/x-pack/legacy/plugins/spaces/public/management/management_service.ts b/x-pack/legacy/plugins/spaces/public/management/management_service.ts deleted file mode 100644 index ada38f5cf3387..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/management/management_service.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import { i18n } from '@kbn/i18n'; -import { ManagementStart } from 'src/plugins/management/public'; - -interface StartDeps { - managementStart: ManagementStart; -} - -const MANAGE_SPACES_KEY = 'spaces'; - -export class ManagementService { - private kibanaSection!: any; - - public start({ managementStart }: StartDeps) { - this.kibanaSection = managementStart.legacy.getSection('kibana'); - if (this.kibanaSection && !this.kibanaSection.hasItem(MANAGE_SPACES_KEY)) { - this.kibanaSection.register(MANAGE_SPACES_KEY, { - name: 'spacesManagementLink', - order: 10, - display: i18n.translate('xpack.spaces.displayName', { - defaultMessage: 'Spaces', - }), - url: `#/management/spaces/list`, - }); - } - } - - public stop() { - if (this.kibanaSection && this.kibanaSection.hasItem(MANAGE_SPACES_KEY)) { - this.kibanaSection.deregister(MANAGE_SPACES_KEY); - } - } -} diff --git a/x-pack/legacy/plugins/spaces/public/management/template.html b/x-pack/legacy/plugins/spaces/public/management/template.html deleted file mode 100644 index 3cd8e144b43fc..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/management/template.html +++ /dev/null @@ -1,3 +0,0 @@ - -
- diff --git a/x-pack/legacy/plugins/spaces/public/nav_control/components/__snapshots__/spaces_description.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/nav_control/components/__snapshots__/spaces_description.test.tsx.snap deleted file mode 100644 index 8e78f64ac59cb..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/nav_control/components/__snapshots__/spaces_description.test.tsx.snap +++ /dev/null @@ -1,43 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SpacesDescription renders without crashing 1`] = ` - - -

- Organize your dashboards and other saved objects into meaningful categories. -

-
-
- -
-
-`; diff --git a/x-pack/legacy/plugins/spaces/public/nav_control/components/spaces_description.test.tsx b/x-pack/legacy/plugins/spaces/public/nav_control/components/spaces_description.test.tsx deleted file mode 100644 index 157dcab3e0be1..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/nav_control/components/spaces_description.test.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { shallow } from 'enzyme'; -import React from 'react'; -import { SpacesDescription } from './spaces_description'; - -describe('SpacesDescription', () => { - it('renders without crashing', () => { - expect( - shallow( - - ) - ).toMatchSnapshot(); - }); -}); diff --git a/x-pack/legacy/plugins/spaces/public/plugin.tsx b/x-pack/legacy/plugins/spaces/public/plugin.tsx deleted file mode 100644 index e6271ac3a0a70..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/plugin.tsx +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; -import { HomePublicPluginSetup } from 'src/plugins/home/public'; -import { ManagementSetup } from 'src/legacy/core_plugins/management/public'; -import { ManagementStart } from 'src/plugins/management/public'; -import { AdvancedSettingsSetup } from 'src/plugins/advanced_settings/public'; -import { SpacesManager } from './spaces_manager'; -import { initSpacesNavControl } from './nav_control'; -import { createSpacesFeatureCatalogueEntry } from './create_feature_catalogue_entry'; -import { CopySavedObjectsToSpaceService } from './copy_saved_objects_to_space'; -import { AdvancedSettingsService } from './advanced_settings'; -import { ManagementService } from './management'; - -export interface SpacesPluginStart { - spacesManager: SpacesManager | null; -} - -export interface PluginsSetup { - home?: HomePublicPluginSetup; - management: ManagementSetup; - advancedSettings: AdvancedSettingsSetup; -} - -export interface PluginsStart { - management: ManagementStart; -} - -export class SpacesPlugin implements Plugin { - private spacesManager!: SpacesManager; - - private managementService?: ManagementService; - - public setup(core: CoreSetup, plugins: PluginsSetup) { - const serverBasePath = core.injectedMetadata.getInjectedVar('serverBasePath') as string; - this.spacesManager = new SpacesManager(serverBasePath, core.http); - - const copySavedObjectsToSpaceService = new CopySavedObjectsToSpaceService(); - copySavedObjectsToSpaceService.setup({ - spacesManager: this.spacesManager, - managementSetup: plugins.management, - }); - - const advancedSettingsService = new AdvancedSettingsService(); - advancedSettingsService.setup({ - getActiveSpace: () => this.spacesManager.getActiveSpace(), - componentRegistry: plugins.advancedSettings.component, - }); - - if (plugins.home) { - plugins.home.featureCatalogue.register(createSpacesFeatureCatalogueEntry()); - } - } - - public start(core: CoreStart, plugins: PluginsStart) { - initSpacesNavControl(this.spacesManager, core); - - this.managementService = new ManagementService(); - this.managementService.start({ managementStart: plugins.management }); - - return { - spacesManager: this.spacesManager, - }; - } - - public stop() { - if (this.managementService) { - this.managementService.stop(); - this.managementService = undefined; - } - } -} diff --git a/x-pack/legacy/plugins/spaces/public/space_selector/index.tsx b/x-pack/legacy/plugins/spaces/public/space_selector/index.tsx deleted file mode 100644 index c1c1b6dc3a2f3..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/space_selector/index.tsx +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -// @ts-ignore -import template from 'plugins/spaces/space_selector/space_selector.html'; -import chrome from 'ui/chrome'; -import { I18nContext } from 'ui/i18n'; -// @ts-ignore -import { uiModules } from 'ui/modules'; - -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { SpaceSelector } from './space_selector'; - -import { start as spacesNPStart } from '../legacy'; - -const module = uiModules.get('spaces_selector', []); -module.controller('spacesSelectorController', ($scope: any) => { - $scope.$$postDigest(() => { - const domNode = document.getElementById('spaceSelectorRoot'); - - const { spacesManager } = spacesNPStart; - - render( - - - , - domNode - ); - - // unmount react on controller destroy - $scope.$on('$destroy', () => { - if (domNode) { - unmountComponentAtNode(domNode); - } - }); - }); -}); - -chrome.setVisible(false).setRootTemplate(template); diff --git a/x-pack/legacy/plugins/spaces/public/space_selector/space_selector.html b/x-pack/legacy/plugins/spaces/public/space_selector/space_selector.html deleted file mode 100644 index 2dbf9fac3f68b..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/space_selector/space_selector.html +++ /dev/null @@ -1,3 +0,0 @@ -
-
-
diff --git a/x-pack/legacy/plugins/spaces/server/routes/views/enter_space.ts b/x-pack/legacy/plugins/spaces/server/routes/views/enter_space.ts index e560d4278b407..337faa2a18fb6 100644 --- a/x-pack/legacy/plugins/spaces/server/routes/views/enter_space.ts +++ b/x-pack/legacy/plugins/spaces/server/routes/views/enter_space.ts @@ -5,7 +5,7 @@ */ import { Legacy } from 'kibana'; -import { ENTER_SPACE_PATH } from '../../../common/constants'; +import { ENTER_SPACE_PATH } from '../../../../../../plugins/spaces/common/constants'; import { wrapError } from '../../lib/errors'; export function initEnterSpaceView(server: Legacy.Server) { diff --git a/x-pack/legacy/plugins/spaces/server/routes/views/index.ts b/x-pack/legacy/plugins/spaces/server/routes/views/index.ts index d7637e299652f..645e8bec48148 100644 --- a/x-pack/legacy/plugins/spaces/server/routes/views/index.ts +++ b/x-pack/legacy/plugins/spaces/server/routes/views/index.ts @@ -4,5 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { initSpaceSelectorView } from './space_selector'; export { initEnterSpaceView } from './enter_space'; diff --git a/x-pack/legacy/plugins/spaces/server/routes/views/space_selector.ts b/x-pack/legacy/plugins/spaces/server/routes/views/space_selector.ts deleted file mode 100644 index 25c4179b99542..0000000000000 --- a/x-pack/legacy/plugins/spaces/server/routes/views/space_selector.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { Legacy } from 'kibana'; - -export function initSpaceSelectorView(server: Legacy.Server) { - const spaceSelector = server.getHiddenUiAppById('space_selector'); - - server.route({ - method: 'GET', - path: '/spaces/space_selector', - async handler(request, h) { - return (await h.renderAppWithDefaultConfig(spaceSelector)).takeover(); - }, - }); -} diff --git a/x-pack/legacy/plugins/task_manager/server/legacy.ts b/x-pack/legacy/plugins/task_manager/server/legacy.ts index f5e81bfd90169..cd2047b757e61 100644 --- a/x-pack/legacy/plugins/task_manager/server/legacy.ts +++ b/x-pack/legacy/plugins/task_manager/server/legacy.ts @@ -47,6 +47,7 @@ export function createLegacyApi(legacyTaskManager: Promise): Legacy legacyTaskManager.then((tm: TaskManager) => tm.registerTaskDefinitions(taskDefinitions)); }, fetch: (opts: SearchOpts) => legacyTaskManager.then((tm: TaskManager) => tm.fetch(opts)), + get: (id: string) => legacyTaskManager.then((tm: TaskManager) => tm.get(id)), remove: (id: string) => legacyTaskManager.then((tm: TaskManager) => tm.remove(id)), schedule: (taskInstance: TaskInstanceWithDeprecatedFields, options?: any) => legacyTaskManager.then((tm: TaskManager) => tm.schedule(taskInstance, options)), diff --git a/x-pack/legacy/plugins/uptime/public/register_feature.ts b/x-pack/legacy/plugins/uptime/public/register_feature.ts index 885d4f6e1310f..2f83fa33ba4bc 100644 --- a/x-pack/legacy/plugins/uptime/public/register_feature.ts +++ b/x-pack/legacy/plugins/uptime/public/register_feature.ts @@ -5,12 +5,14 @@ */ import { i18n } from '@kbn/i18n'; -import { - FeatureCatalogueCategory, - FeatureCatalogueRegistryProvider, -} from 'ui/registry/feature_catalogue'; +import { npSetup } from 'ui/new_platform'; +import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; -FeatureCatalogueRegistryProvider.register(() => ({ +const { + plugins: { home }, +} = npSetup; + +home.featureCatalogue.register({ id: 'uptime', title: i18n.translate('xpack.uptime.uptimeFeatureCatalogueTitle', { defaultMessage: 'Uptime' }), description: i18n.translate('xpack.uptime.featureCatalogueDescription', { @@ -20,4 +22,4 @@ FeatureCatalogueRegistryProvider.register(() => ({ path: `uptime#/`, showOnHomePage: true, category: FeatureCatalogueCategory.DATA, -})); +}); diff --git a/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/__tests__/get_xpack.js b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/__tests__/get_xpack.js index 3b25084e70e95..eca130b4d7465 100644 --- a/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/__tests__/get_xpack.js +++ b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/__tests__/get_xpack.js @@ -17,6 +17,7 @@ function mockGetXPackLicense(callCluster, license, req) { path: '/_license', query: { local: 'true', + accept_enterprise: 'true', }, }) .returns( @@ -32,6 +33,7 @@ function mockGetXPackLicense(callCluster, license, req) { path: '/_license', query: { local: 'true', + accept_enterprise: 'true', }, }) // conveniently wraps the passed in license object as { license: response }, like it really is diff --git a/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_xpack.js b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_xpack.js index 925d573b490b8..aaeb890981aa1 100644 --- a/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_xpack.js +++ b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_xpack.js @@ -23,6 +23,8 @@ export function getXPackLicense(callCluster) { query: { // Fetching the local license is cheaper than getting it from the master and good enough local: 'true', + // For versions >= 7.6 and < 8.0, this flag is needed otherwise 'platinum' is returned for 'enterprise' license. + accept_enterprise: 'true', }, }).then(({ license }) => license); } diff --git a/x-pack/plugins/features/common/feature.ts b/x-pack/plugins/features/common/feature.ts index 423fe1eb99704..748076b95ad77 100644 --- a/x-pack/plugins/features/common/feature.ts +++ b/x-pack/plugins/features/common/feature.ts @@ -43,7 +43,7 @@ export interface Feature< * This does not restrict access to your feature based on license. * Its only purpose is to inform the space and roles UIs on which features to display. */ - validLicenses?: Array<'basic' | 'standard' | 'gold' | 'platinum' | 'enterprise'>; + validLicenses?: Array<'basic' | 'standard' | 'gold' | 'platinum' | 'enterprise' | 'trial'>; /** * An optional EUI Icon to be used when displaying your feature. diff --git a/x-pack/plugins/features/server/feature_schema.ts b/x-pack/plugins/features/server/feature_schema.ts index 7732686db5ee1..cc12ea1b78dce 100644 --- a/x-pack/plugins/features/server/feature_schema.ts +++ b/x-pack/plugins/features/server/feature_schema.ts @@ -51,7 +51,7 @@ const schema = Joi.object({ name: Joi.string().required(), excludeFromBasePrivileges: Joi.boolean(), validLicenses: Joi.array().items( - Joi.string().valid('basic', 'standard', 'gold', 'platinum', 'enterprise') + Joi.string().valid('basic', 'standard', 'gold', 'platinum', 'enterprise', 'trial') ), icon: Joi.string(), description: Joi.string(), diff --git a/x-pack/plugins/features/server/routes/index.test.ts b/x-pack/plugins/features/server/routes/index.test.ts index 98a23a61d542c..b0f8417b7175d 100644 --- a/x-pack/plugins/features/server/routes/index.test.ts +++ b/x-pack/plugins/features/server/routes/index.test.ts @@ -53,7 +53,7 @@ describe('GET /api/features', () => { it('returns a list of available features', async () => { const mockResponse = httpServerMock.createResponseFactory(); - routeHandler(undefined as any, undefined as any, mockResponse); + routeHandler(undefined as any, { query: {} } as any, mockResponse); expect(mockResponse.ok.mock.calls).toMatchInlineSnapshot(` Array [ @@ -84,11 +84,11 @@ describe('GET /api/features', () => { `); }); - it(`does not return features that arent allowed by current license`, async () => { + it(`by default does not return features that arent allowed by current license`, async () => { currentLicenseLevel = 'basic'; const mockResponse = httpServerMock.createResponseFactory(); - routeHandler(undefined as any, undefined as any, mockResponse); + routeHandler(undefined as any, { query: {} } as any, mockResponse); expect(mockResponse.ok.mock.calls).toMatchInlineSnapshot(` Array [ @@ -107,4 +107,63 @@ 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); + + expect(mockResponse.ok.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "body": Array [ + Object { + "app": Array [], + "id": "feature_1", + "name": "Feature 1", + "privileges": Object {}, + }, + ], + }, + ], + ] + `); + }); + + 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); + + expect(mockResponse.ok.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "body": Array [ + Object { + "app": Array [], + "id": "feature_1", + "name": "Feature 1", + "privileges": Object {}, + }, + Object { + "app": Array [ + "bar-app", + ], + "id": "licensed_feature", + "name": "Licensed Feature", + "privileges": Object {}, + "validLicenses": Array [ + "gold", + ], + }, + ], + }, + ], + ] + `); + }); }); diff --git a/x-pack/plugins/features/server/routes/index.ts b/x-pack/plugins/features/server/routes/index.ts index 51869c39cf83c..cf4d61ccac88b 100644 --- a/x-pack/plugins/features/server/routes/index.ts +++ b/x-pack/plugins/features/server/routes/index.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { schema } from '@kbn/config-schema'; import { IRouter } from '../../../../../src/core/server'; import { LegacyAPI } from '../plugin'; import { FeatureRegistry } from '../feature_registry'; @@ -19,13 +20,20 @@ export interface RouteDefinitionParams { export function defineRoutes({ router, featureRegistry, getLegacyAPI }: RouteDefinitionParams) { router.get( - { path: '/api/features', options: { tags: ['access:features'] }, validate: false }, + { + path: '/api/features', + options: { tags: ['access:features'] }, + validate: { + query: schema.object({ ignoreValidLicenses: schema.boolean({ defaultValue: false }) }), + }, + }, (context, request, response) => { const allFeatures = featureRegistry.getAll(); return response.ok({ body: allFeatures.filter( feature => + request.query.ignoreValidLicenses || !feature.validLicenses || !feature.validLicenses.length || getLegacyAPI().xpackInfo.license.isOneOf(feature.validLicenses) diff --git a/x-pack/plugins/security/common/licensing/index.ts b/x-pack/plugins/security/common/licensing/index.ts index 9ddbe86167367..e8efae3dc6a6b 100644 --- a/x-pack/plugins/security/common/licensing/index.ts +++ b/x-pack/plugins/security/common/licensing/index.ts @@ -5,3 +5,5 @@ */ export { SecurityLicenseService, SecurityLicense } from './license_service'; + +export { SecurityLicenseFeatures } from './license_features'; diff --git a/x-pack/plugins/security/public/account_management/account_management_page.test.tsx b/x-pack/plugins/security/public/account_management/account_management_page.test.tsx index 4caf3d25e887b..46bbedd37c434 100644 --- a/x-pack/plugins/security/public/account_management/account_management_page.test.tsx +++ b/x-pack/plugins/security/public/account_management/account_management_page.test.tsx @@ -8,7 +8,6 @@ import { act } from '@testing-library/react'; import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; import { AuthenticatedUser } from '../../common/model'; import { AccountManagementPage } from './account_management_page'; - import { coreMock } from 'src/core/public/mocks'; import { mockAuthenticatedUser } from '../../common/model/authenticated_user.mock'; import { securityMock } from '../mocks'; diff --git a/x-pack/plugins/security/public/index.ts b/x-pack/plugins/security/public/index.ts index 336ec37d76a1b..712f49afd306e 100644 --- a/x-pack/plugins/security/public/index.ts +++ b/x-pack/plugins/security/public/index.ts @@ -10,6 +10,7 @@ import { SecurityPlugin, SecurityPluginSetup, SecurityPluginStart } from './plug export { SecurityPluginSetup, SecurityPluginStart }; export { SessionInfo } from './types'; export { AuthenticatedUser } from '../common/model'; +export { SecurityLicense, SecurityLicenseFeatures } from '../common/licensing'; export const plugin: PluginInitializer = () => new SecurityPlugin(); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx index e183eae08d1e1..23a3f327a2c5c 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx @@ -9,7 +9,6 @@ import React from 'react'; import { act } from '@testing-library/react'; import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; import { Capabilities } from 'src/core/public'; -import { Space } from '../../../../../spaces/common/model/space'; import { Feature } from '../../../../../features/public'; // These modules should be moved into a common directory // eslint-disable-next-line @kbn/eslint/no-restricted-paths @@ -28,6 +27,7 @@ import { dataPluginMock } from '../../../../../../../src/plugins/data/public/moc import { licenseMock } from '../../../../common/licensing/index.mock'; import { userAPIClientMock } from '../../users/index.mock'; import { rolesAPIClientMock, indicesAPIClientMock, privilegesAPIClientMock } from '../index.mock'; +import { Space } from '../../../../../spaces/public'; const buildFeatures = () => { return [ diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx index 33e69a68ca896..42ec3fa419167 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx @@ -37,7 +37,7 @@ import { NotificationsStart, } from 'src/core/public'; import { IndexPatternsContract } from '../../../../../../../src/plugins/data/public'; -import { Space } from '../../../../../spaces/common/model/space'; +import { Space } from '../../../../../spaces/public'; import { Feature } from '../../../../../features/public'; import { KibanaPrivileges, diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx index 4ebe02e687159..a4e287632c764 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx @@ -6,7 +6,7 @@ import React, { Component } from 'react'; import { Capabilities } from 'src/core/public'; -import { Space } from '../../../../../../../spaces/common/model/space'; +import { Space } from '../../../../../../../spaces/public'; import { Feature } from '../../../../../../../features/public'; import { KibanaPrivileges, Role } from '../../../../../../common/model'; import { KibanaPrivilegeCalculatorFactory } from './kibana_privilege_calculator'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_matrix.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_matrix.test.tsx index 16aad4826ae44..a01c026c1a5df 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_matrix.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_matrix.test.tsx @@ -7,7 +7,7 @@ import { EuiButtonEmpty, EuiInMemoryTable } from '@elastic/eui'; import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { Space } from '../../../../../../../../spaces/common/model/space'; +import { Space } from '../../../../../../../../spaces/public'; import { Feature } from '../../../../../../../../features/public'; import { KibanaPrivileges, Role } from '../../../../../../../common/model'; import { KibanaPrivilegeCalculatorFactory } from '../kibana_privilege_calculator'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_matrix.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_matrix.tsx index b3449e32c6c91..f0f425273e25d 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_matrix.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_matrix.tsx @@ -19,8 +19,7 @@ import { } from '@elastic/eui'; import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react'; import React, { Component, Fragment } from 'react'; -import { SpaceAvatar } from '../../../../../../../../../legacy/plugins/spaces/public/space_avatar'; -import { Space } from '../../../../../../../../spaces/common/model/space'; +import { Space, SpaceAvatar } from '../../../../../../../../spaces/public'; import { Feature } from '../../../../../../../../features/public'; import { FeaturesPrivileges, Role } from '../../../../../../../common/model'; import { CalculatedPrivilege } from '../kibana_privilege_calculator'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx index 6d1f5117c52e9..6f841b5d14cb3 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx @@ -24,7 +24,7 @@ import { } from '@elastic/eui'; import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react'; import React, { Component, Fragment } from 'react'; -import { Space } from '../../../../../../../../spaces/common/model/space'; +import { Space } from '../../../../../../../../spaces/public'; import { Feature } from '../../../../../../../../features/public'; import { KibanaPrivileges, Role, copyRole } from '../../../../../../../common/model'; import { diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx index 1c27ec84f50dc..1a43fb9e2683a 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx @@ -13,8 +13,7 @@ import { } from '@elastic/eui'; import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react'; import React, { Component } from 'react'; -import { getSpaceColor } from '../../../../../../../../../legacy/plugins/spaces/public/space_avatar'; -import { Space } from '../../../../../../../../spaces/common/model/space'; +import { Space, getSpaceColor } from '../../../../../../../../spaces/public'; import { FeaturesPrivileges, Role, diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx index b2b92356e5126..5fc238eed0ae7 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx @@ -15,7 +15,7 @@ import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import _ from 'lodash'; import React, { Component, Fragment } from 'react'; import { Capabilities } from 'src/core/public'; -import { Space } from '../../../../../../../../spaces/common/model/space'; +import { Space } from '../../../../../../../../spaces/public'; import { Feature } from '../../../../../../../../features/public'; import { KibanaPrivileges, Role, isReservedRole } from '../../../../../../../common/model'; import { KibanaPrivilegeCalculatorFactory } from '../kibana_privilege_calculator'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_selector.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_selector.tsx index cfeb5b9f37d8c..3e5ea9f146876 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_selector.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_selector.tsx @@ -7,8 +7,7 @@ import { EuiComboBox, EuiComboBoxOptionProps, EuiHealth, EuiHighlight } from '@elastic/eui'; import { InjectedIntl } from '@kbn/i18n/react'; import React, { Component } from 'react'; -import { getSpaceColor } from '../../../../../../../../../legacy/plugins/spaces/public/space_avatar'; -import { Space } from '../../../../../../../../spaces/common/model/space'; +import { Space, getSpaceColor } from '../../../../../../../../spaces/public'; const spaceToOption = (space?: Space, currentSelection?: 'global' | 'spaces') => { if (!space) { diff --git a/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx index bb7a6db97f7c8..f8b2991a844f7 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx @@ -14,9 +14,8 @@ import { } from '@elastic/eui'; import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react'; import React, { Component } from 'react'; -import { SpaceAvatar } from '../../../../../../../legacy/plugins/spaces/public/space_avatar'; +import { Space, SpaceAvatar } from '../../../../../../spaces/public'; import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../../../../../spaces/common'; -import { Space } from '../../../../../../spaces/common/model/space'; interface Props { spaces: Space[]; diff --git a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx index 543d20bb92afe..7a7542909431f 100644 --- a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx +++ b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx @@ -10,7 +10,6 @@ import { EditUserPage } from './edit_user_page'; import React from 'react'; import { User, Role } from '../../../../common/model'; import { ReactWrapper } from 'enzyme'; - import { coreMock } from '../../../../../../../src/core/public/mocks'; import { mockAuthenticatedUser } from '../../../../common/model/authenticated_user.mock'; import { securityMock } from '../../../mocks'; diff --git a/x-pack/plugins/security/public/mocks.ts b/x-pack/plugins/security/public/mocks.ts index 3c0c59d10abd1..33c1d1446afba 100644 --- a/x-pack/plugins/security/public/mocks.ts +++ b/x-pack/plugins/security/public/mocks.ts @@ -6,11 +6,13 @@ import { authenticationMock } from './authentication/index.mock'; import { createSessionTimeoutMock } from './session/session_timeout.mock'; +import { licenseMock } from '../common/licensing/index.mock'; function createSetupMock() { return { authc: authenticationMock.createSetup(), sessionTimeout: createSessionTimeoutMock(), + license: licenseMock.create(), }; } diff --git a/x-pack/plugins/security/public/nav_control/nav_control_service.tsx b/x-pack/plugins/security/public/nav_control/nav_control_service.tsx index 153e7112dc95b..813304148ec77 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_service.tsx +++ b/x-pack/plugins/security/public/nav_control/nav_control_service.tsx @@ -57,7 +57,7 @@ export class SecurityNavControlService { } private registerSecurityNavControl( - core: Pick + core: Pick ) { const currentUserPromise = this.authc.getCurrentUser(); core.chrome.navControls.registerRight({ @@ -65,10 +65,12 @@ export class SecurityNavControlService { mount: (el: HTMLElement) => { const I18nContext = core.i18n.Context; + const logoutUrl = core.injectedMetadata.getInjectedVar('logoutUrl') as string; + const props = { user: currentUserPromise, editProfileUrl: core.http.basePath.prepend('/app/kibana#/account'), - logoutUrl: core.http.basePath.prepend(`/logout`), + logoutUrl, }; ReactDOM.render( diff --git a/x-pack/plugins/security/public/plugin.tsx b/x-pack/plugins/security/public/plugin.tsx index 394e23cbbf646..f1ac2e2ecc3e2 100644 --- a/x-pack/plugins/security/public/plugin.tsx +++ b/x-pack/plugins/security/public/plugin.tsx @@ -107,10 +107,11 @@ export class SecurityPlugin return { authc: this.authc, sessionTimeout: this.sessionTimeout, + license, }; } - public start(core: CoreStart, { data, management }: PluginStartDependencies) { + public start(core: CoreStart, { management }: PluginStartDependencies) { this.sessionTimeout.start(); this.navControlService.start({ core }); diff --git a/x-pack/legacy/plugins/spaces/common/lib/dataurl.ts b/x-pack/plugins/spaces/common/lib/dataurl.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/common/lib/dataurl.ts rename to x-pack/plugins/spaces/common/lib/dataurl.ts diff --git a/x-pack/plugins/spaces/kibana.json b/x-pack/plugins/spaces/kibana.json index d806aaf1807ef..4242d2f5c5d09 100644 --- a/x-pack/plugins/spaces/kibana.json +++ b/x-pack/plugins/spaces/kibana.json @@ -4,7 +4,7 @@ "kibanaVersion": "kibana", "configPath": ["xpack", "spaces"], "requiredPlugins": ["features", "licensing"], - "optionalPlugins": ["security", "home", "usageCollection"], + "optionalPlugins": ["advancedSettings", "home", "management", "security", "usageCollection"], "server": true, - "ui": false + "ui": true } diff --git a/x-pack/legacy/plugins/spaces/public/advanced_settings/advanced_settings_service.test.tsx b/x-pack/plugins/spaces/public/advanced_settings/advanced_settings_service.test.tsx similarity index 78% rename from x-pack/legacy/plugins/spaces/public/advanced_settings/advanced_settings_service.test.tsx rename to x-pack/plugins/spaces/public/advanced_settings/advanced_settings_service.test.tsx index 3c6b2332bbbdc..08cc0a638cd78 100644 --- a/x-pack/legacy/plugins/spaces/public/advanced_settings/advanced_settings_service.test.tsx +++ b/x-pack/plugins/spaces/public/advanced_settings/advanced_settings_service.test.tsx @@ -5,7 +5,7 @@ */ import { AdvancedSettingsService } from './advanced_settings_service'; -import { advancedSettingsMock } from '../../../../../../src/plugins/advanced_settings/public/mocks'; +import { advancedSettingsMock } from '../../../../../src/plugins/advanced_settings/public/mocks'; const componentRegistryMock = advancedSettingsMock.createSetupContract(); @@ -14,7 +14,7 @@ describe('Advanced Settings Service', () => { it('registers space-aware components to augment the advanced settings screen', () => { const deps = { getActiveSpace: jest.fn().mockResolvedValue({ id: 'foo', name: 'foo-space' }), - componentRegistry: componentRegistryMock, + componentRegistry: componentRegistryMock.component, }; const advancedSettingsService = new AdvancedSettingsService(); @@ -22,13 +22,13 @@ describe('Advanced Settings Service', () => { expect(deps.componentRegistry.register).toHaveBeenCalledTimes(2); expect(deps.componentRegistry.register).toHaveBeenCalledWith( - componentRegistryMock.componentType.PAGE_TITLE_COMPONENT, + componentRegistryMock.component.componentType.PAGE_TITLE_COMPONENT, expect.any(Function), true ); expect(deps.componentRegistry.register).toHaveBeenCalledWith( - componentRegistryMock.componentType.PAGE_SUBTITLE_COMPONENT, + componentRegistryMock.component.componentType.PAGE_SUBTITLE_COMPONENT, expect.any(Function), true ); diff --git a/x-pack/legacy/plugins/spaces/public/advanced_settings/advanced_settings_service.tsx b/x-pack/plugins/spaces/public/advanced_settings/advanced_settings_service.tsx similarity index 91% rename from x-pack/legacy/plugins/spaces/public/advanced_settings/advanced_settings_service.tsx rename to x-pack/plugins/spaces/public/advanced_settings/advanced_settings_service.tsx index a1552add18f2d..5dedb285014f8 100644 --- a/x-pack/legacy/plugins/spaces/public/advanced_settings/advanced_settings_service.tsx +++ b/x-pack/plugins/spaces/public/advanced_settings/advanced_settings_service.tsx @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; +import { AdvancedSettingsSetup } from 'src/plugins/advanced_settings/public'; import { Space } from '../../common/model/space'; import { AdvancedSettingsTitle, AdvancedSettingsSubtitle } from './components'; -import { AdvancedSettingsSetup } from '../../../../../../src/plugins/advanced_settings/public'; interface SetupDeps { getActiveSpace: () => Promise; diff --git a/x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx rename to x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx rename to x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx diff --git a/x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/index.ts b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/index.ts rename to x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.test.tsx b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.test.tsx rename to x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.tsx b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.tsx similarity index 94% rename from x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.tsx rename to x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.tsx index b74524db81d81..413be03d08bfc 100644 --- a/x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.tsx +++ b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.tsx @@ -7,7 +7,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { useState, useEffect } from 'react'; -import { Space } from '../../../../../../../plugins/spaces/common/model/space'; +import { Space } from '../../../../common/model/space'; import { SpaceAvatar } from '../../../space_avatar'; interface Props { diff --git a/x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_title/index.ts b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_title/index.ts rename to x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/advanced_settings/components/index.ts b/x-pack/plugins/spaces/public/advanced_settings/components/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/advanced_settings/components/index.ts rename to x-pack/plugins/spaces/public/advanced_settings/components/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/advanced_settings/index.ts b/x-pack/plugins/spaces/public/advanced_settings/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/advanced_settings/index.ts rename to x-pack/plugins/spaces/public/advanced_settings/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/constants.ts b/x-pack/plugins/spaces/public/constants.ts similarity index 80% rename from x-pack/legacy/plugins/spaces/public/constants.ts rename to x-pack/plugins/spaces/public/constants.ts index 94799f6f2b5d8..15c65722d01ef 100644 --- a/x-pack/legacy/plugins/spaces/public/constants.ts +++ b/x-pack/plugins/spaces/public/constants.ts @@ -5,7 +5,6 @@ */ import { i18n } from '@kbn/i18n'; -import { npSetup } from 'ui/new_platform'; let spacesFeatureDescription: string; @@ -19,6 +18,3 @@ export const getSpacesFeatureDescription = () => { return spacesFeatureDescription; }; - -export const getManageSpacesUrl = () => - npSetup.core.http.basePath.prepend(`/app/kibana#/management/spaces/list`); diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/_index.scss b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/_index.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/_index.scss rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/_index.scss diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/_copy_to_space.scss b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/_copy_to_space.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/_copy_to_space.scss rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/_copy_to_space.scss diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/_index.scss b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/_index.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/_index.scss rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/_index.scss diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_indicator.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_indicator.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_indicator.tsx rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_indicator.tsx diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_summary_indicator.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_summary_indicator.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_summary_indicator.tsx rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_summary_indicator.tsx diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx similarity index 97% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx index 28011911e212e..7809b511adda4 100644 --- a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx @@ -6,7 +6,7 @@ import React from 'react'; import Boom from 'boom'; import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; -import { mockManagementPlugin } from '../../../../../../../src/legacy/core_plugins/management/public/np_ready/mocks'; +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'; import { EuiLoadingSpinner, EuiEmptyPrompt } from '@elastic/eui'; @@ -17,9 +17,9 @@ import { act } from '@testing-library/react'; import { ProcessingCopyToSpace } from './processing_copy_to_space'; import { spacesManagerMock } from '../../spaces_manager/mocks'; import { SpacesManager } from '../../spaces_manager'; -import { ToastNotifications } from 'ui/notify/toasts/toast_notifications'; +import { ToastsApi } from 'src/core/public'; -jest.mock('../../../../../../../src/legacy/core_plugins/management/public/legacy', () => ({ +jest.mock('../../../../../../src/legacy/core_plugins/management/public/legacy', () => ({ setup: mockManagementPlugin.createSetupContract(), start: mockManagementPlugin.createStartContract(), })); @@ -86,7 +86,7 @@ const setup = async (opts: SetupOpts = {}) => { ); diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx similarity index 96% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx index f486f2f24f13d..cd2f3986600ff 100644 --- a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx @@ -21,12 +21,12 @@ import { import { mapValues } from 'lodash'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ToastNotifications } from 'ui/notify/toasts/toast_notifications'; -import { SavedObjectsManagementRecord } from '../../../../../../../src/legacy/core_plugins/management/public'; +import { ToastsStart } from 'src/core/public'; import { + SavedObjectsManagementRecord, ProcessedImportResponse, processImportResponse, -} from '../../../../../../../src/legacy/core_plugins/management/public'; +} from '../../../../../../src/legacy/core_plugins/management/public'; import { Space } from '../../../common/model/space'; import { SpacesManager } from '../../spaces_manager'; import { ProcessingCopyToSpace } from './processing_copy_to_space'; @@ -38,7 +38,7 @@ interface Props { onClose: () => void; savedObject: SavedObjectsManagementRecord; spacesManager: SpacesManager; - toastNotifications: ToastNotifications; + toastNotifications: ToastsStart; } export const CopySavedObjectsToSpaceFlyout = (props: Props) => { diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx similarity index 98% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx index 56f39ce3ed4fb..b22cec0af5ea8 100644 --- a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx @@ -8,7 +8,7 @@ import React, { Fragment } from 'react'; import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiStat, EuiHorizontalRule } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { ProcessedImportResponse } from '../../../../../../../src/legacy/core_plugins/management/public'; +import { ProcessedImportResponse } from '../../../../../../src/legacy/core_plugins/management/public'; import { ImportRetry } from '../types'; interface Props { diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_form.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_form.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_form.tsx rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_form.tsx diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/index.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/index.ts rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx similarity index 94% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx index 285abb828a011..2735218fdd370 100644 --- a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx @@ -13,8 +13,10 @@ import { EuiHorizontalRule, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SavedObjectsManagementRecord } from '../../../../../../../src/legacy/core_plugins/management/public'; -import { ProcessedImportResponse } from '../../../../../../../src/legacy/core_plugins/management/public'; +import { + SavedObjectsManagementRecord, + ProcessedImportResponse, +} from '../../../../../../src/legacy/core_plugins/management/public'; import { Space } from '../../../common/model/space'; import { CopyOptions, ImportRetry } from '../types'; import { SpaceResult } from './space_result'; diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/selectable_spaces_control.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/selectable_spaces_control.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/selectable_spaces_control.tsx rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/selectable_spaces_control.tsx diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/space_result.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result.tsx similarity index 98% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/space_result.tsx rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result.tsx index 22f0767ba196e..b667ec2a6e11d 100644 --- a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/space_result.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { EuiAccordion, EuiFlexGroup, EuiFlexItem, EuiText, EuiSpacer } from '@elastic/eui'; -import { SavedObjectsManagementRecord } from '../../../../../../../src/legacy/core_plugins/management/public'; +import { SavedObjectsManagementRecord } from '../../../../../../src/legacy/core_plugins/management/public'; import { SummarizedCopyToSpaceResult } from '../index'; import { SpaceAvatar } from '../../space_avatar'; import { Space } from '../../../common/model/space'; diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/space_result_details.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result_details.tsx similarity index 98% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/space_result_details.tsx rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result_details.tsx index d3ab406b87c3e..cae72bb1e75e9 100644 --- a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/space_result_details.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result_details.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { EuiText, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { SummarizedCopyToSpaceResult } from '../index'; -import { SavedObjectsManagementRecord } from '../../../../../../../src/legacy/core_plugins/management/public'; +import { SavedObjectsManagementRecord } from '../../../../../../src/legacy/core_plugins/management/public'; import { Space } from '../../../common/model/space'; import { CopyStatusIndicator } from './copy_status_indicator'; import { ImportRetry } from '../types'; diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx similarity index 83% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx index c016494a4cdf9..c9297ed97f09b 100644 --- a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx @@ -5,11 +5,11 @@ */ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { toastNotifications } from 'ui/notify'; +import { NotificationsStart } from 'src/core/public'; import { SavedObjectsManagementAction, SavedObjectsManagementRecord, -} from '../../../../../../src/legacy/core_plugins/management/public'; +} from '../../../../../src/legacy/core_plugins/management/public'; import { CopySavedObjectsToSpaceFlyout } from './components'; import { SpacesManager } from '../spaces_manager'; @@ -30,7 +30,10 @@ export class CopyToSpaceSavedObjectsManagementAction extends SavedObjectsManagem }, }; - constructor(private readonly spacesManager: SpacesManager) { + constructor( + private readonly spacesManager: SpacesManager, + private readonly notifications: NotificationsStart + ) { super(); } @@ -43,7 +46,7 @@ export class CopyToSpaceSavedObjectsManagementAction extends SavedObjectsManagem onClose={this.onClose} savedObject={this.record} spacesManager={this.spacesManager} - toastNotifications={toastNotifications} + toastNotifications={this.notifications.toasts} /> ); }; diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.test.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.test.ts similarity index 90% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.test.ts rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.test.ts index 63a59344dfe5d..2603f80a19520 100644 --- a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.test.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.test.ts @@ -8,12 +8,14 @@ import { ManagementSetup } from 'src/legacy/core_plugins/management/public'; import { CopyToSpaceSavedObjectsManagementAction } from './copy_saved_objects_to_space_action'; import { spacesManagerMock } from '../spaces_manager/mocks'; import { CopySavedObjectsToSpaceService } from '.'; +import { notificationServiceMock } from 'src/core/public/mocks'; describe('CopySavedObjectsToSpaceService', () => { describe('#setup', () => { it('registers the CopyToSpaceSavedObjectsManagementAction', () => { const deps = { spacesManager: spacesManagerMock.create(), + notificationsSetup: notificationServiceMock.createSetupContract(), // we don't have a proper NP mock for this yet managementSetup: ({ savedObjects: { diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.ts similarity index 71% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.ts rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.ts index 37354f985a2fc..2db3d4b319acb 100644 --- a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.ts @@ -5,17 +5,19 @@ */ import { ManagementSetup } from 'src/legacy/core_plugins/management/public'; +import { NotificationsSetup } from 'src/core/public'; import { CopyToSpaceSavedObjectsManagementAction } from './copy_saved_objects_to_space_action'; import { SpacesManager } from '../spaces_manager'; interface SetupDeps { spacesManager: SpacesManager; - managementSetup: ManagementSetup; + managementSetup: Pick; + notificationsSetup: NotificationsSetup; } export class CopySavedObjectsToSpaceService { - public setup({ spacesManager, managementSetup }: SetupDeps) { - const action = new CopyToSpaceSavedObjectsManagementAction(spacesManager); + public setup({ spacesManager, managementSetup, notificationsSetup }: SetupDeps) { + const action = new CopyToSpaceSavedObjectsManagementAction(spacesManager, notificationsSetup); managementSetup.savedObjects.registry.register(action); } } diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/index.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/index.ts rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.test.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.test.ts similarity index 98% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.test.ts rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.test.ts index 0244a35711e6f..fb2616619c644 100644 --- a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.test.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.test.ts @@ -5,7 +5,7 @@ */ import { summarizeCopyResult } from './summarize_copy_result'; -import { ProcessedImportResponse } from '../../../../../../src/legacy/core_plugins/management/public'; +import { ProcessedImportResponse } from 'src/legacy/core_plugins/management/public'; const createSavedObjectsManagementRecord = () => ({ type: 'dashboard', diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.ts similarity index 94% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.ts rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.ts index 7bc47d35efc6c..5ad4fe2cad3ab 100644 --- a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.ts @@ -4,8 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsManagementRecord } from '../../../../../../src/legacy/core_plugins/management/public'; -import { ProcessedImportResponse } from '../../../../../../src/legacy/core_plugins/management/public'; +import { + ProcessedImportResponse, + SavedObjectsManagementRecord, +} from 'src/legacy/core_plugins/management/public'; export interface SummarizedSavedObjectResult { type: string; diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/types.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/types.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/types.ts rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/types.ts diff --git a/x-pack/legacy/plugins/spaces/public/create_feature_catalogue_entry.ts b/x-pack/plugins/spaces/public/create_feature_catalogue_entry.ts similarity index 88% rename from x-pack/legacy/plugins/spaces/public/create_feature_catalogue_entry.ts rename to x-pack/plugins/spaces/public/create_feature_catalogue_entry.ts index 464066d2221de..2cf34e842ce33 100644 --- a/x-pack/legacy/plugins/spaces/public/create_feature_catalogue_entry.ts +++ b/x-pack/plugins/spaces/public/create_feature_catalogue_entry.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { FeatureCatalogueEntry, FeatureCatalogueCategory, -} from '../../../../../src/plugins/home/public'; +} from '../../../../src/plugins/home/public'; import { getSpacesFeatureDescription } from './constants'; export const createSpacesFeatureCatalogueEntry = (): FeatureCatalogueEntry => { @@ -19,7 +19,7 @@ export const createSpacesFeatureCatalogueEntry = (): FeatureCatalogueEntry => { }), description: getSpacesFeatureDescription(), icon: 'spacesApp', - path: '/app/kibana#/management/spaces/list', + path: '/app/kibana#/management/kibana/spaces', showOnHomePage: true, category: FeatureCatalogueCategory.ADMIN, }; diff --git a/x-pack/plugins/spaces/public/index.scss b/x-pack/plugins/spaces/public/index.scss new file mode 100644 index 0000000000000..26269f1d31aa3 --- /dev/null +++ b/x-pack/plugins/spaces/public/index.scss @@ -0,0 +1,16 @@ +// Import the EUI global scope so we can use EUI constants +@import 'src/legacy/ui/public/styles/_styling_constants'; + +/* Spaces plugin styles */ + +// Prefix all styles with "spc" to avoid conflicts. +// Examples +// spcChart +// spcChart__legend +// spcChart__legend--small +// spcChart__legend-isLoading + +@import './management/index'; +@import './nav_control/index'; +@import './space_selector/index'; +@import './copy_saved_objects_to_space/index'; diff --git a/x-pack/legacy/plugins/spaces/public/index.ts b/x-pack/plugins/spaces/public/index.ts similarity index 62% rename from x-pack/legacy/plugins/spaces/public/index.ts rename to x-pack/plugins/spaces/public/index.ts index 53cb906a619d3..9abf05cab2786 100644 --- a/x-pack/legacy/plugins/spaces/public/index.ts +++ b/x-pack/plugins/spaces/public/index.ts @@ -5,7 +5,11 @@ */ import { SpacesPlugin } from './plugin'; -export { SpaceAvatar } from './space_avatar'; +export { Space } from '../common/model/space'; + +export { SpaceAvatar, getSpaceColor, getSpaceImageUrl, getSpaceInitials } from './space_avatar'; + +export { SpacesPluginSetup, SpacesPluginStart } from './plugin'; export const plugin = () => { return new SpacesPlugin(); diff --git a/x-pack/legacy/plugins/spaces/public/management/_index.scss b/x-pack/plugins/spaces/public/management/_index.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/_index.scss rename to x-pack/plugins/spaces/public/management/_index.scss diff --git a/x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/__snapshots__/confirm_delete_modal.test.tsx.snap b/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/__snapshots__/confirm_delete_modal.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/__snapshots__/confirm_delete_modal.test.tsx.snap rename to x-pack/plugins/spaces/public/management/components/confirm_delete_modal/__snapshots__/confirm_delete_modal.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/_confirm_delete_modal.scss b/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/_confirm_delete_modal.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/_confirm_delete_modal.scss rename to x-pack/plugins/spaces/public/management/components/confirm_delete_modal/_confirm_delete_modal.scss diff --git a/x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.test.tsx b/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.test.tsx rename to x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx b/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx rename to x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx diff --git a/x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/index.ts b/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/index.ts rename to x-pack/plugins/spaces/public/management/components/confirm_delete_modal/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/management/components/index.ts b/x-pack/plugins/spaces/public/management/components/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/components/index.ts rename to x-pack/plugins/spaces/public/management/components/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/index.ts b/x-pack/plugins/spaces/public/management/components/secure_space_message/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/index.ts rename to x-pack/plugins/spaces/public/management/components/secure_space_message/index.ts diff --git a/x-pack/plugins/spaces/public/management/components/secure_space_message/secure_space_message.tsx b/x-pack/plugins/spaces/public/management/components/secure_space_message/secure_space_message.tsx new file mode 100644 index 0000000000000..38d8451b658a8 --- /dev/null +++ b/x-pack/plugins/spaces/public/management/components/secure_space_message/secure_space_message.tsx @@ -0,0 +1,40 @@ +/* + * 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 { EuiHorizontalRule, EuiLink, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import React, { Fragment } from 'react'; + +export const SecureSpaceMessage = () => { + const rolesLinkTextAriaLabel = i18n.translate( + 'xpack.spaces.management.secureSpaceMessage.rolesLinkTextAriaLabel', + { defaultMessage: 'Roles management page' } + ); + return ( + + + +

+ + + + ), + }} + /> +

+
+
+ ); +}; diff --git a/x-pack/legacy/plugins/spaces/public/management/components/unauthorized_prompt/__snapshots__/unauthorized_prompt.test.tsx.snap b/x-pack/plugins/spaces/public/management/components/unauthorized_prompt/__snapshots__/unauthorized_prompt.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/components/unauthorized_prompt/__snapshots__/unauthorized_prompt.test.tsx.snap rename to x-pack/plugins/spaces/public/management/components/unauthorized_prompt/__snapshots__/unauthorized_prompt.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/management/components/unauthorized_prompt/index.ts b/x-pack/plugins/spaces/public/management/components/unauthorized_prompt/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/components/unauthorized_prompt/index.ts rename to x-pack/plugins/spaces/public/management/components/unauthorized_prompt/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.test.tsx b/x-pack/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.test.tsx rename to x-pack/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.tsx b/x-pack/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.tsx rename to x-pack/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.tsx diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/__snapshots__/delete_spaces_button.test.tsx.snap b/x-pack/plugins/spaces/public/management/edit_space/__snapshots__/delete_spaces_button.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/__snapshots__/delete_spaces_button.test.tsx.snap rename to x-pack/plugins/spaces/public/management/edit_space/__snapshots__/delete_spaces_button.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/__snapshots__/confirm_alter_active_space_modal.test.tsx.snap b/x-pack/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/__snapshots__/confirm_alter_active_space_modal.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/__snapshots__/confirm_alter_active_space_modal.test.tsx.snap rename to x-pack/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/__snapshots__/confirm_alter_active_space_modal.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.test.tsx rename to x-pack/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.tsx b/x-pack/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.tsx rename to x-pack/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.tsx diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/index.ts b/x-pack/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/index.ts rename to x-pack/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/__snapshots__/customize_space_avatar.test.tsx.snap b/x-pack/plugins/spaces/public/management/edit_space/customize_space/__snapshots__/customize_space_avatar.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/__snapshots__/customize_space_avatar.test.tsx.snap rename to x-pack/plugins/spaces/public/management/edit_space/customize_space/__snapshots__/customize_space_avatar.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/__snapshots__/space_identifier.test.tsx.snap b/x-pack/plugins/spaces/public/management/edit_space/customize_space/__snapshots__/space_identifier.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/__snapshots__/space_identifier.test.tsx.snap rename to x-pack/plugins/spaces/public/management/edit_space/customize_space/__snapshots__/space_identifier.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx b/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx similarity index 77% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx rename to x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx index b0d74afaa90aa..d581ac22650e3 100644 --- a/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx @@ -16,7 +16,8 @@ import { EuiTextArea, EuiTitle, } from '@elastic/eui'; -import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; import React, { ChangeEvent, Component, Fragment } from 'react'; import { isReservedSpace } from '../../../../common'; import { Space } from '../../../../common/model/space'; @@ -30,7 +31,6 @@ interface Props { validator: SpaceValidator; space: Partial; editingExistingSpace: boolean; - intl: InjectedIntl; onChange: (space: Partial) => void; } @@ -46,24 +46,21 @@ export class CustomizeSpace extends Component { }; public render() { - const { validator, editingExistingSpace, intl } = this.props; + const { validator, editingExistingSpace } = this.props; const { name = '', description = '' } = this.props.space; - const panelTitle = intl.formatMessage({ - id: 'xpack.spaces.management.manageSpacePage.customizeSpaceTitle', - defaultMessage: 'Customize your space', - }); + const panelTitle = i18n.translate( + 'xpack.spaces.management.manageSpacePage.customizeSpaceTitle', + { + defaultMessage: 'Customize your space', + } + ); const extraPopoverProps: Partial = { initialFocus: 'input[name="spaceInitials"]', }; return ( - + @@ -81,8 +78,7 @@ export class CustomizeSpace extends Component { { > { @@ -147,14 +149,18 @@ export class CustomizeSpace extends Component { )} diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.test.tsx similarity index 91% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.test.tsx rename to x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.test.tsx index 642f2f0013222..dc20a375ada1e 100644 --- a/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.test.tsx @@ -15,9 +15,7 @@ const space = { }; test('renders without crashing', () => { - const wrapper = shallowWithIntl( - - ); + const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); }); diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.tsx b/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.tsx similarity index 83% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.tsx rename to x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.tsx index c3207c82bf95e..55fea3671645b 100644 --- a/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.tsx @@ -16,16 +16,14 @@ import { EuiSpacer, isValidHex, } from '@elastic/eui'; -import { InjectedIntl, injectI18n } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { Space } from '../../../../common/model/space'; import { imageTypes, encode } from '../../../../common/lib/dataurl'; import { getSpaceColor, getSpaceInitials } from '../../../space_avatar'; -import { Space } from '../../../../../../../plugins/spaces/common/model/space'; -import { MAX_SPACE_INITIALS } from '../../../../../../../plugins/spaces/common'; - +import { MAX_SPACE_INITIALS } from '../../../../common'; interface Props { space: Partial; onChange: (space: Partial) => void; - intl: InjectedIntl; } interface State { @@ -33,7 +31,7 @@ interface State { pendingInitials?: string | null; } -class CustomizeSpaceAvatarUI extends Component { +export class CustomizeSpaceAvatar extends Component { private initialsRef: HTMLInputElement | null = null; constructor(props: Props) { @@ -101,7 +99,7 @@ class CustomizeSpaceAvatarUI extends Component { }; public render() { - const { space, intl } = this.props; + const { space } = this.props; const { initialsHasFocus, pendingInitials } = this.state; @@ -111,10 +109,12 @@ class CustomizeSpaceAvatarUI extends Component { return (
false}> { { } public filePickerOrImage() { - const { intl } = this.props; - if (!this.props.space.imageUrl) { return ( @@ -179,8 +177,7 @@ class CustomizeSpaceAvatarUI extends Component { return ( this.removeImageUrl()} color="danger" iconType="trash"> - {intl.formatMessage({ - id: 'xpack.spaces.management.customizeSpaceAvatar.removeImage', + {i18n.translate('xpack.spaces.management.customizeSpaceAvatar.removeImage', { defaultMessage: 'Remove custom image', })} @@ -237,5 +234,3 @@ class CustomizeSpaceAvatarUI extends Component { }); }; } - -export const CustomizeSpaceAvatar = injectI18n(CustomizeSpaceAvatarUI); diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/index.ts b/x-pack/plugins/spaces/public/management/edit_space/customize_space/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/index.ts rename to x-pack/plugins/spaces/public/management/edit_space/customize_space/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/space_identifier.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/customize_space/space_identifier.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/space_identifier.test.tsx rename to x-pack/plugins/spaces/public/management/edit_space/customize_space/space_identifier.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/space_identifier.tsx b/x-pack/plugins/spaces/public/management/edit_space/customize_space/space_identifier.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/space_identifier.tsx rename to x-pack/plugins/spaces/public/management/edit_space/customize_space/space_identifier.tsx diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/delete_spaces_button.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.test.tsx similarity index 82% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/delete_spaces_button.test.tsx rename to x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.test.tsx index 364145d6495b8..fbb2a3d07a293 100644 --- a/x-pack/legacy/plugins/spaces/public/management/edit_space/delete_spaces_button.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.test.tsx @@ -9,6 +9,7 @@ import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import { DeleteSpacesButton } from './delete_spaces_button'; import { spacesManagerMock } from '../../spaces_manager/mocks'; import { SpacesManager } from '../../spaces_manager'; +import { notificationServiceMock } from 'src/core/public/mocks'; const space = { id: 'my-space', @@ -20,12 +21,14 @@ describe('DeleteSpacesButton', () => { it('renders as expected', () => { const spacesManager = spacesManagerMock.create(); + const notifications = notificationServiceMock.createStartContract(); + const wrapper = shallowWithIntl( - ); expect(wrapper).toMatchSnapshot(); diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx b/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx similarity index 69% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx rename to x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx index 56a858eb4ccf6..28e45bc8cfd2a 100644 --- a/x-pack/legacy/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx @@ -5,9 +5,10 @@ */ import { EuiButton, EuiButtonIcon, EuiButtonIconProps } from '@elastic/eui'; -import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component, Fragment } from 'react'; -import { toastNotifications } from 'ui/notify'; +import { NotificationsStart } from 'src/core/public'; import { Space } from '../../../common/model/space'; import { SpacesManager } from '../../spaces_manager'; import { ConfirmDeleteModal } from '../components/confirm_delete_modal'; @@ -17,7 +18,7 @@ interface Props { space: Space; spacesManager: SpacesManager; onDelete: () => void; - intl: InjectedIntl; + notifications: NotificationsStart; } interface State { @@ -25,7 +26,7 @@ interface State { showConfirmRedirectModal: boolean; } -class DeleteSpacesButtonUI extends Component { +export class DeleteSpacesButton extends Component { public state = { showConfirmDeleteModal: false, showConfirmRedirectModal: false, @@ -38,7 +39,6 @@ class DeleteSpacesButtonUI extends Component { defaultMessage="Delete space" /> ); - const { intl } = this.props; let ButtonComponent: any = EuiButton; @@ -54,10 +54,12 @@ class DeleteSpacesButtonUI extends Component { {buttonText} @@ -95,23 +97,18 @@ class DeleteSpacesButtonUI extends Component { }; public deleteSpaces = async () => { - const { spacesManager, space, intl } = this.props; + const { spacesManager, space } = this.props; try { await spacesManager.deleteSpace(space); } catch (error) { const { message: errorMessage = '' } = error.data || {}; - toastNotifications.addDanger( - intl.formatMessage( - { - id: 'xpack.spaces.management.deleteSpacesButton.deleteSpaceErrorTitle', - defaultMessage: 'Error deleting space: {errorMessage}', - }, - { - errorMessage, - } - ) + this.props.notifications.toasts.addDanger( + i18n.translate('xpack.spaces.management.deleteSpacesButton.deleteSpaceErrorTitle', { + defaultMessage: 'Error deleting space: {errorMessage}', + values: { errorMessage }, + }) ); } @@ -119,23 +116,18 @@ class DeleteSpacesButtonUI extends Component { showConfirmDeleteModal: false, }); - const message = intl.formatMessage( + const message = i18n.translate( + 'xpack.spaces.management.deleteSpacesButton.spaceSuccessfullyDeletedNotificationMessage', { - id: - 'xpack.spaces.management.deleteSpacesButton.spaceSuccessfullyDeletedNotificationMessage', defaultMessage: 'Deleted {spaceName} space.', - }, - { - spaceName: space.name, + values: { spaceName: space.name }, } ); - toastNotifications.addSuccess(message); + this.props.notifications.toasts.addSuccess(message); if (this.props.onDelete) { this.props.onDelete(); } }; } - -export const DeleteSpacesButton = injectI18n(DeleteSpacesButtonUI); diff --git a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap new file mode 100644 index 0000000000000..7db3d5456fbd3 --- /dev/null +++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap @@ -0,0 +1,120 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EnabledFeatures renders as expected 1`] = ` + + + + + + + + + + } +> + + + +

+ +

+
+ + +

+ +

+

+ + + , + } + } + /> +

+
+
+ + + +
+
+`; diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/_index.scss b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/_index.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/_index.scss rename to x-pack/plugins/spaces/public/management/edit_space/enabled_features/_index.scss diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx similarity index 88% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx rename to x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx index f770857d9313d..d9282ad0457dd 100644 --- a/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx @@ -7,10 +7,10 @@ import { EuiLink } from '@elastic/eui'; import React from 'react'; import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { Feature } from '../../../../../../../plugins/features/public'; import { Space } from '../../../../common/model/space'; import { SectionPanel } from '../section_panel'; import { EnabledFeatures } from './enabled_features'; +import { Feature } from '../../../../../features/public'; const features: Feature[] = [ { @@ -35,15 +35,6 @@ const space: Space = { disabledFeatures: ['feature-1', 'feature-2'], }; -const capabilities = { - navLinks: {}, - management: {}, - catalogue: {}, - spaces: { - manage: true, - }, -}; - describe('EnabledFeatures', () => { it(`renders as expected`, () => { expect( @@ -51,8 +42,7 @@ describe('EnabledFeatures', () => { ) @@ -66,8 +56,7 @@ describe('EnabledFeatures', () => { ); @@ -101,8 +90,7 @@ describe('EnabledFeatures', () => { ); diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx similarity index 89% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx rename to x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx index 70312296f757b..52a0fe8d4d26c 100644 --- a/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx @@ -5,10 +5,10 @@ */ import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; -import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component, Fragment, ReactNode } from 'react'; -import { Capabilities } from 'src/core/public'; -import { Feature } from '../../../../../../../plugins/features/public'; +import { Feature } from '../../../../../../plugins/features/public'; import { Space } from '../../../../common/model/space'; import { getEnabledFeatures } from '../../lib/feature_utils'; import { SectionPanel } from '../section_panel'; @@ -17,17 +17,18 @@ import { FeatureTable } from './feature_table'; interface Props { space: Partial; features: Feature[]; - capabilities: Capabilities; - intl: InjectedIntl; + securityEnabled: boolean; onChange: (space: Partial) => void; } export class EnabledFeatures extends Component { public render() { - const description = this.props.intl.formatMessage({ - id: 'xpack.spaces.management.manageSpacePage.customizeVisibleFeatures', - defaultMessage: 'Customize visible features', - }); + const description = i18n.translate( + 'xpack.spaces.management.manageSpacePage.customizeVisibleFeatures', + { + defaultMessage: 'Customize visible features', + } + ); return ( { initiallyCollapsed title={this.getPanelTitle()} description={description} - intl={this.props.intl} data-test-subj="enabled-features-panel" > @@ -56,7 +56,6 @@ export class EnabledFeatures extends Component { features={this.props.features} space={this.props.space} onChange={this.props.onChange} - intl={this.props.intl} />
@@ -130,7 +129,7 @@ export class EnabledFeatures extends Component { defaultMessage="The feature is hidden in the UI, but is not disabled." />

- {this.props.capabilities.spaces.manage && ( + {this.props.securityEnabled && (

; features: Feature[]; - intl: InjectedIntl; onChange: (space: Partial) => void; } @@ -66,8 +66,7 @@ export class FeatureTable extends Component { private getColumns = () => [ { field: 'feature', - name: this.props.intl.formatMessage({ - id: 'xpack.spaces.management.enabledSpaceFeaturesFeatureColumnTitle', + name: i18n.translate('xpack.spaces.management.enabledSpaceFeaturesFeatureColumnTitle', { defaultMessage: 'Feature', }), render: (feature: Feature, _item: { feature: Feature; space: Props['space'] }) => { diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/index.ts b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/index.ts rename to x-pack/plugins/spaces/public/management/edit_space/enabled_features/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/toggle_all_features.tsx b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/toggle_all_features.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/toggle_all_features.tsx rename to x-pack/plugins/spaces/public/management/edit_space/enabled_features/toggle_all_features.tsx diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/index.ts b/x-pack/plugins/spaces/public/management/edit_space/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/index.ts rename to x-pack/plugins/spaces/public/management/edit_space/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx similarity index 84% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx rename to x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx index d24e932bce112..2aba1522a7e3f 100644 --- a/x-pack/legacy/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx @@ -3,10 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * 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', icon: 'spacesApp' }]), -})); -import '../../__mocks__/xpack_info'; + import { EuiButton, EuiLink, EuiSwitch } from '@elastic/eui'; import { ReactWrapper } from 'enzyme'; import React from 'react'; @@ -16,6 +13,7 @@ import { ManageSpacePage } from './manage_space_page'; import { SectionPanel } from './section_panel'; import { spacesManagerMock } from '../../spaces_manager/mocks'; import { SpacesManager } from '../../spaces_manager'; +import { httpServiceMock, notificationServiceMock } from 'src/core/public/mocks'; const space = { id: 'my-space', @@ -29,10 +27,15 @@ describe('ManageSpacePage', () => { spacesManager.createSpace = jest.fn(spacesManager.createSpace); spacesManager.getActiveSpace = jest.fn().mockResolvedValue(space); + const httpStart = httpServiceMock.createStartContract(); + httpStart.get.mockResolvedValue([{ id: 'feature-1', name: 'feature 1', icon: 'spacesApp' }]); + const wrapper = mountWithIntl( - { }); spacesManager.getActiveSpace = jest.fn().mockResolvedValue(space); + const httpStart = httpServiceMock.createStartContract(); + httpStart.get.mockResolvedValue([{ id: 'feature-1', name: 'feature 1', icon: 'spacesApp' }]); + const onLoadSpace = jest.fn(); const wrapper = mountWithIntl( - { }); spacesManager.getActiveSpace = jest.fn().mockResolvedValue(space); + const httpStart = httpServiceMock.createStartContract(); + httpStart.get.mockResolvedValue([{ id: 'feature-1', name: 'feature 1', icon: 'spacesApp' }]); + const wrapper = mountWithIntl( - { }); spacesManager.getActiveSpace = jest.fn().mockResolvedValue(space); + const httpStart = httpServiceMock.createStartContract(); + httpStart.get.mockResolvedValue([{ id: 'feature-1', name: 'feature 1', icon: 'spacesApp' }]); + const wrapper = mountWithIntl( - void; capabilities: Capabilities; + securityEnabled: boolean; } interface State { @@ -54,7 +55,7 @@ interface State { }; } -class ManageSpacePageUI extends Component { +export class ManageSpacePage extends Component { private readonly validator: SpaceValidator; constructor(props: Props) { @@ -74,47 +75,24 @@ class ManageSpacePageUI extends Component { return; } - const { spaceId, spacesManager, intl, onLoadSpace } = this.props; + const { spaceId, http } = this.props; - const getFeatures = kfetch({ method: 'get', pathname: '/api/features' }); + const getFeatures = http.get('/api/features'); if (spaceId) { - try { - const [space, features] = await Promise.all([spacesManager.getSpace(spaceId), getFeatures]); - if (space) { - if (onLoadSpace) { - onLoadSpace(space); - } - - this.setState({ - space, - features: await features, - originalSpace: space, - isLoading: false, - }); - } - } catch (error) { - const { message = '' } = error.data || {}; - - toastNotifications.addDanger( - intl.formatMessage( - { - id: 'xpack.spaces.management.manageSpacePage.errorLoadingSpaceTitle', - defaultMessage: 'Error loading space: {message}', - }, - { - message, - } - ) - ); - this.backToSpacesList(); - } + await this.loadSpace(spaceId, getFeatures); } else { const features = await getFeatures; this.setState({ isLoading: false, features }); } } + public async componentDidUpdate(previousProps: Props) { + if (this.props.spaceId !== previousProps.spaceId && this.props.spaceId) { + await this.loadSpace(this.props.spaceId, Promise.resolve(this.state.features)); + } + } + public render() { const content = this.state.isLoading ? this.getLoadingIndicator() : this.getForm(); @@ -162,7 +140,6 @@ class ManageSpacePageUI extends Component { onChange={this.onSpaceChange} editingExistingSpace={this.editingExistingSpace()} validator={this.validator} - intl={this.props.intl} /> @@ -170,9 +147,8 @@ class ManageSpacePageUI extends Component { @@ -212,27 +188,33 @@ class ManageSpacePageUI extends Component { }; public maybeGetSecureSpacesMessage = () => { - if (this.editingExistingSpace()) { + if (this.editingExistingSpace() && this.props.securityEnabled) { return ; } return null; }; public getFormButtons = () => { - const createSpaceText = this.props.intl.formatMessage({ - id: 'xpack.spaces.management.manageSpacePage.createSpaceButton', - defaultMessage: 'Create space', - }); + const createSpaceText = i18n.translate( + 'xpack.spaces.management.manageSpacePage.createSpaceButton', + { + defaultMessage: 'Create space', + } + ); - const updateSpaceText = this.props.intl.formatMessage({ - id: 'xpack.spaces.management.manageSpacePage.updateSpaceButton', - defaultMessage: 'Update space', - }); + const updateSpaceText = i18n.translate( + 'xpack.spaces.management.manageSpacePage.updateSpaceButton', + { + defaultMessage: 'Update space', + } + ); - const cancelButtonText = this.props.intl.formatMessage({ - id: 'xpack.spaces.management.manageSpacePage.cancelSpaceButton', - defaultMessage: 'Cancel', - }); + const cancelButtonText = i18n.translate( + 'xpack.spaces.management.manageSpacePage.cancelSpaceButton', + { + defaultMessage: 'Cancel', + } + ); const saveText = this.editingExistingSpace() ? updateSpaceText : createSpaceText; return ( @@ -267,6 +249,7 @@ class ManageSpacePageUI extends Component { space={this.state.space as Space} spacesManager={this.props.spacesManager} onDelete={this.backToSpacesList} + notifications={this.props.notifications} /> ); @@ -320,8 +303,40 @@ class ManageSpacePageUI extends Component { } }; + private loadSpace = async (spaceId: string, featuresPromise: Promise) => { + const { spacesManager, onLoadSpace } = this.props; + + try { + const [space, features] = await Promise.all([ + spacesManager.getSpace(spaceId), + featuresPromise, + ]); + if (space) { + if (onLoadSpace) { + onLoadSpace(space); + } + + this.setState({ + space, + features: await features, + originalSpace: space, + isLoading: false, + }); + } + } catch (error) { + const message = error?.body?.message ?? ''; + + this.props.notifications.toasts.addDanger( + i18n.translate('xpack.spaces.management.manageSpacePage.errorLoadingSpaceTitle', { + defaultMessage: 'Error loading space: {message}', + values: { message }, + }) + ); + this.backToSpacesList(); + } + }; + private performSave = (requireRefresh = false) => { - const { intl } = this.props; if (!this.state.space) { return; } @@ -357,19 +372,16 @@ class ManageSpacePageUI extends Component { action .then(() => { - toastNotifications.addSuccess( - intl.formatMessage( + this.props.notifications.toasts.addSuccess( + i18n.translate( + 'xpack.spaces.management.manageSpacePage.spaceSuccessfullySavedNotificationMessage', { - id: - 'xpack.spaces.management.manageSpacePage.spaceSuccessfullySavedNotificationMessage', defaultMessage: `Space {name} was saved.`, - }, - { - name: `'${name}'`, + values: { name: `'${name}'` }, } ) ); - window.location.hash = `#/management/spaces/list`; + window.location.hash = `#/management/kibana/spaces`; if (requireRefresh) { setTimeout(() => { window.location.reload(); @@ -377,29 +389,22 @@ class ManageSpacePageUI extends Component { } }) .catch(error => { - const { message = '' } = error.data || {}; + const message = error?.body?.message ?? ''; this.setState({ saveInProgress: false }); - toastNotifications.addDanger( - intl.formatMessage( - { - id: 'xpack.spaces.management.manageSpacePage.errorSavingSpaceTitle', - defaultMessage: 'Error saving space: {message}', - }, - { - message, - } - ) + this.props.notifications.toasts.addDanger( + i18n.translate('xpack.spaces.management.manageSpacePage.errorSavingSpaceTitle', { + defaultMessage: 'Error saving space: {message}', + values: { message }, + }) ); }); }; private backToSpacesList = () => { - window.location.hash = `#/management/spaces/list`; + window.location.hash = `#/management/kibana/spaces`; }; private editingExistingSpace = () => !!this.props.spaceId; } - -export const ManageSpacePage = injectI18n(ManageSpacePageUI); diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/reserved_space_badge.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/reserved_space_badge.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/reserved_space_badge.test.tsx rename to x-pack/plugins/spaces/public/management/edit_space/reserved_space_badge.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/reserved_space_badge.tsx b/x-pack/plugins/spaces/public/management/edit_space/reserved_space_badge.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/reserved_space_badge.tsx rename to x-pack/plugins/spaces/public/management/edit_space/reserved_space_badge.tsx diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/__snapshots__/section_panel.test.tsx.snap b/x-pack/plugins/spaces/public/management/edit_space/section_panel/__snapshots__/section_panel.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/__snapshots__/section_panel.test.tsx.snap rename to x-pack/plugins/spaces/public/management/edit_space/section_panel/__snapshots__/section_panel.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/_section_panel.scss b/x-pack/plugins/spaces/public/management/edit_space/section_panel/_section_panel.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/_section_panel.scss rename to x-pack/plugins/spaces/public/management/edit_space/section_panel/_section_panel.scss diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/index.ts b/x-pack/plugins/spaces/public/management/edit_space/section_panel/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/index.ts rename to x-pack/plugins/spaces/public/management/edit_space/section_panel/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx similarity index 73% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx rename to x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx index 9b736c98d1f0c..0b8085ff1ad16 100644 --- a/x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx @@ -11,13 +11,7 @@ import { SectionPanel } from './section_panel'; test('it renders without blowing up', () => { const wrapper = shallowWithIntl( - +

child

); @@ -27,13 +21,7 @@ test('it renders without blowing up', () => { test('it renders children by default', () => { const wrapper = mountWithIntl( - +

child 1

child 2

@@ -45,13 +33,7 @@ test('it renders children by default', () => { test('it hides children when the "hide" link is clicked', () => { const wrapper = mountWithIntl( - +

child 1

child 2

diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx similarity index 77% rename from x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx rename to x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx index a205130d2d765..a0a6bc5a89d33 100644 --- a/x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx @@ -14,7 +14,7 @@ import { EuiTitle, IconType, } from '@elastic/eui'; -import { InjectedIntl } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; import React, { Component, Fragment, ReactNode } from 'react'; interface Props { @@ -22,7 +22,6 @@ interface Props { title: string | ReactNode; description: string; collapsible: boolean; - intl: InjectedIntl; initiallyCollapsed?: boolean; } @@ -52,38 +51,31 @@ export class SectionPanel extends Component { } public getTitle = () => { - const showLinkText = this.props.intl.formatMessage({ - id: 'xpack.spaces.management.collapsiblePanel.showLinkText', + const showLinkText = i18n.translate('xpack.spaces.management.collapsiblePanel.showLinkText', { defaultMessage: 'show', }); - const hideLinkText = this.props.intl.formatMessage({ - id: 'xpack.spaces.management.collapsiblePanel.hideLinkText', + const hideLinkText = i18n.translate('xpack.spaces.management.collapsiblePanel.hideLinkText', { defaultMessage: 'hide', }); - const showLinkDescription = this.props.intl.formatMessage( + const showLinkDescription = i18n.translate( + 'xpack.spaces.management.collapsiblePanel.showLinkDescription', { - id: 'xpack.spaces.management.collapsiblePanel.showLinkDescription', defaultMessage: 'show {title}', - }, - { - title: this.props.description, + values: { title: this.props.description }, } ); - const hideLinkDescription = this.props.intl.formatMessage( + const hideLinkDescription = i18n.translate( + 'xpack.spaces.management.collapsiblePanel.hideLinkDescription', { - id: 'xpack.spaces.management.collapsiblePanel.hideLinkDescription', defaultMessage: 'hide {title}', - }, - { - title: this.props.description, + values: { title: this.props.description }, } ); return ( - // @ts-ignore diff --git a/x-pack/legacy/plugins/spaces/public/management/index.ts b/x-pack/plugins/spaces/public/management/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/index.ts rename to x-pack/plugins/spaces/public/management/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/management/lib/feature_utils.test.ts b/x-pack/plugins/spaces/public/management/lib/feature_utils.test.ts similarity index 95% rename from x-pack/legacy/plugins/spaces/public/management/lib/feature_utils.test.ts rename to x-pack/plugins/spaces/public/management/lib/feature_utils.test.ts index ce874956d0ef2..a3360969fb3f2 100644 --- a/x-pack/legacy/plugins/spaces/public/management/lib/feature_utils.test.ts +++ b/x-pack/plugins/spaces/public/management/lib/feature_utils.test.ts @@ -5,7 +5,7 @@ */ import { getEnabledFeatures } from './feature_utils'; -import { Feature } from '../../../../../../plugins/features/public'; +import { Feature } from '../../../../features/public'; const buildFeatures = () => [ diff --git a/x-pack/legacy/plugins/spaces/public/management/lib/feature_utils.ts b/x-pack/plugins/spaces/public/management/lib/feature_utils.ts similarity index 74% rename from x-pack/legacy/plugins/spaces/public/management/lib/feature_utils.ts rename to x-pack/plugins/spaces/public/management/lib/feature_utils.ts index ff1688637ef73..a1b64eb954403 100644 --- a/x-pack/legacy/plugins/spaces/public/management/lib/feature_utils.ts +++ b/x-pack/plugins/spaces/public/management/lib/feature_utils.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature } from '../../../../../../plugins/features/common'; +import { Feature } from '../../../../features/common'; -import { Space } from '../../../../../../plugins/spaces/common/model/space'; +import { Space } from '../..'; export function getEnabledFeatures(features: Feature[], space: Partial) { return features.filter(feature => !(space.disabledFeatures || []).includes(feature.id)); diff --git a/x-pack/legacy/plugins/spaces/public/management/lib/index.ts b/x-pack/plugins/spaces/public/management/lib/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/lib/index.ts rename to x-pack/plugins/spaces/public/management/lib/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/management/lib/space_identifier_utils.test.ts b/x-pack/plugins/spaces/public/management/lib/space_identifier_utils.test.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/lib/space_identifier_utils.test.ts rename to x-pack/plugins/spaces/public/management/lib/space_identifier_utils.test.ts diff --git a/x-pack/legacy/plugins/spaces/public/management/lib/space_identifier_utils.ts b/x-pack/plugins/spaces/public/management/lib/space_identifier_utils.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/lib/space_identifier_utils.ts rename to x-pack/plugins/spaces/public/management/lib/space_identifier_utils.ts diff --git a/x-pack/legacy/plugins/spaces/public/management/lib/validate_space.test.ts b/x-pack/plugins/spaces/public/management/lib/validate_space.test.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/lib/validate_space.test.ts rename to x-pack/plugins/spaces/public/management/lib/validate_space.test.ts diff --git a/x-pack/legacy/plugins/spaces/public/management/lib/validate_space.ts b/x-pack/plugins/spaces/public/management/lib/validate_space.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/management/lib/validate_space.ts rename to x-pack/plugins/spaces/public/management/lib/validate_space.ts diff --git a/x-pack/plugins/spaces/public/management/management_service.test.ts b/x-pack/plugins/spaces/public/management/management_service.test.ts new file mode 100644 index 0000000000000..d4c6bdaea2776 --- /dev/null +++ b/x-pack/plugins/spaces/public/management/management_service.test.ts @@ -0,0 +1,138 @@ +/* + * 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 { ManagementService } from '.'; +import { coreMock } from 'src/core/public/mocks'; +import { spacesManagerMock } from '../spaces_manager/mocks'; +import { managementPluginMock } from '../../../../../src/plugins/management/public/mocks'; +import { ManagementSection } from 'src/plugins/management/public'; +import { Capabilities } from 'kibana/public'; + +describe('ManagementService', () => { + describe('#setup', () => { + it('registers the spaces management page under the kibana section', () => { + const mockKibanaSection = ({ + registerApp: jest.fn(), + } as unknown) as ManagementSection; + const deps = { + management: managementPluginMock.createSetupContract(), + getStartServices: coreMock.createSetup().getStartServices, + spacesManager: spacesManagerMock.create(), + }; + + deps.management.sections.getSection.mockReturnValue(mockKibanaSection); + + const service = new ManagementService(); + service.setup(deps); + + expect(deps.management.sections.getSection).toHaveBeenCalledTimes(1); + expect(deps.management.sections.getSection).toHaveBeenCalledWith('kibana'); + + expect(mockKibanaSection.registerApp).toHaveBeenCalledTimes(1); + expect(mockKibanaSection.registerApp).toHaveBeenCalledWith({ + id: 'spaces', + title: 'Spaces', + order: 10, + mount: expect.any(Function), + }); + }); + + it('will not crash if the kibana section is missing', () => { + const deps = { + management: managementPluginMock.createSetupContract(), + getStartServices: coreMock.createSetup().getStartServices, + spacesManager: spacesManagerMock.create(), + }; + + const service = new ManagementService(); + service.setup(deps); + }); + }); + + describe('#start', () => { + it('disables the spaces management page if the user is not authorized', () => { + const mockSpacesManagementPage = { disable: jest.fn() }; + const mockKibanaSection = ({ + registerApp: jest.fn().mockReturnValue(mockSpacesManagementPage), + } as unknown) as ManagementSection; + + const deps = { + management: managementPluginMock.createSetupContract(), + getStartServices: coreMock.createSetup().getStartServices, + spacesManager: spacesManagerMock.create(), + }; + + deps.management.sections.getSection.mockImplementation(id => { + if (id === 'kibana') return mockKibanaSection; + throw new Error(`unexpected getSection call: ${id}`); + }); + + const service = new ManagementService(); + service.setup(deps); + + const capabilities = ({ spaces: { manage: false } } as unknown) as Capabilities; + service.start({ capabilities }); + + expect(mockKibanaSection.registerApp).toHaveBeenCalledTimes(1); + expect(mockSpacesManagementPage.disable).toHaveBeenCalledTimes(1); + }); + + it('does not disable the spaces management page if the user is authorized', () => { + const mockSpacesManagementPage = { disable: jest.fn() }; + const mockKibanaSection = ({ + registerApp: jest.fn().mockReturnValue(mockSpacesManagementPage), + } as unknown) as ManagementSection; + + const deps = { + management: managementPluginMock.createSetupContract(), + getStartServices: coreMock.createSetup().getStartServices, + spacesManager: spacesManagerMock.create(), + }; + + deps.management.sections.getSection.mockImplementation(id => { + if (id === 'kibana') return mockKibanaSection; + throw new Error(`unexpected getSection call: ${id}`); + }); + + const service = new ManagementService(); + service.setup(deps); + + const capabilities = ({ spaces: { manage: true } } as unknown) as Capabilities; + service.start({ capabilities }); + + expect(mockKibanaSection.registerApp).toHaveBeenCalledTimes(1); + expect(mockSpacesManagementPage.disable).toHaveBeenCalledTimes(0); + }); + }); + + describe('#stop', () => { + it('disables the spaces management page', () => { + const mockSpacesManagementPage = { disable: jest.fn() }; + const mockKibanaSection = ({ + registerApp: jest.fn().mockReturnValue(mockSpacesManagementPage), + } as unknown) as ManagementSection; + + const deps = { + management: managementPluginMock.createSetupContract(), + getStartServices: coreMock.createSetup().getStartServices, + spacesManager: spacesManagerMock.create(), + }; + + deps.management.sections.getSection.mockImplementation(id => { + if (id === 'kibana') return mockKibanaSection; + throw new Error(`unexpected getSection call: ${id}`); + }); + + const service = new ManagementService(); + service.setup(deps); + + service.stop(); + + expect(mockKibanaSection.registerApp).toHaveBeenCalledTimes(1); + expect(mockSpacesManagementPage.disable).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/x-pack/plugins/spaces/public/management/management_service.tsx b/x-pack/plugins/spaces/public/management/management_service.tsx new file mode 100644 index 0000000000000..c81a3497762a5 --- /dev/null +++ b/x-pack/plugins/spaces/public/management/management_service.tsx @@ -0,0 +1,51 @@ +/* + * 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 { ManagementSetup, ManagementApp } from 'src/plugins/management/public'; +import { CoreSetup, Capabilities } from 'src/core/public'; +import { SecurityLicense } from '../../../security/public'; +import { SpacesManager } from '../spaces_manager'; +import { PluginsStart } from '../plugin'; +import { spacesManagementApp } from './spaces_management_app'; + +interface SetupDeps { + management: ManagementSetup; + getStartServices: CoreSetup['getStartServices']; + spacesManager: SpacesManager; + securityLicense?: SecurityLicense; +} + +interface StartDeps { + capabilities: Capabilities; +} +export class ManagementService { + private registeredSpacesManagementApp?: ManagementApp; + + public setup({ getStartServices, management, spacesManager, securityLicense }: SetupDeps) { + const kibanaSection = management.sections.getSection('kibana'); + if (kibanaSection) { + this.registeredSpacesManagementApp = kibanaSection.registerApp( + spacesManagementApp.create({ getStartServices, spacesManager, securityLicense }) + ); + } + } + + public start({ capabilities }: StartDeps) { + if (!capabilities.spaces.manage) { + this.disableSpacesApp(); + } + } + + public stop() { + this.disableSpacesApp(); + } + + private disableSpacesApp() { + if (this.registeredSpacesManagementApp) { + this.registeredSpacesManagementApp.disable(); + } + } +} diff --git a/x-pack/legacy/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap b/x-pack/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap similarity index 99% rename from x-pack/legacy/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap rename to x-pack/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap index 02dbca28c7b66..aa6db7e22fd5d 100644 --- a/x-pack/legacy/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap +++ b/x-pack/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap @@ -39,7 +39,7 @@ exports[`SpacesGridPage renders as expected 1`] = ` > { +export class SpacesGridPage extends Component { constructor(props: Props) { super(props); this.state = { @@ -72,15 +73,13 @@ class SpacesGridPageUI extends Component { return (
{this.getPageContent()} - + {this.props.securityEnabled && } {this.getConfirmDeleteModal()}
); } public getPageContent() { - const { intl } = this.props; - if (!this.props.capabilities.spaces.manage) { return ; } @@ -114,10 +113,12 @@ class SpacesGridPageUI extends Component { sorting={true} search={{ box: { - placeholder: intl.formatMessage({ - id: 'xpack.spaces.management.spacesGridPage.searchPlaceholder', - defaultMessage: 'Search', - }), + placeholder: i18n.translate( + 'xpack.spaces.management.spacesGridPage.searchPlaceholder', + { + defaultMessage: 'Search', + } + ), }, }} loading={this.state.loading} @@ -138,12 +139,7 @@ class SpacesGridPageUI extends Component { public getPrimaryActionButton() { return ( - { - window.location.hash = `#/management/spaces/create`; - }} - > + { }; public deleteSpace = async () => { - const { intl } = this.props; const { spacesManager } = this.props; const space = this.state.selectedSpace; @@ -188,16 +183,13 @@ class SpacesGridPageUI extends Component { } catch (error) { const { message: errorMessage = '' } = error.data || {}; - toastNotifications.addDanger( - intl.formatMessage( - { - id: 'xpack.spaces.management.spacesGridPage.errorDeletingSpaceErrorMessage', - defaultMessage: 'Error deleting space: {errorMessage}', - }, - { + this.props.notifications.toasts.addDanger( + i18n.translate('xpack.spaces.management.spacesGridPage.errorDeletingSpaceErrorMessage', { + defaultMessage: 'Error deleting space: {errorMessage}', + values: { errorMessage, - } - ) + }, + }) ); } @@ -207,21 +199,19 @@ class SpacesGridPageUI extends Component { this.loadGrid(); - const message = intl.formatMessage( + const message = i18n.translate( + 'xpack.spaces.management.spacesGridPage.spaceSuccessfullyDeletedNotificationMessage', { - id: 'xpack.spaces.management.spacesGridPage.spaceSuccessfullyDeletedNotificationMessage', defaultMessage: 'Deleted "{spaceName}" space.', - }, - { - spaceName: space.name, + values: { spaceName: space.name }, } ); - toastNotifications.addSuccess(message); + this.props.notifications.toasts.addSuccess(message); }; public loadGrid = async () => { - const { spacesManager } = this.props; + const { spacesManager, http } = this.props; this.setState({ loading: true, @@ -230,7 +220,7 @@ class SpacesGridPageUI extends Component { }); const getSpaces = spacesManager.getSpaces(); - const getFeatures = kfetch({ method: 'get', pathname: '/api/features' }); + const getFeatures = http.get('/api/features'); try { const [spaces, features] = await Promise.all([getSpaces, getFeatures]); @@ -248,51 +238,37 @@ class SpacesGridPageUI extends Component { }; public getColumnConfig() { - const { intl } = this.props; return [ { field: 'initials', name: '', width: '50px', render: (value: string, record: Space) => ( - { - this.onEditSpaceClick(record); - }} - > + ), }, { field: 'name', - name: intl.formatMessage({ - id: 'xpack.spaces.management.spacesGridPage.spaceColumnName', + name: i18n.translate('xpack.spaces.management.spacesGridPage.spaceColumnName', { defaultMessage: 'Space', }), sortable: true, render: (value: string, record: Space) => ( - { - this.onEditSpaceClick(record); - }} - > - {value} - + {value} ), }, { field: 'description', - name: intl.formatMessage({ - id: 'xpack.spaces.management.spacesGridPage.descriptionColumnName', + name: i18n.translate('xpack.spaces.management.spacesGridPage.descriptionColumnName', { defaultMessage: 'Description', }), sortable: true, }, { field: 'disabledFeatures', - name: intl.formatMessage({ - id: 'xpack.spaces.management.spacesGridPage.featuresColumnName', + name: i18n.translate('xpack.spaces.management.spacesGridPage.featuresColumnName', { defaultMessage: 'Features', }), sortable: (space: Space) => { @@ -332,8 +308,7 @@ class SpacesGridPageUI extends Component { }, { field: 'id', - name: intl.formatMessage({ - id: 'xpack.spaces.management.spacesGridPage.identifierColumnName', + name: i18n.translate('xpack.spaces.management.spacesGridPage.identifierColumnName', { defaultMessage: 'Identifier', }), sortable: true, @@ -345,26 +320,23 @@ class SpacesGridPageUI extends Component { }, }, { - name: intl.formatMessage({ - id: 'xpack.spaces.management.spacesGridPage.actionsColumnName', + name: i18n.translate('xpack.spaces.management.spacesGridPage.actionsColumnName', { defaultMessage: 'Actions', }), actions: [ { render: (record: Space) => ( this.onEditSpaceClick(record)} + href={this.getEditSpacePath(record)} /> ), }, @@ -372,13 +344,11 @@ class SpacesGridPageUI extends Component { available: (record: Space) => !isReservedSpace(record), render: (record: Space) => ( { ]; } - private onEditSpaceClick = (space: Space) => { - window.location.hash = `#/management/spaces/edit/${encodeURIComponent(space.id)}`; + private getEditSpacePath = (space: Space) => { + return `#/management/kibana/spaces/edit/${encodeURIComponent(space.id)}`; }; private onDeleteSpaceClick = (space: Space) => { @@ -403,5 +373,3 @@ class SpacesGridPageUI extends Component { }); }; } - -export const SpacesGridPage = injectI18n(SpacesGridPageUI); diff --git a/x-pack/legacy/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx similarity index 71% rename from x-pack/legacy/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx rename to x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx index 7856d2e7bee01..90c7aba65e3d6 100644 --- a/x-pack/legacy/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx @@ -3,16 +3,15 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -jest.mock('ui/kfetch', () => ({ - kfetch: () => Promise.resolve([]), -})); -import '../../__mocks__/xpack_info'; + import React from 'react'; -import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; +import { mountWithIntl, shallowWithIntl, nextTick } from 'test_utils/enzyme_helpers'; import { SpaceAvatar } from '../../space_avatar'; import { spacesManagerMock } from '../../spaces_manager/mocks'; import { SpacesManager } from '../../spaces_manager'; import { SpacesGridPage } from './spaces_grid_page'; +import { httpServiceMock } from 'src/core/public/mocks'; +import { notificationServiceMock } from 'src/core/public/mocks'; const spaces = [ { @@ -41,11 +40,16 @@ spacesManager.getSpaces = jest.fn().mockResolvedValue(spaces); describe('SpacesGridPage', () => { it('renders as expected', () => { + const httpStart = httpServiceMock.createStartContract(); + httpStart.get.mockResolvedValue([]); + expect( shallowWithIntl( - { }); it('renders the list of spaces', async () => { + const httpStart = httpServiceMock.createStartContract(); + httpStart.get.mockResolvedValue([]); + const wrapper = mountWithIntl( - { ); // allow spacesManager to load spaces - await Promise.resolve(); - wrapper.update(); - await Promise.resolve(); + await nextTick(); wrapper.update(); expect(wrapper.find(SpaceAvatar)).toHaveLength(spaces.length); diff --git a/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx b/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx new file mode 100644 index 0000000000000..b19ef995283da --- /dev/null +++ b/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx @@ -0,0 +1,137 @@ +/* + * 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. + */ + +jest.mock('./spaces_grid', () => ({ + SpacesGridPage: (props: any) => `Spaces Page: ${JSON.stringify(props)}`, +})); + +jest.mock('./edit_space', () => ({ + ManageSpacePage: (props: any) => { + if (props.spacesManager && props.onLoadSpace) { + props.spacesManager.getSpace().then((space: any) => props.onLoadSpace(space)); + } + return `Spaces Edit Page: ${JSON.stringify(props)}`; + }, +})); + +import { spacesManagementApp } from './spaces_management_app'; + +import { coreMock } from '../../../../../src/core/public/mocks'; +import { securityMock } from '../../../security/public/mocks'; +import { spacesManagerMock } from '../spaces_manager/mocks'; +import { SecurityLicenseFeatures } from '../../../security/public'; + +async function mountApp(basePath: string, spaceId?: string) { + const container = document.createElement('div'); + const setBreadcrumbs = jest.fn(); + + const spacesManager = spacesManagerMock.create(); + if (spaceId) { + spacesManager.getSpace.mockResolvedValue({ + id: spaceId, + name: `space with id ${spaceId}`, + disabledFeatures: [], + }); + } + + const securityLicense = securityMock.createSetup().license; + securityLicense.getFeatures.mockReturnValue({ + showLinks: true, + } as SecurityLicenseFeatures); + + const unmount = await spacesManagementApp + .create({ + spacesManager, + securityLicense, + getStartServices: coreMock.createSetup().getStartServices as any, + }) + .mount({ basePath, element: container, setBreadcrumbs }); + + return { unmount, container, setBreadcrumbs }; +} + +describe('spacesManagementApp', () => { + it('create() returns proper management app descriptor', () => { + expect( + spacesManagementApp.create({ + spacesManager: spacesManagerMock.create(), + securityLicense: securityMock.createSetup().license, + getStartServices: coreMock.createSetup().getStartServices as any, + }) + ).toMatchInlineSnapshot(` + Object { + "id": "spaces", + "mount": [Function], + "order": 10, + "title": "Spaces", + } + `); + }); + + it('mount() works for the `grid` page', async () => { + const basePath = '/some-base-path/spaces'; + window.location.hash = basePath; + + const { setBreadcrumbs, container, unmount } = await mountApp(basePath); + + expect(setBreadcrumbs).toHaveBeenCalledTimes(1); + expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `#${basePath}`, text: 'Spaces' }]); + expect(container).toMatchInlineSnapshot(` +
+ Spaces Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"http":{"basePath":{"basePath":""},"anonymousPaths":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{"_isScalar":false}},"securityEnabled":true} +
+ `); + + unmount(); + + expect(container).toMatchInlineSnapshot(`
`); + }); + + it('mount() works for the `create space` page', async () => { + const basePath = '/some-base-path/spaces'; + window.location.hash = `${basePath}/create`; + + const { setBreadcrumbs, container, unmount } = await mountApp(basePath); + + expect(setBreadcrumbs).toHaveBeenCalledTimes(1); + expect(setBreadcrumbs).toHaveBeenCalledWith([ + { href: `#${basePath}`, text: 'Spaces' }, + { text: 'Create' }, + ]); + expect(container).toMatchInlineSnapshot(` +
+ Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"http":{"basePath":{"basePath":""},"anonymousPaths":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{"_isScalar":false}},"securityEnabled":true} +
+ `); + + unmount(); + + expect(container).toMatchInlineSnapshot(`
`); + }); + + it('mount() works for the `edit space` page', async () => { + const basePath = '/some-base-path/spaces'; + const spaceId = 'some-space'; + window.location.hash = `${basePath}/edit/${spaceId}`; + + const { setBreadcrumbs, container, unmount } = await mountApp(basePath, spaceId); + + expect(setBreadcrumbs).toHaveBeenCalledTimes(1); + expect(setBreadcrumbs).toHaveBeenCalledWith([ + { href: `#${basePath}`, text: 'Spaces' }, + { href: `#/some-base-path/spaces/edit/${spaceId}`, text: `space with id some-space` }, + ]); + expect(container).toMatchInlineSnapshot(` +
+ Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"http":{"basePath":{"basePath":""},"anonymousPaths":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{"_isScalar":false}},"spaceId":"some-space","securityEnabled":true} +
+ `); + + unmount(); + + expect(container).toMatchInlineSnapshot(`
`); + }); +}); diff --git a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx new file mode 100644 index 0000000000000..663237cfc2e8a --- /dev/null +++ b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx @@ -0,0 +1,131 @@ +/* + * 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 React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { HashRouter as Router, Route, Switch, useParams } from 'react-router-dom'; +import { i18n } from '@kbn/i18n'; +import { CoreSetup } from 'src/core/public'; +import { SecurityLicense } from '../../../security/public'; +import { RegisterManagementAppArgs } from '../../../../../src/plugins/management/public'; +import { PluginsStart } from '../plugin'; +import { SpacesManager } from '../spaces_manager'; +import { SpacesGridPage } from './spaces_grid'; +import { ManageSpacePage } from './edit_space'; +import { Space } from '..'; + +interface CreateParams { + getStartServices: CoreSetup['getStartServices']; + spacesManager: SpacesManager; + securityLicense?: SecurityLicense; +} + +export const spacesManagementApp = Object.freeze({ + id: 'spaces', + create({ getStartServices, spacesManager, securityLicense }: CreateParams) { + return { + id: this.id, + order: 10, + title: i18n.translate('xpack.spaces.displayName', { + defaultMessage: 'Spaces', + }), + async mount({ basePath, element, setBreadcrumbs }) { + const [{ http, notifications, i18n: i18nStart, application }] = await getStartServices(); + const spacesBreadcrumbs = [ + { + text: i18n.translate('xpack.spaces.management.breadcrumb', { + defaultMessage: 'Spaces', + }), + href: `#${basePath}`, + }, + ]; + + const SpacesGridPageWithBreadcrumbs = () => { + setBreadcrumbs(spacesBreadcrumbs); + return ( + + ); + }; + + const CreateSpacePageWithBreadcrumbs = () => { + setBreadcrumbs([ + ...spacesBreadcrumbs, + { + text: i18n.translate('xpack.spaces.management.createSpaceBreadcrumb', { + defaultMessage: 'Create', + }), + }, + ]); + + return ( + + ); + }; + + const EditSpacePageWithBreadcrumbs = () => { + const { spaceId } = useParams<{ spaceId: string }>(); + + const onLoadSpace = (space: Space) => { + setBreadcrumbs([ + ...spacesBreadcrumbs, + { + text: space.name, + href: `#${basePath}/edit/${encodeURIComponent(space.id)}`, + }, + ]); + }; + + return ( + + ); + }; + + render( + + + + + + + + + + + + + + + , + element + ); + + return () => { + unmountComponentAtNode(element); + }; + }, + } as RegisterManagementAppArgs; + }, +}); diff --git a/x-pack/legacy/plugins/spaces/public/nav_control/__snapshots__/nav_control_popover.test.tsx.snap b/x-pack/plugins/spaces/public/nav_control/__snapshots__/nav_control_popover.test.tsx.snap similarity index 96% rename from x-pack/legacy/plugins/spaces/public/nav_control/__snapshots__/nav_control_popover.test.tsx.snap rename to x-pack/plugins/spaces/public/nav_control/__snapshots__/nav_control_popover.test.tsx.snap index 45daa03e94c2e..22d65f4600e05 100644 --- a/x-pack/legacy/plugins/spaces/public/nav_control/__snapshots__/nav_control_popover.test.tsx.snap +++ b/x-pack/plugins/spaces/public/nav_control/__snapshots__/nav_control_popover.test.tsx.snap @@ -40,6 +40,7 @@ exports[`NavControlPopover renders without crashing 1`] = ` } } id="headerSpacesMenuContent" + navigateToApp={[MockFunction]} onManageSpacesClick={[Function]} /> diff --git a/x-pack/legacy/plugins/spaces/public/nav_control/_index.scss b/x-pack/plugins/spaces/public/nav_control/_index.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/nav_control/_index.scss rename to x-pack/plugins/spaces/public/nav_control/_index.scss diff --git a/x-pack/legacy/plugins/spaces/public/nav_control/_nav_control.scss b/x-pack/plugins/spaces/public/nav_control/_nav_control.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/nav_control/_nav_control.scss rename to x-pack/plugins/spaces/public/nav_control/_nav_control.scss diff --git a/x-pack/legacy/plugins/spaces/public/nav_control/components/__snapshots__/manage_spaces_button.test.tsx.snap b/x-pack/plugins/spaces/public/nav_control/components/__snapshots__/manage_spaces_button.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/nav_control/components/__snapshots__/manage_spaces_button.test.tsx.snap rename to x-pack/plugins/spaces/public/nav_control/components/__snapshots__/manage_spaces_button.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/nav_control/components/_index.scss b/x-pack/plugins/spaces/public/nav_control/components/_index.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/nav_control/components/_index.scss rename to x-pack/plugins/spaces/public/nav_control/components/_index.scss diff --git a/x-pack/legacy/plugins/spaces/public/nav_control/components/_spaces_description.scss b/x-pack/plugins/spaces/public/nav_control/components/_spaces_description.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/nav_control/components/_spaces_description.scss rename to x-pack/plugins/spaces/public/nav_control/components/_spaces_description.scss diff --git a/x-pack/legacy/plugins/spaces/public/nav_control/components/_spaces_menu.scss b/x-pack/plugins/spaces/public/nav_control/components/_spaces_menu.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/nav_control/components/_spaces_menu.scss rename to x-pack/plugins/spaces/public/nav_control/components/_spaces_menu.scss diff --git a/x-pack/legacy/plugins/spaces/public/nav_control/components/manage_spaces_button.test.tsx b/x-pack/plugins/spaces/public/nav_control/components/manage_spaces_button.test.tsx similarity index 94% rename from x-pack/legacy/plugins/spaces/public/nav_control/components/manage_spaces_button.test.tsx rename to x-pack/plugins/spaces/public/nav_control/components/manage_spaces_button.test.tsx index 2dc6ae919c018..009b6aa89d089 100644 --- a/x-pack/legacy/plugins/spaces/public/nav_control/components/manage_spaces_button.test.tsx +++ b/x-pack/plugins/spaces/public/nav_control/components/manage_spaces_button.test.tsx @@ -12,6 +12,7 @@ describe('ManageSpacesButton', () => { it('renders as expected', () => { const component = ( { it(`doesn't render if user profile forbids managing spaces`, () => { const component = ( void; capabilities: Capabilities; + navigateToApp: ApplicationStart['navigateToApp']; } export class ManageSpacesButton extends Component { @@ -45,6 +45,7 @@ export class ManageSpacesButton extends Component { if (this.props.onClick) { this.props.onClick(); } - window.location.replace(getManageSpacesUrl()); + + this.props.navigateToApp('kibana', { path: '#/management/kibana/spaces' }); }; } diff --git a/x-pack/legacy/plugins/spaces/public/nav_control/components/spaces_description.tsx b/x-pack/plugins/spaces/public/nav_control/components/spaces_description.tsx similarity index 88% rename from x-pack/legacy/plugins/spaces/public/nav_control/components/spaces_description.tsx rename to x-pack/plugins/spaces/public/nav_control/components/spaces_description.tsx index b6982a3d687a6..3a431ae0929b8 100644 --- a/x-pack/legacy/plugins/spaces/public/nav_control/components/spaces_description.tsx +++ b/x-pack/plugins/spaces/public/nav_control/components/spaces_description.tsx @@ -6,7 +6,7 @@ import { EuiContextMenuPanel, EuiText } from '@elastic/eui'; import React, { FC } from 'react'; -import { Capabilities } from 'src/core/public'; +import { Capabilities, ApplicationStart } from 'src/core/public'; import { ManageSpacesButton } from './manage_spaces_button'; import { getSpacesFeatureDescription } from '../../constants'; @@ -14,6 +14,7 @@ interface Props { id: string; onManageSpacesClick: () => void; capabilities: Capabilities; + navigateToApp: ApplicationStart['navigateToApp']; } export const SpacesDescription: FC = (props: Props) => { @@ -34,6 +35,7 @@ export const SpacesDescription: FC = (props: Props) => { style={{ width: `100%` }} onClick={props.onManageSpacesClick} capabilities={props.capabilities} + navigateToApp={props.navigateToApp} />
diff --git a/x-pack/legacy/plugins/spaces/public/nav_control/components/spaces_menu.tsx b/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx similarity index 97% rename from x-pack/legacy/plugins/spaces/public/nav_control/components/spaces_menu.tsx rename to x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx index 4d89f57d4ccf1..59656333f865e 100644 --- a/x-pack/legacy/plugins/spaces/public/nav_control/components/spaces_menu.tsx +++ b/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx @@ -13,7 +13,7 @@ import { } from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React, { Component, ReactElement } from 'react'; -import { Capabilities } from 'src/core/public'; +import { Capabilities, ApplicationStart } from 'src/core/public'; import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../../common/constants'; import { Space } from '../../../common/model/space'; import { ManageSpacesButton } from './manage_spaces_button'; @@ -27,6 +27,7 @@ interface Props { onManageSpacesClick: () => void; intl: InjectedIntl; capabilities: Capabilities; + navigateToApp: ApplicationStart['navigateToApp']; } interface State { @@ -166,6 +167,7 @@ class SpacesMenuUI extends Component { size="s" onClick={this.props.onManageSpacesClick} capabilities={this.props.capabilities} + navigateToApp={this.props.navigateToApp} /> ); }; diff --git a/x-pack/legacy/plugins/spaces/public/nav_control/index.ts b/x-pack/plugins/spaces/public/nav_control/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/nav_control/index.ts rename to x-pack/plugins/spaces/public/nav_control/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/nav_control/nav_control.tsx b/x-pack/plugins/spaces/public/nav_control/nav_control.tsx similarity index 95% rename from x-pack/legacy/plugins/spaces/public/nav_control/nav_control.tsx rename to x-pack/plugins/spaces/public/nav_control/nav_control.tsx index 9ec070eff3fed..53d7038cec4d5 100644 --- a/x-pack/legacy/plugins/spaces/public/nav_control/nav_control.tsx +++ b/x-pack/plugins/spaces/public/nav_control/nav_control.tsx @@ -25,6 +25,7 @@ export function initSpacesNavControl(spacesManager: SpacesManager, core: CoreSta spacesManager={spacesManager} anchorPosition="downLeft" capabilities={core.application.capabilities} + navigateToApp={core.application.navigateToApp} /> , targetDomElement diff --git a/x-pack/legacy/plugins/spaces/public/nav_control/nav_control_popover.test.tsx b/x-pack/plugins/spaces/public/nav_control/nav_control_popover.test.tsx similarity index 96% rename from x-pack/legacy/plugins/spaces/public/nav_control/nav_control_popover.test.tsx rename to x-pack/plugins/spaces/public/nav_control/nav_control_popover.test.tsx index 5ce141abb713e..0e0a3473be7ff 100644 --- a/x-pack/legacy/plugins/spaces/public/nav_control/nav_control_popover.test.tsx +++ b/x-pack/plugins/spaces/public/nav_control/nav_control_popover.test.tsx @@ -23,6 +23,7 @@ describe('NavControlPopover', () => { spacesManager={(spacesManager as unknown) as SpacesManager} anchorPosition={'downRight'} capabilities={{ navLinks: {}, management: {}, catalogue: {}, spaces: { manage: true } }} + navigateToApp={jest.fn()} /> ); expect(wrapper).toMatchSnapshot(); @@ -54,6 +55,7 @@ describe('NavControlPopover', () => { spacesManager={(spacesManager as unknown) as SpacesManager} anchorPosition={'rightCenter'} capabilities={{ navLinks: {}, management: {}, catalogue: {}, spaces: { manage: true } }} + navigateToApp={jest.fn()} /> ); diff --git a/x-pack/legacy/plugins/spaces/public/nav_control/nav_control_popover.tsx b/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx similarity index 95% rename from x-pack/legacy/plugins/spaces/public/nav_control/nav_control_popover.tsx rename to x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx index 59c8052a644da..ef7eff437c86a 100644 --- a/x-pack/legacy/plugins/spaces/public/nav_control/nav_control_popover.tsx +++ b/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx @@ -11,7 +11,7 @@ import { EuiHeaderSectionItemButton, } from '@elastic/eui'; import React, { Component } from 'react'; -import { Capabilities } from 'src/core/public'; +import { Capabilities, ApplicationStart } from 'src/core/public'; import { Subscription } from 'rxjs'; import { Space } from '../../common/model/space'; import { SpaceAvatar } from '../space_avatar'; @@ -23,6 +23,7 @@ interface Props { spacesManager: SpacesManager; anchorPosition: PopoverAnchorPosition; capabilities: Capabilities; + navigateToApp: ApplicationStart['navigateToApp']; } interface State { @@ -76,6 +77,7 @@ export class NavControlPopover extends Component { id={popoutContentId} onManageSpacesClick={this.toggleSpaceSelector} capabilities={this.props.capabilities} + navigateToApp={this.props.navigateToApp} /> ); } else { @@ -87,6 +89,7 @@ export class NavControlPopover extends Component { onSelectSpace={this.onSelectSpace} onManageSpacesClick={this.toggleSpaceSelector} capabilities={this.props.capabilities} + navigateToApp={this.props.navigateToApp} /> ); } diff --git a/x-pack/legacy/plugins/spaces/public/nav_control/types.tsx b/x-pack/plugins/spaces/public/nav_control/types.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/nav_control/types.tsx rename to x-pack/plugins/spaces/public/nav_control/types.tsx diff --git a/x-pack/plugins/spaces/public/plugin.test.ts b/x-pack/plugins/spaces/public/plugin.test.ts new file mode 100644 index 0000000000000..28f8433be6fd9 --- /dev/null +++ b/x-pack/plugins/spaces/public/plugin.test.ts @@ -0,0 +1,109 @@ +/* + * 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 } from 'src/core/public/mocks'; +import { SpacesPlugin } from './plugin'; +import { homePluginMock } from '../../../../src/plugins/home/public/mocks'; +import { ManagementSection } from '../../../../src/plugins/management/public'; +import { managementPluginMock } from '../../../../src/plugins/management/public/mocks'; +import { advancedSettingsMock } from '../../../../src/plugins/advanced_settings/public/mocks'; + +describe('Spaces plugin', () => { + describe('#setup', () => { + it('should register the space selector app', () => { + const coreSetup = coreMock.createSetup(); + + const plugin = new SpacesPlugin(); + plugin.setup(coreSetup, {}); + + expect(coreSetup.application.register).toHaveBeenCalledWith( + expect.objectContaining({ + id: 'space_selector', + chromeless: true, + appRoute: '/spaces/space_selector', + mount: expect.any(Function), + }) + ); + }); + + it('should register the management and feature catalogue sections when the management and home plugins are both available', () => { + const coreSetup = coreMock.createSetup(); + + const kibanaSection = new ManagementSection( + { + id: 'kibana', + title: 'Mock Kibana Section', + order: 1, + }, + jest.fn(), + jest.fn(), + jest.fn(), + coreSetup.getStartServices + ); + + const registerAppSpy = jest.spyOn(kibanaSection, 'registerApp'); + + const home = homePluginMock.createSetupContract(); + + const management = managementPluginMock.createSetupContract(); + management.sections.getSection.mockReturnValue(kibanaSection); + + const plugin = new SpacesPlugin(); + plugin.setup(coreSetup, { + management, + home, + }); + + expect(registerAppSpy).toHaveBeenCalledWith(expect.objectContaining({ id: 'spaces' })); + + expect(home.featureCatalogue.register).toHaveBeenCalledWith( + expect.objectContaining({ + category: 'admin', + icon: 'spacesApp', + id: 'spaces', + showOnHomePage: true, + }) + ); + }); + + it('should register the advanced settings components if the advanced_settings plugin is available', () => { + const coreSetup = coreMock.createSetup(); + const advancedSettings = advancedSettingsMock.createSetupContract(); + + const plugin = new SpacesPlugin(); + plugin.setup(coreSetup, { advancedSettings }); + + expect(advancedSettings.component.register.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + "advanced_settings_page_title", + [Function], + true, + ], + Array [ + "advanced_settings_page_subtitle", + [Function], + true, + ], + ] + `); + }); + }); + + describe('#start', () => { + it('should register the spaces nav control', () => { + const coreSetup = coreMock.createSetup(); + const coreStart = coreMock.createStart(); + + const plugin = new SpacesPlugin(); + plugin.setup(coreSetup, {}); + + plugin.start(coreStart, {}); + + expect(coreStart.chrome.navControls.registerLeft).toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/spaces/public/plugin.tsx b/x-pack/plugins/spaces/public/plugin.tsx new file mode 100644 index 0000000000000..73dae84c1873b --- /dev/null +++ b/x-pack/plugins/spaces/public/plugin.tsx @@ -0,0 +1,120 @@ +/* + * 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 { CoreSetup, CoreStart, Plugin } from 'src/core/public'; +import { HomePublicPluginSetup } from 'src/plugins/home/public'; +import { SavedObjectsManagementAction } from 'src/legacy/core_plugins/management/public'; +import { ManagementStart, ManagementSetup } from 'src/plugins/management/public'; +import { AdvancedSettingsSetup } from 'src/plugins/advanced_settings/public'; +import { SecurityPluginStart, SecurityPluginSetup } from '../../security/public'; +import { SpacesManager } from './spaces_manager'; +import { initSpacesNavControl } from './nav_control'; +import { createSpacesFeatureCatalogueEntry } from './create_feature_catalogue_entry'; +import { CopySavedObjectsToSpaceService } from './copy_saved_objects_to_space'; +import { AdvancedSettingsService } from './advanced_settings'; +import { ManagementService } from './management'; +import { spaceSelectorApp } from './space_selector'; + +export interface PluginsSetup { + advancedSettings?: AdvancedSettingsSetup; + home?: HomePublicPluginSetup; + management?: ManagementSetup; + security?: SecurityPluginSetup; +} + +export interface PluginsStart { + management?: ManagementStart; + security?: SecurityPluginStart; +} + +interface LegacyAPI { + registerSavedObjectsManagementAction: (action: SavedObjectsManagementAction) => void; +} + +export type SpacesPluginSetup = ReturnType; +export type SpacesPluginStart = ReturnType; + +export class SpacesPlugin implements Plugin { + private spacesManager!: SpacesManager; + + private managementService?: ManagementService; + + public setup(core: CoreSetup, plugins: PluginsSetup) { + const serverBasePath = core.injectedMetadata.getInjectedVar('serverBasePath') as string; + this.spacesManager = new SpacesManager(serverBasePath, core.http); + + if (plugins.home) { + plugins.home.featureCatalogue.register(createSpacesFeatureCatalogueEntry()); + } + + if (plugins.management) { + this.managementService = new ManagementService(); + this.managementService.setup({ + management: plugins.management, + getStartServices: core.getStartServices, + spacesManager: this.spacesManager, + securityLicense: plugins.security?.license, + }); + } + + if (plugins.advancedSettings) { + const advancedSettingsService = new AdvancedSettingsService(); + advancedSettingsService.setup({ + getActiveSpace: () => this.spacesManager.getActiveSpace(), + componentRegistry: plugins.advancedSettings.component, + }); + } + + spaceSelectorApp.create({ + getStartServices: core.getStartServices, + application: core.application, + spacesManager: this.spacesManager, + }); + + return { + registerLegacyAPI: (legacyAPI: LegacyAPI) => { + const copySavedObjectsToSpaceService = new CopySavedObjectsToSpaceService(); + copySavedObjectsToSpaceService.setup({ + spacesManager: this.spacesManager, + managementSetup: { + savedObjects: { + registry: { + register: action => legacyAPI.registerSavedObjectsManagementAction(action), + has: () => { + throw new Error('not available in legacy shim'); + }, + get: () => { + throw new Error('not available in legacy shim'); + }, + }, + }, + }, + notificationsSetup: core.notifications, + }); + }, + }; + } + + public start(core: CoreStart, plugins: PluginsStart) { + initSpacesNavControl(this.spacesManager, core); + + if (this.managementService) { + this.managementService.start({ capabilities: core.application.capabilities }); + } + + return { + activeSpace$: this.spacesManager.onActiveSpaceChange$, + getActiveSpace: () => this.spacesManager.getActiveSpace(), + }; + } + + public stop() { + if (this.managementService) { + this.managementService.stop(); + this.managementService = undefined; + } + } +} diff --git a/x-pack/legacy/plugins/spaces/public/space_avatar/__snapshots__/space_avatar.test.tsx.snap b/x-pack/plugins/spaces/public/space_avatar/__snapshots__/space_avatar.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/space_avatar/__snapshots__/space_avatar.test.tsx.snap rename to x-pack/plugins/spaces/public/space_avatar/__snapshots__/space_avatar.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/space_avatar/index.ts b/x-pack/plugins/spaces/public/space_avatar/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/space_avatar/index.ts rename to x-pack/plugins/spaces/public/space_avatar/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/space_avatar/space_attributes.test.ts b/x-pack/plugins/spaces/public/space_avatar/space_attributes.test.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/space_avatar/space_attributes.test.ts rename to x-pack/plugins/spaces/public/space_avatar/space_attributes.test.ts diff --git a/x-pack/legacy/plugins/spaces/public/space_avatar/space_attributes.ts b/x-pack/plugins/spaces/public/space_avatar/space_attributes.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/space_avatar/space_attributes.ts rename to x-pack/plugins/spaces/public/space_avatar/space_attributes.ts diff --git a/x-pack/legacy/plugins/spaces/public/space_avatar/space_avatar.test.tsx b/x-pack/plugins/spaces/public/space_avatar/space_avatar.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/space_avatar/space_avatar.test.tsx rename to x-pack/plugins/spaces/public/space_avatar/space_avatar.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/space_avatar/space_avatar.tsx b/x-pack/plugins/spaces/public/space_avatar/space_avatar.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/space_avatar/space_avatar.tsx rename to x-pack/plugins/spaces/public/space_avatar/space_avatar.tsx diff --git a/x-pack/legacy/plugins/spaces/public/space_selector/__snapshots__/space_selector.test.tsx.snap b/x-pack/plugins/spaces/public/space_selector/__snapshots__/space_selector.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/space_selector/__snapshots__/space_selector.test.tsx.snap rename to x-pack/plugins/spaces/public/space_selector/__snapshots__/space_selector.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/space_selector/_index.scss b/x-pack/plugins/spaces/public/space_selector/_index.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/space_selector/_index.scss rename to x-pack/plugins/spaces/public/space_selector/_index.scss diff --git a/x-pack/legacy/plugins/spaces/public/space_selector/_space_selector.scss b/x-pack/plugins/spaces/public/space_selector/_space_selector.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/space_selector/_space_selector.scss rename to x-pack/plugins/spaces/public/space_selector/_space_selector.scss diff --git a/x-pack/legacy/plugins/spaces/public/space_selector/components/_index.scss b/x-pack/plugins/spaces/public/space_selector/components/_index.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/space_selector/components/_index.scss rename to x-pack/plugins/spaces/public/space_selector/components/_index.scss diff --git a/x-pack/legacy/plugins/spaces/public/space_selector/components/_space_card.scss b/x-pack/plugins/spaces/public/space_selector/components/_space_card.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/space_selector/components/_space_card.scss rename to x-pack/plugins/spaces/public/space_selector/components/_space_card.scss diff --git a/x-pack/legacy/plugins/spaces/public/space_selector/components/_space_cards.scss b/x-pack/plugins/spaces/public/space_selector/components/_space_cards.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/space_selector/components/_space_cards.scss rename to x-pack/plugins/spaces/public/space_selector/components/_space_cards.scss diff --git a/x-pack/legacy/plugins/spaces/public/space_selector/components/index.ts b/x-pack/plugins/spaces/public/space_selector/components/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/space_selector/components/index.ts rename to x-pack/plugins/spaces/public/space_selector/components/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/space_selector/components/space_card.test.tsx b/x-pack/plugins/spaces/public/space_selector/components/space_card.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/space_selector/components/space_card.test.tsx rename to x-pack/plugins/spaces/public/space_selector/components/space_card.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/space_selector/components/space_card.tsx b/x-pack/plugins/spaces/public/space_selector/components/space_card.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/space_selector/components/space_card.tsx rename to x-pack/plugins/spaces/public/space_selector/components/space_card.tsx diff --git a/x-pack/legacy/plugins/spaces/public/space_selector/components/space_cards.test.tsx b/x-pack/plugins/spaces/public/space_selector/components/space_cards.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/space_selector/components/space_cards.test.tsx rename to x-pack/plugins/spaces/public/space_selector/components/space_cards.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/space_selector/components/space_cards.tsx b/x-pack/plugins/spaces/public/space_selector/components/space_cards.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/space_selector/components/space_cards.tsx rename to x-pack/plugins/spaces/public/space_selector/components/space_cards.tsx diff --git a/x-pack/legacy/plugins/logstash/public/lib/register_home_feature/index.js b/x-pack/plugins/spaces/public/space_selector/index.tsx old mode 100755 new mode 100644 similarity index 81% rename from x-pack/legacy/plugins/logstash/public/lib/register_home_feature/index.js rename to x-pack/plugins/spaces/public/space_selector/index.tsx index 72e3f201bd4ca..b99689cbabab1 --- a/x-pack/legacy/plugins/logstash/public/lib/register_home_feature/index.js +++ b/x-pack/plugins/spaces/public/space_selector/index.tsx @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -import './register_home_feature'; +export { spaceSelectorApp } from './space_selector_app'; diff --git a/x-pack/legacy/plugins/spaces/public/space_selector/space_selector.test.tsx b/x-pack/plugins/spaces/public/space_selector/space_selector.test.tsx similarity index 82% rename from x-pack/legacy/plugins/spaces/public/space_selector/space_selector.test.tsx rename to x-pack/plugins/spaces/public/space_selector/space_selector.test.tsx index b4d0f96307500..c8173de1661be 100644 --- a/x-pack/legacy/plugins/spaces/public/space_selector/space_selector.test.tsx +++ b/x-pack/plugins/spaces/public/space_selector/space_selector.test.tsx @@ -7,8 +7,8 @@ import React from 'react'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import { Space } from '../../common/model/space'; -import { spacesManagerMock } from '../spaces_manager/mocks'; import { SpaceSelector } from './space_selector'; +import { spacesManagerMock } from '../spaces_manager/mocks'; function getSpacesManager(spaces: Space[] = []) { const manager = spacesManagerMock.create(); @@ -18,9 +18,7 @@ function getSpacesManager(spaces: Space[] = []) { test('it renders without crashing', () => { const spacesManager = getSpacesManager(); - const component = shallowWithIntl( - - ); + const component = shallowWithIntl(); expect(component).toMatchSnapshot(); }); @@ -36,9 +34,7 @@ test('it queries for spaces when loaded', () => { const spacesManager = getSpacesManager(spaces); - shallowWithIntl( - - ); + shallowWithIntl(); return Promise.resolve().then(() => { expect(spacesManager.getSpaces).toHaveBeenCalledTimes(1); diff --git a/x-pack/legacy/plugins/spaces/public/space_selector/space_selector.tsx b/x-pack/plugins/spaces/public/space_selector/space_selector.tsx similarity index 89% rename from x-pack/legacy/plugins/spaces/public/space_selector/space_selector.tsx rename to x-pack/plugins/spaces/public/space_selector/space_selector.tsx index 206d38454fa8c..b63de399b95c9 100644 --- a/x-pack/legacy/plugins/spaces/public/space_selector/space_selector.tsx +++ b/x-pack/plugins/spaces/public/space_selector/space_selector.tsx @@ -18,16 +18,18 @@ import { EuiTitle, EuiLoadingSpinner, } from '@elastic/eui'; -import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component, Fragment } from 'react'; -import { SpacesManager } from '../spaces_manager'; +import ReactDOM from 'react-dom'; +import { CoreStart } from 'src/core/public'; import { Space } from '../../common/model/space'; import { SpaceCards } from './components'; import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../common/constants'; +import { SpacesManager } from '../spaces_manager'; interface Props { spacesManager: SpacesManager; - intl: InjectedIntl; } interface State { @@ -36,7 +38,7 @@ interface State { spaces: Space[]; } -class SpaceSelectorUI extends Component { +export class SpaceSelector extends Component { private headerRef?: HTMLElement | null; constructor(props: Props) { super(props); @@ -152,7 +154,6 @@ class SpaceSelectorUI extends Component { } public getSearchField = () => { - const { intl } = this.props; if (!this.state.spaces || this.state.spaces.length < SPACE_SEARCH_COUNT_THRESHOLD) { return null; } @@ -162,8 +163,7 @@ class SpaceSelectorUI extends Component { // @ts-ignore onSearch doesn't exist on EuiFieldSearch { }; } -export const SpaceSelector = injectI18n(SpaceSelectorUI); +export const renderSpaceSelectorApp = (i18nStart: CoreStart['i18n'], el: Element, props: Props) => { + ReactDOM.render( + + + , + el + ); + return () => ReactDOM.unmountComponentAtNode(el); +}; diff --git a/x-pack/plugins/spaces/public/space_selector/space_selector_app.tsx b/x-pack/plugins/spaces/public/space_selector/space_selector_app.tsx new file mode 100644 index 0000000000000..29ead8d34994d --- /dev/null +++ b/x-pack/plugins/spaces/public/space_selector/space_selector_app.tsx @@ -0,0 +1,36 @@ +/* + * 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 { CoreSetup, AppMountParameters } from 'src/core/public'; +import { i18n } from '@kbn/i18n'; +import { SpacesManager } from '../spaces_manager'; + +interface CreateDeps { + application: CoreSetup['application']; + spacesManager: SpacesManager; + getStartServices: CoreSetup['getStartServices']; +} + +export const spaceSelectorApp = Object.freeze({ + id: 'space_selector', + create({ application, getStartServices, spacesManager }: CreateDeps) { + application.register({ + id: this.id, + title: i18n.translate('xpack.spaces.spaceSelector.appTitle', { + defaultMessage: 'Select a space', + }), + chromeless: true, + appRoute: '/spaces/space_selector', + mount: async (params: AppMountParameters) => { + const [[coreStart], { renderSpaceSelectorApp }] = await Promise.all([ + getStartServices(), + import('./space_selector'), + ]); + return renderSpaceSelectorApp(coreStart.i18n, params.element, { spacesManager }); + }, + }); + }, +}); diff --git a/x-pack/legacy/plugins/spaces/public/spaces_manager/index.ts b/x-pack/plugins/spaces/public/spaces_manager/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/spaces_manager/index.ts rename to x-pack/plugins/spaces/public/spaces_manager/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/spaces_manager/mocks.ts b/x-pack/plugins/spaces/public/spaces_manager/mocks.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/spaces_manager/mocks.ts rename to x-pack/plugins/spaces/public/spaces_manager/mocks.ts diff --git a/x-pack/legacy/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts rename to x-pack/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts diff --git a/x-pack/legacy/plugins/spaces/public/spaces_manager/spaces_manager.test.ts b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.test.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/spaces_manager/spaces_manager.test.ts rename to x-pack/plugins/spaces/public/spaces_manager/spaces_manager.test.ts diff --git a/x-pack/legacy/plugins/spaces/public/spaces_manager/spaces_manager.ts b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts similarity index 95% rename from x-pack/legacy/plugins/spaces/public/spaces_manager/spaces_manager.ts rename to x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts index e9c738cf40c69..e151dcd4f9368 100644 --- a/x-pack/legacy/plugins/spaces/public/spaces_manager/spaces_manager.ts +++ b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts @@ -6,12 +6,12 @@ import { Observable, BehaviorSubject } from 'rxjs'; import { skipWhile } from 'rxjs/operators'; import { HttpSetup } from 'src/core/public'; -import { SavedObjectsManagementRecord } from '../../../../../../src/legacy/core_plugins/management/public'; +import { SavedObjectsManagementRecord } from 'src/legacy/core_plugins/management/public'; import { Space } from '../../common/model/space'; import { GetSpacePurpose } from '../../common/model/types'; import { ENTER_SPACE_PATH } from '../../common/constants'; -import { addSpaceIdToPath } from '../../../../../plugins/spaces/common'; import { CopySavedObjectsToSpaceResponse } from '../copy_saved_objects_to_space/types'; +import { addSpaceIdToPath } from '../../common'; export class SpacesManager { private activeSpace$: BehaviorSubject = new BehaviorSubject(null); diff --git a/x-pack/plugins/spaces/server/index.ts b/x-pack/plugins/spaces/server/index.ts index 18f7575ff75d6..77eb3e9c73980 100644 --- a/x-pack/plugins/spaces/server/index.ts +++ b/x-pack/plugins/spaces/server/index.ts @@ -17,6 +17,7 @@ import { Plugin } from './plugin'; export { SpacesPluginSetup } from './plugin'; export { SpacesServiceSetup } from './spaces_service'; +export { Space } from '../common/model/space'; export const config = { schema: ConfigSchema }; export const plugin = (initializerContext: PluginInitializerContext) => diff --git a/x-pack/plugins/spaces/server/plugin.ts b/x-pack/plugins/spaces/server/plugin.ts index 52ff7eaee3d68..90c2da6e69df8 100644 --- a/x-pack/plugins/spaces/server/plugin.ts +++ b/x-pack/plugins/spaces/server/plugin.ts @@ -16,7 +16,6 @@ import { import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { SecurityPluginSetup } from '../../security/server'; import { LicensingPluginSetup } from '../../licensing/server'; -import { XPackMainPlugin } from '../../../legacy/plugins/xpack_main/server/xpack_main'; import { createDefaultSpace } from './lib/create_default_space'; // @ts-ignore import { AuditLogger } from '../../../../server/lib/audit_logger'; @@ -31,6 +30,7 @@ import { toggleUICapabilities } from './lib/toggle_ui_capabilities'; import { initSpacesRequestInterceptors } from './lib/request_interceptors'; import { initExternalSpacesApi } from './routes/api/external'; import { initInternalSpacesApi } from './routes/api/internal'; +import { initSpacesViewsRoutes } from './routes/views'; /** * Describes a set of APIs that is available in the legacy platform only and required by this plugin @@ -44,7 +44,6 @@ export interface LegacyAPI { legacyConfig: { kibanaIndex: string; }; - xpackMain: XPackMainPlugin; } export interface PluginsSetup { @@ -109,6 +108,12 @@ export class Plugin { config$: this.config$, }); + const viewRouter = core.http.createRouter(); + initSpacesViewsRoutes({ + viewRouter, + cspHeader: core.http.csp.header, + }); + const externalRouter = core.http.createRouter(); initExternalSpacesApi({ externalRouter, diff --git a/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_legacy_api.ts b/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_legacy_api.ts index dfeb094e34e25..812b02e94f591 100644 --- a/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_legacy_api.ts +++ b/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_legacy_api.ts @@ -104,7 +104,6 @@ export const createLegacyAPI = ({ kibanaIndex: '', }, auditLogger: {} as any, - xpackMain: {} as any, savedObjects: savedObjectsService, }; diff --git a/x-pack/plugins/spaces/server/routes/views/index.ts b/x-pack/plugins/spaces/server/routes/views/index.ts new file mode 100644 index 0000000000000..2a346c7e5241a --- /dev/null +++ b/x-pack/plugins/spaces/server/routes/views/index.ts @@ -0,0 +1,29 @@ +/* + * 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 { IRouter } from 'src/core/server'; + +export interface ViewRouteDeps { + viewRouter: IRouter; + cspHeader: string; +} + +export function initSpacesViewsRoutes(deps: ViewRouteDeps) { + deps.viewRouter.get( + { + path: '/spaces/space_selector', + validate: false, + }, + async (context, request, response) => { + return response.ok({ + headers: { + 'Content-Security-Policy': deps.cspHeader, + }, + body: await context.core.rendering.render({ includeUserSettings: true }), + }); + } + ); +} diff --git a/x-pack/plugins/task_manager/server/README.md b/x-pack/plugins/task_manager/server/README.md index a067358dc8841..a4154f3ecf212 100644 --- a/x-pack/plugins/task_manager/server/README.md +++ b/x-pack/plugins/task_manager/server/README.md @@ -261,6 +261,9 @@ The _Start_ Plugin api allow you to use Task Manager to facilitate your Plugin's remove: (id: string) => { // ... }, + get: (id: string) => { + // ... + }, schedule: (taskInstance: TaskInstanceWithDeprecatedFields, options?: any) => { // ... }, diff --git a/x-pack/plugins/task_manager/server/create_task_manager.test.ts b/x-pack/plugins/task_manager/server/create_task_manager.test.ts index 34258e15f45d1..133cfcac4c046 100644 --- a/x-pack/plugins/task_manager/server/create_task_manager.test.ts +++ b/x-pack/plugins/task_manager/server/create_task_manager.test.ts @@ -42,20 +42,21 @@ describe('createTaskManager', () => { const mockLegacyDeps = getMockLegacyDeps(); const setupResult = createTaskManager(mockCoreSetup, mockLegacyDeps); expect(setupResult).toMatchInlineSnapshot(` - TaskManager { - "addMiddleware": [MockFunction], - "assertUninitialized": [MockFunction], - "attemptToRun": [MockFunction], - "ensureScheduled": [MockFunction], - "fetch": [MockFunction], - "registerTaskDefinitions": [MockFunction], - "remove": [MockFunction], - "runNow": [MockFunction], - "schedule": [MockFunction], - "start": [MockFunction], - "stop": [MockFunction], - "waitUntilStarted": [MockFunction], - } - `); + TaskManager { + "addMiddleware": [MockFunction], + "assertUninitialized": [MockFunction], + "attemptToRun": [MockFunction], + "ensureScheduled": [MockFunction], + "fetch": [MockFunction], + "get": [MockFunction], + "registerTaskDefinitions": [MockFunction], + "remove": [MockFunction], + "runNow": [MockFunction], + "schedule": [MockFunction], + "start": [MockFunction], + "stop": [MockFunction], + "waitUntilStarted": [MockFunction], + } + `); }); }); diff --git a/x-pack/plugins/task_manager/server/mocks.ts b/x-pack/plugins/task_manager/server/mocks.ts index 00b27bd55e7dd..8ec05dd1bd401 100644 --- a/x-pack/plugins/task_manager/server/mocks.ts +++ b/x-pack/plugins/task_manager/server/mocks.ts @@ -18,6 +18,7 @@ const createSetupMock = () => { const createStartMock = () => { const mock: jest.Mocked = { fetch: jest.fn(), + get: jest.fn(), remove: jest.fn(), schedule: jest.fn(), runNow: jest.fn(), diff --git a/x-pack/plugins/task_manager/server/plugin.ts b/x-pack/plugins/task_manager/server/plugin.ts index 5e59be65c729d..fdfe0c068afcf 100644 --- a/x-pack/plugins/task_manager/server/plugin.ts +++ b/x-pack/plugins/task_manager/server/plugin.ts @@ -21,7 +21,7 @@ export type TaskManagerSetupContract = { export type TaskManagerStartContract = Pick< TaskManager, - 'fetch' | 'remove' | 'schedule' | 'runNow' | 'ensureScheduled' + 'fetch' | 'get' | 'remove' | 'schedule' | 'runNow' | 'ensureScheduled' >; export class TaskManagerPlugin @@ -69,6 +69,7 @@ export class TaskManagerPlugin public start(): TaskManagerStartContract { return { fetch: (...args) => this.taskManager.then(tm => tm.fetch(...args)), + get: (...args) => this.taskManager.then(tm => tm.get(...args)), remove: (...args) => this.taskManager.then(tm => tm.remove(...args)), schedule: (...args) => this.taskManager.then(tm => tm.schedule(...args)), runNow: (...args) => this.taskManager.then(tm => tm.runNow(...args)), diff --git a/x-pack/plugins/task_manager/server/task_manager.mock.ts b/x-pack/plugins/task_manager/server/task_manager.mock.ts index 89d1210b00671..1be1a81cdeb68 100644 --- a/x-pack/plugins/task_manager/server/task_manager.mock.ts +++ b/x-pack/plugins/task_manager/server/task_manager.mock.ts @@ -21,6 +21,7 @@ export const taskManagerMock = { ensureScheduled: jest.fn(), schedule: jest.fn(), fetch: jest.fn(), + get: jest.fn(), runNow: jest.fn(), remove: jest.fn(), ...overrides, diff --git a/x-pack/plugins/task_manager/server/task_manager.ts b/x-pack/plugins/task_manager/server/task_manager.ts index da9640fa3e071..641826de615b1 100644 --- a/x-pack/plugins/task_manager/server/task_manager.ts +++ b/x-pack/plugins/task_manager/server/task_manager.ts @@ -333,6 +333,17 @@ export class TaskManager { return this.store.fetch(opts); } + /** + * Get the current state of a specified task. + * + * @param {string} id + * @returns {Promise} + */ + public async get(id: string): Promise { + await this.waitUntilStarted(); + return this.store.get(id); + } + /** * Removes the specified task from the index. * diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 1c2a0fc3d5ac8..e9a5d9611c806 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -352,7 +352,6 @@ "common.ui.flotCharts.thuLabel": "木", "common.ui.flotCharts.tueLabel": "火", "common.ui.flotCharts.wedLabel": "水", - "common.ui.modals.cancelButtonLabel": "キャンセル", "common.ui.paginateControls.pageSizeLabel": "ページサイズ", "common.ui.paginateControls.scrollTopButtonLabel": "最上部に移動", "common.ui.savedObjects.confirmModal.overwriteButtonLabel": "上書き", @@ -1436,8 +1435,8 @@ "kbn.management.indexPattern.confirmOverwriteTitle": "{type} を上書きしますか?", "kbn.management.indexPattern.sectionsHeader": "インデックスパターン", "kbn.management.indexPattern.titleExistsLabel": "「{title}」というタイトルのインデックスパターンが既に存在します。", - "kbn.management.indexPatternHeader": "インデックスパターン", - "kbn.management.indexPatternLabel": "Elasticsearch からのデータの取得に役立つインデックスパターンを管理します。", + "management.indexPatternHeader": "インデックスパターン", + "management.indexPatternLabel": "Elasticsearch からのデータの取得に役立つインデックスパターンを管理します。", "kbn.management.indexPatternList.createButton.betaLabel": "ベータ", "kbn.management.indexPatternPrompt.exampleOne": "チャートを作成したりコンテンツを素早くクエリできるように log-west-001 という名前の単一のデータソースをインデックスします。", "kbn.management.indexPatternPrompt.exampleOneTitle": "単一のデータソース", @@ -1556,9 +1555,9 @@ "kbn.management.objects.objectsTable.table.typeFilterName": "タイプ", "kbn.management.objects.objectsTable.unableFindSavedObjectsNotificationMessage": "保存されたオブジェクトが見つかりません", "kbn.management.objects.parsingFieldErrorMessage": "{fieldName} をインデックスパターン {indexName} 用にパース中にエラーが発生しました: {errorMessage}", - "kbn.management.objects.savedObjectsDescription": "保存された検索、ビジュアライゼーション、ダッシュボードのインポート、エクスポート、管理を行います", + "management.objects.savedObjectsDescription": "保存された検索、ビジュアライゼーション、ダッシュボードのインポート、エクスポート、管理を行います", "kbn.management.objects.savedObjectsSectionLabel": "保存されたオブジェクト", - "kbn.management.objects.savedObjectsTitle": "保存されたオブジェクト", + "management.objects.savedObjectsTitle": "保存されたオブジェクト", "kbn.management.objects.view.cancelButtonAriaLabel": "キャンセル", "kbn.management.objects.view.cancelButtonLabel": "キャンセル", "kbn.management.objects.view.deleteItemButtonLabel": "{title} を削除", @@ -1576,8 +1575,8 @@ "kbn.management.objects.view.viewItemTitle": "{title} を表示", "kbn.management.savedObjects.editBreadcrumb": "{savedObjectType} を編集", "kbn.management.savedObjects.indexBreadcrumb": "保存されたオブジェクト", - "kbn.management.settings.advancedSettingsDescription": "Kibana の動作を管理する設定を直接変更します。", - "kbn.management.settings.advancedSettingsLabel": "高度な設定", + "advancedSettings.advancedSettingsDescription": "Kibana の動作を管理する設定を直接変更します。", + "advancedSettings.advancedSettingsLabel": "高度な設定", "kbn.management.settings.breadcrumb": "高度な設定", "kbn.management.settings.callOutCautionDescription": "これらの設定は非常に上級ユーザー向けなのでご注意ください。ここでの変更は Kibana の重要な部分に不具合を生じさせる可能性があります。これらの設定はドキュメントに記載されていなかったり、サポートされていなかったり、実験的であったりします。フィールドにデフォルトの値が設定されている場合、そのフィールドを未入力のままにするとデフォルトに戻り、他の構成により利用できない可能性があります。カスタム設定を削除すると、Kibana の構成から永久に削除されます。", "kbn.management.settings.callOutCautionTitle": "注意:不具合が起こる可能性があります", @@ -2792,7 +2791,6 @@ "timelion.noFunctionErrorMessage": "そのような関数はありません: {name}", "timelion.panels.noRenderFunctionErrorMessage": "パネルにはレンダリング関数が必要です", "timelion.panels.timechart.unknownIntervalErrorMessage": "不明な間隔", - "timelion.registerFeatureDescription": "時系列データを分析して結果を可視化するには、式言語を使用してください。", "timelion.requestHandlerErrorTitle": "Timelion リクエストエラー", "timelion.savedObjects.howToSaveAsNewDescription": "Kibana の以前のバージョンでは、{savedObjectName} の名前を変更すると新しい名前でコピーが作成されました。今後この操作を行うには、「新規 {savedObjectName} として保存」を使用します。", "timelion.savedObjects.saveAsNewLabel": "新規 {savedObjectName} として保存", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index c24f2952ef2ac..201e3c35ee282 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -352,7 +352,6 @@ "common.ui.flotCharts.thuLabel": "周四", "common.ui.flotCharts.tueLabel": "周二", "common.ui.flotCharts.wedLabel": "周三", - "common.ui.modals.cancelButtonLabel": "取消", "common.ui.paginateControls.pageSizeLabel": "页面大小", "common.ui.paginateControls.scrollTopButtonLabel": "滚动至顶部", "common.ui.savedObjects.confirmModal.overwriteButtonLabel": "覆盖", @@ -1436,8 +1435,8 @@ "kbn.management.indexPattern.confirmOverwriteTitle": "覆盖“{type}”?", "kbn.management.indexPattern.sectionsHeader": "索引模式", "kbn.management.indexPattern.titleExistsLabel": "具有标题 “{title}” 的索引模式已存在。", - "kbn.management.indexPatternHeader": "索引模式", - "kbn.management.indexPatternLabel": "管理帮助从 Elasticsearch 检索数据的索引模式。", + "management.indexPatternHeader": "索引模式", + "management.indexPatternLabel": "管理帮助从 Elasticsearch 检索数据的索引模式。", "kbn.management.indexPatternList.createButton.betaLabel": "公测版", "kbn.management.indexPatternPrompt.exampleOne": "索引单个称作 log-west-001 的数据源,以便可以快速地构建图表或查询其内容。", "kbn.management.indexPatternPrompt.exampleOneTitle": "单数据源", @@ -1556,9 +1555,9 @@ "kbn.management.objects.objectsTable.table.typeFilterName": "类型", "kbn.management.objects.objectsTable.unableFindSavedObjectsNotificationMessage": "找不到已保存对象", "kbn.management.objects.parsingFieldErrorMessage": "为索引模式 “{indexName}” 解析 “{fieldName}” 时发生错误:{errorMessage}", - "kbn.management.objects.savedObjectsDescription": "导入、导出和管理您的已保存搜索、可视化和仪表板。", + "management.objects.savedObjectsDescription": "导入、导出和管理您的已保存搜索、可视化和仪表板。", "kbn.management.objects.savedObjectsSectionLabel": "已保存对象", - "kbn.management.objects.savedObjectsTitle": "已保存对象", + "management.objects.savedObjectsTitle": "已保存对象", "kbn.management.objects.view.cancelButtonAriaLabel": "取消", "kbn.management.objects.view.cancelButtonLabel": "取消", "kbn.management.objects.view.deleteItemButtonLabel": "删除“{title}”", @@ -1576,8 +1575,8 @@ "kbn.management.objects.view.viewItemTitle": "查看“{title}”", "kbn.management.savedObjects.editBreadcrumb": "编辑 {savedObjectType}", "kbn.management.savedObjects.indexBreadcrumb": "已保存对象", - "kbn.management.settings.advancedSettingsDescription": "直接编辑在 Kibana 中控制行为的设置。", - "kbn.management.settings.advancedSettingsLabel": "高级设置", + "advancedSettings.advancedSettingsDescription": "直接编辑在 Kibana 中控制行为的设置。", + "advancedSettings.advancedSettingsLabel": "高级设置", "kbn.management.settings.breadcrumb": "高级设置", "kbn.management.settings.callOutCautionDescription": "此处请谨慎操作,这些设置仅供高级用户使用。您在这里所做的更改可能使 Kibana 的大部分功能出现问题。这些设置有一部分可能未在文档中说明、不受支持或是实验性设置。如果字段有默认值,将字段留空会将其设置为默认值,其他配置指令可能不接受其默认值。删除定制设置会将其从 Kibana 的配置中永久删除。", "kbn.management.settings.callOutCautionTitle": "注意:在这里您可能会使问题出现", @@ -2792,7 +2791,6 @@ "timelion.noFunctionErrorMessage": "没有此类函数:{name}", "timelion.panels.noRenderFunctionErrorMessage": "面板必须具有渲染函数", "timelion.panels.timechart.unknownIntervalErrorMessage": "时间间隔未知", - "timelion.registerFeatureDescription": "使用表达式语言分析时间序列数据,并将结果可视化。", "timelion.requestHandlerErrorTitle": "Timelion 请求错误", "timelion.savedObjects.howToSaveAsNewDescription": "在 Kibana 的以前版本中,更改 {savedObjectName} 的名称将创建具有新名称的副本。使用“另存为新的 {savedObjectName}” 复选框可立即达到此目的。", "timelion.savedObjects.saveAsNewLabel": "另存为新的 {savedObjectName}", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/context/alerts_context.tsx b/x-pack/plugins/triggers_actions_ui/public/application/context/alerts_context.tsx index 9b6b4a2cf1f22..04090d2c6428d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/context/alerts_context.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/context/alerts_context.tsx @@ -7,7 +7,7 @@ import React, { useContext, createContext } from 'react'; import { HttpSetup, IUiSettingsClient, ToastsApi } from 'kibana/public'; import { ChartsPluginSetup } from 'src/plugins/charts/public'; -import { FieldFormatsRegistry } from 'src/plugins/data/common/field_formats/static'; +import { DataPublicPluginSetup } from 'src/plugins/data/public'; import { TypeRegistry } from '../type_registry'; import { AlertTypeModel, ActionTypeModel } from '../../types'; @@ -24,7 +24,7 @@ export interface AlertsContextValue { 'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError' >; charts?: ChartsPluginSetup; - dataFieldsFormats?: Pick; + dataFieldsFormats?: DataPublicPluginSetup['fieldFormats']; } const AlertsContext = createContext(null as any); diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts index 6c2a22f2737fe..f7f3d0fa91fff 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import { times } from 'lodash'; import { schema } from '@kbn/config-schema'; import { AlertExecutorOptions, AlertType } from '../../../../../../legacy/plugins/alerting'; import { ActionTypeExecutorOptions, ActionType } from '../../../../../../plugins/actions/server'; @@ -249,6 +249,29 @@ export default function(kibana: any) { }; }, }; + // Alert types + const cumulativeFiringAlertType: AlertType = { + id: 'test.cumulative-firing', + name: 'Test: Cumulative Firing', + actionGroups: ['default', 'other'], + async executor(alertExecutorOptions: AlertExecutorOptions) { + const { services, state } = alertExecutorOptions; + const group = 'default'; + + const runCount = (state.runCount || 0) + 1; + + times(runCount, index => { + services + .alertInstanceFactory(`instance-${index}`) + .replaceState({ instanceStateValue: true }) + .scheduleActions(group); + }); + + return { + runCount, + }; + }, + }; const neverFiringAlertType: AlertType = { id: 'test.never-firing', name: 'Test: Never firing', @@ -364,6 +387,7 @@ export default function(kibana: any) { async executor({ services, params, state }: AlertExecutorOptions) {}, }; server.plugins.alerting.setup.registerType(alwaysFiringAlertType); + server.plugins.alerting.setup.registerType(cumulativeFiringAlertType); server.plugins.alerting.setup.registerType(neverFiringAlertType); server.plugins.alerting.setup.registerType(failingAlertType); server.plugins.alerting.setup.registerType(validationAlertType); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_state.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_state.ts new file mode 100644 index 0000000000000..d95f9ea8ac0ea --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_state.ts @@ -0,0 +1,126 @@ +/* + * 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 expect from '@kbn/expect'; +import { getUrlPrefix, ObjectRemover, getTestAlertData } from '../../../common/lib'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { UserAtSpaceScenarios } from '../../scenarios'; + +// eslint-disable-next-line import/no-default-export +export default function createGetAlertStateTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('getAlertState', () => { + const objectRemover = new ObjectRemover(supertest); + + afterEach(() => objectRemover.removeAll()); + + for (const scenario of UserAtSpaceScenarios) { + const { user, space } = scenario; + describe(scenario.id, () => { + it('should handle getAlertState alert request appropriately', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alert`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData()) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'alert'); + + const response = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}/state`) + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(404); + expect(response.body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: 'Not Found', + }); + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + expect(response.statusCode).to.eql(200); + expect(response.body).to.key('alertInstances', 'previousStartedAt'); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`shouldn't getAlertState for an alert from another space`, async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alert`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData()) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'alert'); + + const response = await supertestWithoutAuth + .get(`${getUrlPrefix('other')}/api/alert/${createdAlert.id}/state`) + .auth(user.username, user.password); + + expect(response.statusCode).to.eql(404); + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'space_1_all at space1': + expect(response.body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: 'Not Found', + }); + break; + case 'global_read at space1': + case 'superuser at space1': + expect(response.body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: `Saved object [alert/${createdAlert.id}] not found`, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`should handle getAlertState request appropriately when alert doesn't exist`, async () => { + const response = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/api/alert/1/state`) + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(404); + expect(response.body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: 'Not Found', + }); + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + expect(response.statusCode).to.eql(404); + expect(response.body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: 'Saved object [alert/1] not found', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + }); + } + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts index 1aa084356cfa4..91b0ca0a37c92 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts @@ -15,6 +15,7 @@ export default function alertingTests({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./enable')); loadTestFile(require.resolve('./find')); loadTestFile(require.resolve('./get')); + loadTestFile(require.resolve('./get_alert_state')); loadTestFile(require.resolve('./list_alert_types')); loadTestFile(require.resolve('./mute_all')); loadTestFile(require.resolve('./mute_instance')); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_state.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_state.ts new file mode 100644 index 0000000000000..053df3b7199cc --- /dev/null +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_state.ts @@ -0,0 +1,89 @@ +/* + * 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 expect from '@kbn/expect'; +import { Spaces } from '../../scenarios'; +import { getUrlPrefix, ObjectRemover, getTestAlertData } from '../../../common/lib'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function createGetAlertStateTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const retry = getService('retry'); + + describe('getAlertState', () => { + const objectRemover = new ObjectRemover(supertest); + + afterEach(() => objectRemover.removeAll()); + + it('should handle getAlertState request appropriately', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData()) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert'); + + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/api/alert/${createdAlert.id}/state` + ); + + expect(response.statusCode).to.eql(200); + expect(response.body).to.key('alertInstances', 'previousStartedAt'); + }); + + it('should fetch updated state', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alert`) + .set('kbn-xsrf', 'foo') + .send({ + enabled: true, + name: 'abc', + tags: ['foo'], + alertTypeId: 'test.cumulative-firing', + consumer: 'bar', + schedule: { interval: '5s' }, + throttle: '5s', + actions: [], + params: {}, + }) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert'); + + // wait for alert to actually execute + await retry.try(async () => { + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/api/alert/${createdAlert.id}/state` + ); + + expect(response.statusCode).to.eql(200); + expect(response.body).to.key('alertInstances', 'alertTypeState', 'previousStartedAt'); + expect(response.body.alertTypeState.runCount).to.greaterThan(1); + }); + + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/api/alert/${createdAlert.id}/state` + ); + + expect(response.body.alertTypeState.runCount).to.greaterThan(0); + + const alertInstances = Object.entries>(response.body.alertInstances); + expect(alertInstances.length).to.eql(response.body.alertTypeState.runCount); + alertInstances.forEach(([key, value], index) => { + expect(key).to.eql(`instance-${index}`); + expect(value.state).to.eql({ instanceStateValue: true }); + }); + }); + + it(`should handle getAlertState request appropriately when alert doesn't exist`, async () => { + await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/alert/1/state`).expect(404, { + statusCode: 404, + error: 'Not Found', + message: 'Saved object [alert/1] not found', + }); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts index 569c0d538d473..0b7f51ac9a79b 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts @@ -15,6 +15,7 @@ export default function alertingTests({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./enable')); loadTestFile(require.resolve('./find')); loadTestFile(require.resolve('./get')); + loadTestFile(require.resolve('./get_alert_state')); loadTestFile(require.resolve('./list_alert_types')); loadTestFile(require.resolve('./mute_all')); loadTestFile(require.resolve('./mute_instance')); diff --git a/x-pack/test/functional/apps/endpoint/index.ts b/x-pack/test/functional/apps/endpoint/index.ts index 1a0d3e973285b..e44a4cb846f2c 100644 --- a/x-pack/test/functional/apps/endpoint/index.ts +++ b/x-pack/test/functional/apps/endpoint/index.ts @@ -10,5 +10,6 @@ export default function({ loadTestFile }: FtrProviderContext) { this.tags('ciGroup7'); loadTestFile(require.resolve('./feature_controls')); + loadTestFile(require.resolve('./landing_page')); }); } diff --git a/x-pack/test/functional/apps/endpoint/landing_page.ts b/x-pack/test/functional/apps/endpoint/landing_page.ts new file mode 100644 index 0000000000000..65af91feae407 --- /dev/null +++ b/x-pack/test/functional/apps/endpoint/landing_page.ts @@ -0,0 +1,24 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default ({ getPageObjects }: FtrProviderContext) => { + const pageObjects = getPageObjects(['common', 'endpoint']); + + describe('Endpoint landing page', function() { + this.tags('ciGroup7'); + before(async () => { + await pageObjects.common.navigateToApp('endpoint'); + }); + + it('Loads the endpoint app', async () => { + const welcomeEndpointMessage = await pageObjects.endpoint.welcomeEndpointTitle(); + expect(welcomeEndpointMessage).to.be('Hello World'); + }); + }); +}; diff --git a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/classification_creation.ts b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/classification_creation.ts new file mode 100644 index 0000000000000..1b8c8299a8ac6 --- /dev/null +++ b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/classification_creation.ts @@ -0,0 +1,174 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + + describe('classification creation', function() { + this.tags(['smoke']); + before(async () => { + await esArchiver.load('ml/bm_classification'); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + await esArchiver.unload('ml/bm_classification'); + }); + + const testDataList = [ + { + suiteTitle: 'bank marketing', + jobType: 'classification', + jobId: `bm_1_${Date.now()}`, + jobDescription: + "Classification job based on 'bank-marketing' dataset with dependentVariable 'y' and trainingPercent '20'", + source: 'bank-marketing*', + get destinationIndex(): string { + return `dest_${this.jobId}`; + }, + dependentVariable: 'y', + trainingPercent: '20', + modelMemory: '105mb', + createIndexPattern: true, + expected: { + row: { + type: 'classification', + status: 'stopped', + progress: '100', + }, + }, + }, + ]; + for (const testData of testDataList) { + describe(`${testData.suiteTitle}`, function() { + after(async () => { + await ml.api.deleteIndices(testData.destinationIndex); + }); + + it('loads the data frame analytics page', async () => { + await ml.navigation.navigateToMl(); + await ml.navigation.navigateToDataFrameAnalytics(); + }); + + it('loads the job creation flyout', async () => { + await ml.dataFrameAnalytics.startAnalyticsCreation(); + }); + + it('selects the job type', async () => { + await ml.dataFrameAnalyticsCreation.assertJobTypeSelectExists(); + await ml.dataFrameAnalyticsCreation.selectJobType(testData.jobType); + }); + + it('inputs the job id', async () => { + await ml.dataFrameAnalyticsCreation.assertJobIdInputExists(); + await ml.dataFrameAnalyticsCreation.setJobId(testData.jobId); + }); + + it('inputs the job description', async () => { + await ml.dataFrameAnalyticsCreation.assertJobDescriptionInputExists(); + await ml.dataFrameAnalyticsCreation.setJobDescription(testData.jobDescription); + }); + + it('selects the source index', async () => { + await ml.dataFrameAnalyticsCreation.assertSourceIndexInputExists(); + await ml.dataFrameAnalyticsCreation.selectSourceIndex(testData.source); + }); + + it('inputs the destination index', async () => { + await ml.dataFrameAnalyticsCreation.assertDestIndexInputExists(); + await ml.dataFrameAnalyticsCreation.setDestIndex(testData.destinationIndex); + }); + + it('inputs the dependent variable', async () => { + await ml.dataFrameAnalyticsCreation.assertDependentVariableInputExists(); + await ml.dataFrameAnalyticsCreation.selectDependentVariable(testData.dependentVariable); + }); + + it('inputs the training percent', async () => { + await ml.dataFrameAnalyticsCreation.assertTrainingPercentInputExists(); + await ml.dataFrameAnalyticsCreation.setTrainingPercent(testData.trainingPercent); + }); + + it('inputs the model memory limit', async () => { + await ml.dataFrameAnalyticsCreation.assertModelMemoryInputExists(); + await ml.dataFrameAnalyticsCreation.setModelMemory(testData.modelMemory); + }); + + it('sets the create index pattern switch', async () => { + await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists(); + await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState( + testData.createIndexPattern + ); + }); + + it('creates the analytics job', async () => { + await ml.dataFrameAnalyticsCreation.assertCreateButtonExists(); + await ml.dataFrameAnalyticsCreation.createAnalyticsJob(); + }); + + it('starts the analytics job', async () => { + await ml.dataFrameAnalyticsCreation.assertStartButtonExists(); + await ml.dataFrameAnalyticsCreation.startAnalyticsJob(); + }); + + it('closes the create job flyout', async () => { + await ml.dataFrameAnalyticsCreation.assertCloseButtonExists(); + await ml.dataFrameAnalyticsCreation.closeCreateAnalyticsJobFlyout(); + }); + + it('finishes analytics processing', async () => { + await ml.dataFrameAnalytics.waitForAnalyticsCompletion(testData.jobId); + }); + + it('displays the analytics table', async () => { + await ml.dataFrameAnalytics.assertAnalyticsTableExists(); + }); + + it('displays the stats bar', async () => { + await ml.dataFrameAnalytics.assertAnalyticsStatsBarExists(); + }); + + it('displays the created job in the analytics table', async () => { + await ml.dataFrameAnalyticsTable.refreshAnalyticsTable(); + await ml.dataFrameAnalyticsTable.filterWithSearchString(testData.jobId); + const rows = await ml.dataFrameAnalyticsTable.parseAnalyticsTable(); + const filteredRows = rows.filter(row => row.id === testData.jobId); + expect(filteredRows).to.have.length( + 1, + `Filtered analytics table should have 1 row for job id '${testData.jobId}' (got matching items '${filteredRows}')` + ); + }); + + it('displays details for the created job in the analytics table', async () => { + await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, { + id: testData.jobId, + description: testData.jobDescription, + sourceIndex: testData.source, + destinationIndex: testData.destinationIndex, + type: testData.expected.row.type, + status: testData.expected.row.status, + progress: testData.expected.row.progress, + }); + }); + + it('creates the destination index and writes results to it', async () => { + await ml.api.assertIndicesExist(testData.destinationIndex); + await ml.api.assertIndicesNotEmpty(testData.destinationIndex); + }); + + it('displays the results view for created job', async () => { + await ml.dataFrameAnalyticsTable.openResultsView(); + await ml.dataFrameAnalytics.assertClassificationEvaluatePanelElementsExists(); + await ml.dataFrameAnalytics.assertClassificationTablePanelExists(); + }); + }); + } + }); +} diff --git a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/index.ts b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/index.ts index dd8de77e6d5d0..fda0c5d203f2e 100644 --- a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/index.ts +++ b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/index.ts @@ -11,5 +11,6 @@ export default function({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./outlier_detection_creation')); loadTestFile(require.resolve('./regression_creation')); + loadTestFile(require.resolve('./classification_creation')); }); } diff --git a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/regression_creation.ts b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/regression_creation.ts index 1a514f4ad44e5..6a01afe6183ed 100644 --- a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/regression_creation.ts +++ b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/regression_creation.ts @@ -11,7 +11,7 @@ export default function({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const ml = getService('ml'); - describe('outlier detection creation', function() { + describe('regression creation', function() { this.tags(['smoke']); before(async () => { await esArchiver.load('ml/egs_regression'); diff --git a/x-pack/test/functional/apps/security/security.js b/x-pack/test/functional/apps/security/security.ts similarity index 54% rename from x-pack/test/functional/apps/security/security.js rename to x-pack/test/functional/apps/security/security.ts index 37b01ff61f5af..2096a7755e01d 100644 --- a/x-pack/test/functional/apps/security/security.js +++ b/x-pack/test/functional/apps/security/security.ts @@ -5,11 +5,15 @@ */ import expect from '@kbn/expect'; +import { parse } from 'url'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function({ getService, getPageObjects }) { +export default function({ getService, getPageObjects }: FtrProviderContext) { + const browser = getService('browser'); const esArchiver = getService('esArchiver'); - const PageObjects = getPageObjects(['security']); + const PageObjects = getPageObjects(['security', 'spaceSelector']); const testSubjects = getService('testSubjects'); + const spaces = getService('spaces'); describe('Security', function() { this.tags('smoke'); @@ -46,6 +50,37 @@ export default function({ getService, getPageObjects }) { const logoutMessage = await testSubjects.getVisibleText('loginInfoMessage'); expect(logoutMessage).to.eql('You have logged out of Kibana.'); }); + + describe('within a non-default space', async () => { + before(async () => { + await PageObjects.security.forceLogout(); + + await spaces.create({ + id: 'some-space', + name: 'Some non-default space', + disabledFeatures: [], + }); + }); + + after(async () => { + await spaces.delete('some-space'); + }); + + it('logging out of a non-default space redirects to the login page at the server root', async () => { + await PageObjects.security.login(null, null, { + expectSpaceSelector: true, + }); + + await PageObjects.spaceSelector.clickSpaceCard('some-space'); + await PageObjects.spaceSelector.expectHomePage('some-space'); + + await PageObjects.security.logout(); + + const currentUrl = await browser.getCurrentUrl(); + const url = parse(currentUrl); + expect(url.pathname).to.eql('/login'); + }); + }); }); }); } diff --git a/x-pack/test/functional/apps/spaces/feature_controls/spaces_security.ts b/x-pack/test/functional/apps/spaces/feature_controls/spaces_security.ts index d71d197a6ea19..9ca314ba5ec18 100644 --- a/x-pack/test/functional/apps/spaces/feature_controls/spaces_security.ts +++ b/x-pack/test/functional/apps/spaces/feature_controls/spaces_security.ts @@ -66,7 +66,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { }); it(`can navigate to spaces grid page`, async () => { - await PageObjects.common.navigateToActualUrl('kibana', 'management/spaces/list', { + await PageObjects.common.navigateToActualUrl('kibana', 'management/kibana/spaces', { ensureCurrentUrl: false, shouldLoginIfPrompted: false, }); @@ -75,7 +75,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { }); it(`can navigate to create new space page`, async () => { - await PageObjects.common.navigateToActualUrl('kibana', 'management/spaces/create', { + await PageObjects.common.navigateToActualUrl('kibana', 'management/kibana/spaces/create', { ensureCurrentUrl: false, shouldLoginIfPrompted: false, }); @@ -84,10 +84,14 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { }); it(`can navigate to edit space page`, async () => { - await PageObjects.common.navigateToActualUrl('kibana', 'management/spaces/edit/default', { - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - }); + await PageObjects.common.navigateToActualUrl( + 'kibana', + 'management/kibana/spaces/edit/default', + { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + } + ); await testSubjects.existOrFail('spaces-edit-page'); }); @@ -136,35 +140,39 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it(`doesn't display Spaces management section`, async () => { await PageObjects.settings.navigateTo(); - await testSubjects.existOrFail('objects'); // this ensures we've gotten to the management page + await testSubjects.existOrFail('management-landing'); // this ensures we've gotten to the management page await testSubjects.missingOrFail('spaces'); }); it(`can't navigate to spaces grid page`, async () => { - await PageObjects.common.navigateToActualUrl('kibana', 'management/spaces/list', { + await PageObjects.common.navigateToActualUrl('kibana', 'management/kibana/spaces', { ensureCurrentUrl: false, shouldLoginIfPrompted: false, }); - await testSubjects.existOrFail('homeApp'); + await testSubjects.existOrFail('management-landing'); }); it(`can't navigate to create new space page`, async () => { - await PageObjects.common.navigateToActualUrl('kibana', 'management/spaces/create', { + await PageObjects.common.navigateToActualUrl('kibana', 'management/kibana/spaces/create', { ensureCurrentUrl: false, shouldLoginIfPrompted: false, }); - await testSubjects.existOrFail('homeApp'); + await testSubjects.existOrFail('management-landing'); }); it(`can't navigate to edit space page`, async () => { - await PageObjects.common.navigateToActualUrl('kibana', 'management/spaces/edit/default', { - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - }); + await PageObjects.common.navigateToActualUrl( + 'kibana', + 'management/kibana/spaces/edit/default', + { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + } + ); - await testSubjects.existOrFail('homeApp'); + await testSubjects.existOrFail('management-landing'); }); }); }); diff --git a/x-pack/test/functional/es_archives/ml/bm_classification/data.json.gz b/x-pack/test/functional/es_archives/ml/bm_classification/data.json.gz new file mode 100644 index 0000000000000..12ccf6ae60512 Binary files /dev/null and b/x-pack/test/functional/es_archives/ml/bm_classification/data.json.gz differ diff --git a/x-pack/test/functional/es_archives/ml/bm_classification/mappings.json b/x-pack/test/functional/es_archives/ml/bm_classification/mappings.json new file mode 100644 index 0000000000000..9d2cca22bf300 --- /dev/null +++ b/x-pack/test/functional/es_archives/ml/bm_classification/mappings.json @@ -0,0 +1,1548 @@ +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "bank-marketing", + "mappings": { + "properties": { + "age": { + "type": "integer" + }, + "balance": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "campaign": { + "type": "integer" + }, + "cons_conf_idx": { + "type": "float" + }, + "contact": { + "type": "keyword" + }, + "day": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "day_of_week": { + "type": "keyword" + }, + "default": { + "type": "keyword" + }, + "duration": { + "type": "integer" + }, + "education": { + "type": "keyword" + }, + "emp_var_rate": { + "type": "float" + }, + "euribor3m": { + "type": "float" + }, + "housing": { + "type": "keyword" + }, + "job": { + "type": "keyword" + }, + "loan": { + "type": "keyword" + }, + "marital": { + "type": "keyword" + }, + "month": { + "type": "keyword" + }, + "nr_employed": { + "type": "integer" + }, + "pdays": { + "type": "integer" + }, + "poutcome": { + "type": "keyword" + }, + "previous": { + "type": "integer" + }, + "y": { + "type": "keyword" + } + } + }, + "settings": { + "index": { + "number_of_replicas": "1", + "number_of_shards": "1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + ".kibana": { + } + }, + "index": ".kibana_1", + "mappings": { + "_meta": { + "migrationMappingPropertyHashes": { + "action": "c0c235fba02ebd2a2412bcda79009b58", + "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", + "alert": "e588043a01d3d43477e7cad7efa0f5d8", + "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", + "apm-services-telemetry": "07ee1939fa4302c62ddc052ec03fed90", + "canvas-element": "7390014e1091044523666d97247392fc", + "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", + "config": "87aca8fdb053154f11383fce3dbf3edf", + "dashboard": "d00f614b29a80360e1190193fd333bab", + "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", + "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", + "index-pattern": "66eccb05066c5a89924f48a9e9736499", + "infrastructure-ui-source": "ddc0ecb18383f6b26101a2fadb2dab0c", + "inventory-view": "84b320fd67209906333ffce261128462", + "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", + "lens": "21c3ea0763beb1ecb0162529706b88c5", + "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", + "map": "23d7aa4a720d4938ccde3983f87bd58d", + "maps-telemetry": "268da3a48066123fc5baf35abaa55014", + "metrics-explorer-view": "53c5365793677328df0ccb6138bf3cdd", + "migrationVersion": "4a1746014a75ade3a714e1db5763276f", + "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", + "namespace": "2f4316de49999235636386fe51dc06c1", + "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", + "references": "7997cf5a56cc02bdc9c93361bde732b0", + "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", + "search": "181661168bbadd1eff5902361e2a0d5c", + "server": "ec97f1c5da1a19609a60874e5af1100c", + "siem-detection-engine-rule-status": "0367e4d775814b56a4bee29384f9aafe", + "siem-ui-timeline": "ac8020190f5950dd3250b6499144e7fb", + "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", + "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", + "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", + "telemetry": "358ffaa88ba34a97d55af0933a117de4", + "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", + "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", + "type": "2f4316de49999235636386fe51dc06c1", + "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0", + "upgrade-assistant-reindex-operation": "a53a20fe086b72c9a86da3cc12dad8a6", + "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", + "url": "c7f66a0df8b1b52f17c28c4adb111105", + "visualization": "52d7a13ad68a150c4525b292d23e12cc" + } + }, + "dynamic": "strict", + "properties": { + "action": { + "properties": { + "actionTypeId": { + "type": "keyword" + }, + "config": { + "enabled": false, + "type": "object" + }, + "name": { + "type": "text" + }, + "secrets": { + "type": "binary" + } + } + }, + "action_task_params": { + "properties": { + "actionId": { + "type": "keyword" + }, + "apiKey": { + "type": "binary" + }, + "params": { + "enabled": false, + "type": "object" + } + } + }, + "alert": { + "properties": { + "actions": { + "properties": { + "actionRef": { + "type": "keyword" + }, + "actionTypeId": { + "type": "keyword" + }, + "group": { + "type": "keyword" + }, + "params": { + "enabled": false, + "type": "object" + } + }, + "type": "nested" + }, + "alertTypeId": { + "type": "keyword" + }, + "apiKey": { + "type": "binary" + }, + "apiKeyOwner": { + "type": "keyword" + }, + "consumer": { + "type": "keyword" + }, + "createdAt": { + "type": "date" + }, + "createdBy": { + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "muteAll": { + "type": "boolean" + }, + "mutedInstanceIds": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "params": { + "enabled": false, + "type": "object" + }, + "schedule": { + "properties": { + "interval": { + "type": "keyword" + } + } + }, + "scheduledTaskId": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "throttle": { + "type": "keyword" + }, + "updatedBy": { + "type": "keyword" + } + } + }, + "apm-indices": { + "properties": { + "apm_oss": { + "properties": { + "errorIndices": { + "type": "keyword" + }, + "metricsIndices": { + "type": "keyword" + }, + "onboardingIndices": { + "type": "keyword" + }, + "sourcemapIndices": { + "type": "keyword" + }, + "spanIndices": { + "type": "keyword" + }, + "transactionIndices": { + "type": "keyword" + } + } + } + } + }, + "apm-services-telemetry": { + "properties": { + "has_any_services": { + "type": "boolean" + }, + "services_per_agent": { + "properties": { + "dotnet": { + "null_value": 0, + "type": "long" + }, + "go": { + "null_value": 0, + "type": "long" + }, + "java": { + "null_value": 0, + "type": "long" + }, + "js-base": { + "null_value": 0, + "type": "long" + }, + "nodejs": { + "null_value": 0, + "type": "long" + }, + "python": { + "null_value": 0, + "type": "long" + }, + "ruby": { + "null_value": 0, + "type": "long" + }, + "rum-js": { + "null_value": 0, + "type": "long" + } + } + } + } + }, + "canvas-element": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "content": { + "type": "text" + }, + "help": { + "type": "text" + }, + "image": { + "type": "text" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "canvas-workpad": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "config": { + "dynamic": "true", + "properties": { + "buildNum": { + "type": "keyword" + }, + "dateFormat:tz": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "defaultIndex": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "dashboard": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "optionsJSON": { + "type": "text" + }, + "panelsJSON": { + "type": "text" + }, + "refreshInterval": { + "properties": { + "display": { + "type": "keyword" + }, + "pause": { + "type": "boolean" + }, + "section": { + "type": "integer" + }, + "value": { + "type": "integer" + } + } + }, + "timeFrom": { + "type": "keyword" + }, + "timeRestore": { + "type": "boolean" + }, + "timeTo": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "file-upload-telemetry": { + "properties": { + "filesUploadedTotalCount": { + "type": "long" + } + } + }, + "graph-workspace": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "numLinks": { + "type": "integer" + }, + "numVertices": { + "type": "integer" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "wsState": { + "type": "text" + } + } + }, + "index-pattern": { + "properties": { + "fieldFormatMap": { + "type": "text" + }, + "fields": { + "type": "text" + }, + "intervalName": { + "type": "keyword" + }, + "notExpandable": { + "type": "boolean" + }, + "sourceFilters": { + "type": "text" + }, + "timeFieldName": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "type": { + "type": "keyword" + }, + "typeMeta": { + "type": "keyword" + } + } + }, + "infrastructure-ui-source": { + "properties": { + "description": { + "type": "text" + }, + "fields": { + "properties": { + "container": { + "type": "keyword" + }, + "host": { + "type": "keyword" + }, + "pod": { + "type": "keyword" + }, + "tiebreaker": { + "type": "keyword" + }, + "timestamp": { + "type": "keyword" + } + } + }, + "logAlias": { + "type": "keyword" + }, + "logColumns": { + "properties": { + "fieldColumn": { + "properties": { + "field": { + "type": "keyword" + }, + "id": { + "type": "keyword" + } + } + }, + "messageColumn": { + "properties": { + "id": { + "type": "keyword" + } + } + }, + "timestampColumn": { + "properties": { + "id": { + "type": "keyword" + } + } + } + }, + "type": "nested" + }, + "metricAlias": { + "type": "keyword" + }, + "name": { + "type": "text" + } + } + }, + "inventory-view": { + "properties": { + "autoBounds": { + "type": "boolean" + }, + "autoReload": { + "type": "boolean" + }, + "boundsOverride": { + "properties": { + "max": { + "type": "integer" + }, + "min": { + "type": "integer" + } + } + }, + "customOptions": { + "properties": { + "field": { + "type": "keyword" + }, + "text": { + "type": "keyword" + } + }, + "type": "nested" + }, + "filterQuery": { + "properties": { + "expression": { + "type": "keyword" + }, + "kind": { + "type": "keyword" + } + } + }, + "groupBy": { + "properties": { + "field": { + "type": "keyword" + }, + "label": { + "type": "keyword" + } + }, + "type": "nested" + }, + "metric": { + "properties": { + "type": { + "type": "keyword" + } + } + }, + "name": { + "type": "keyword" + }, + "nodeType": { + "type": "keyword" + }, + "time": { + "type": "integer" + }, + "view": { + "type": "keyword" + } + } + }, + "kql-telemetry": { + "properties": { + "optInCount": { + "type": "long" + }, + "optOutCount": { + "type": "long" + } + } + }, + "lens": { + "properties": { + "expression": { + "index": false, + "type": "keyword" + }, + "state": { + "type": "flattened" + }, + "title": { + "type": "text" + }, + "visualizationType": { + "type": "keyword" + } + } + }, + "lens-ui-telemetry": { + "properties": { + "count": { + "type": "integer" + }, + "date": { + "type": "date" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "map": { + "properties": { + "bounds": { + "type": "geo_shape" + }, + "description": { + "type": "text" + }, + "layerListJSON": { + "type": "text" + }, + "mapStateJSON": { + "type": "text" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "maps-telemetry": { + "properties": { + "attributesPerMap": { + "properties": { + "dataSourcesCount": { + "properties": { + "avg": { + "type": "long" + }, + "max": { + "type": "long" + }, + "min": { + "type": "long" + } + } + }, + "emsVectorLayersCount": { + "dynamic": "true", + "type": "object" + }, + "layerTypesCount": { + "dynamic": "true", + "type": "object" + }, + "layersCount": { + "properties": { + "avg": { + "type": "long" + }, + "max": { + "type": "long" + }, + "min": { + "type": "long" + } + } + } + } + }, + "indexPatternsWithGeoFieldCount": { + "type": "long" + }, + "mapsTotalCount": { + "type": "long" + }, + "settings": { + "properties": { + "showMapVisualizationTypes": { + "type": "boolean" + } + } + }, + "timeCaptured": { + "type": "date" + } + } + }, + "metrics-explorer-view": { + "properties": { + "chartOptions": { + "properties": { + "stack": { + "type": "boolean" + }, + "type": { + "type": "keyword" + }, + "yAxisMode": { + "type": "keyword" + } + } + }, + "currentTimerange": { + "properties": { + "from": { + "type": "keyword" + }, + "interval": { + "type": "keyword" + }, + "to": { + "type": "keyword" + } + } + }, + "name": { + "type": "keyword" + }, + "options": { + "properties": { + "aggregation": { + "type": "keyword" + }, + "filterQuery": { + "type": "keyword" + }, + "groupBy": { + "type": "keyword" + }, + "limit": { + "type": "integer" + }, + "metrics": { + "properties": { + "aggregation": { + "type": "keyword" + }, + "color": { + "type": "keyword" + }, + "field": { + "type": "keyword" + }, + "label": { + "type": "keyword" + } + }, + "type": "nested" + } + } + } + } + }, + "migrationVersion": { + "dynamic": "true", + "properties": { + "index-pattern": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "space": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "ml-telemetry": { + "properties": { + "file_data_visualizer": { + "properties": { + "index_creation_count": { + "type": "long" + } + } + } + } + }, + "namespace": { + "type": "keyword" + }, + "query": { + "properties": { + "description": { + "type": "text" + }, + "filters": { + "enabled": false, + "type": "object" + }, + "query": { + "properties": { + "language": { + "type": "keyword" + }, + "query": { + "index": false, + "type": "keyword" + } + } + }, + "timefilter": { + "enabled": false, + "type": "object" + }, + "title": { + "type": "text" + } + } + }, + "references": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "sample-data-telemetry": { + "properties": { + "installCount": { + "type": "long" + }, + "unInstallCount": { + "type": "long" + } + } + }, + "search": { + "properties": { + "columns": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "sort": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "server": { + "properties": { + "uuid": { + "type": "keyword" + } + } + }, + "siem-detection-engine-rule-status": { + "properties": { + "alertId": { + "type": "keyword" + }, + "lastFailureAt": { + "type": "date" + }, + "lastFailureMessage": { + "type": "text" + }, + "lastSuccessAt": { + "type": "date" + }, + "lastSuccessMessage": { + "type": "text" + }, + "status": { + "type": "keyword" + }, + "statusDate": { + "type": "date" + } + } + }, + "siem-ui-timeline": { + "properties": { + "columns": { + "properties": { + "aggregatable": { + "type": "boolean" + }, + "category": { + "type": "keyword" + }, + "columnHeaderType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "example": { + "type": "text" + }, + "id": { + "type": "keyword" + }, + "indexes": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "placeholder": { + "type": "text" + }, + "searchable": { + "type": "boolean" + }, + "type": { + "type": "keyword" + } + } + }, + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "dataProviders": { + "properties": { + "and": { + "properties": { + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + } + } + }, + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + } + } + }, + "dateRange": { + "properties": { + "end": { + "type": "date" + }, + "start": { + "type": "date" + } + } + }, + "description": { + "type": "text" + }, + "eventType": { + "type": "keyword" + }, + "favorite": { + "properties": { + "favoriteDate": { + "type": "date" + }, + "fullName": { + "type": "text" + }, + "keySearch": { + "type": "text" + }, + "userName": { + "type": "text" + } + } + }, + "filters": { + "properties": { + "exists": { + "type": "text" + }, + "match_all": { + "type": "text" + }, + "meta": { + "properties": { + "alias": { + "type": "text" + }, + "controlledBy": { + "type": "text" + }, + "disabled": { + "type": "boolean" + }, + "field": { + "type": "text" + }, + "formattedValue": { + "type": "text" + }, + "index": { + "type": "keyword" + }, + "key": { + "type": "keyword" + }, + "negate": { + "type": "boolean" + }, + "params": { + "type": "text" + }, + "type": { + "type": "keyword" + }, + "value": { + "type": "text" + } + } + }, + "missing": { + "type": "text" + }, + "query": { + "type": "text" + }, + "range": { + "type": "text" + }, + "script": { + "type": "text" + } + } + }, + "kqlMode": { + "type": "keyword" + }, + "kqlQuery": { + "properties": { + "filterQuery": { + "properties": { + "kuery": { + "properties": { + "expression": { + "type": "text" + }, + "kind": { + "type": "keyword" + } + } + }, + "serializedQuery": { + "type": "text" + } + } + } + } + }, + "savedQueryId": { + "type": "keyword" + }, + "sort": { + "properties": { + "columnId": { + "type": "keyword" + }, + "sortDirection": { + "type": "keyword" + } + } + }, + "title": { + "type": "text" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-note": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "note": { + "type": "text" + }, + "timelineId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-pinned-event": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "timelineId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "space": { + "properties": { + "_reserved": { + "type": "boolean" + }, + "color": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "disabledFeatures": { + "type": "keyword" + }, + "imageUrl": { + "index": false, + "type": "text" + }, + "initials": { + "type": "keyword" + }, + "name": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "telemetry": { + "properties": { + "enabled": { + "type": "boolean" + }, + "lastReported": { + "type": "date" + }, + "lastVersionChecked": { + "ignore_above": 256, + "type": "keyword" + }, + "sendUsageFrom": { + "ignore_above": 256, + "type": "keyword" + }, + "userHasSeenNotice": { + "type": "boolean" + } + } + }, + "timelion-sheet": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "timelion_chart_height": { + "type": "integer" + }, + "timelion_columns": { + "type": "integer" + }, + "timelion_interval": { + "type": "keyword" + }, + "timelion_other_interval": { + "type": "keyword" + }, + "timelion_rows": { + "type": "integer" + }, + "timelion_sheet": { + "type": "text" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "tsvb-validation-telemetry": { + "properties": { + "failedRequests": { + "type": "long" + } + } + }, + "type": { + "type": "keyword" + }, + "ui-metric": { + "properties": { + "count": { + "type": "integer" + } + } + }, + "updated_at": { + "type": "date" + }, + "upgrade-assistant-reindex-operation": { + "dynamic": "true", + "properties": { + "indexName": { + "type": "keyword" + }, + "status": { + "type": "integer" + } + } + }, + "upgrade-assistant-telemetry": { + "properties": { + "features": { + "properties": { + "deprecation_logging": { + "properties": { + "enabled": { + "null_value": true, + "type": "boolean" + } + } + } + } + }, + "ui_open": { + "properties": { + "cluster": { + "null_value": 0, + "type": "long" + }, + "indices": { + "null_value": 0, + "type": "long" + }, + "overview": { + "null_value": 0, + "type": "long" + } + } + }, + "ui_reindex": { + "properties": { + "close": { + "null_value": 0, + "type": "long" + }, + "open": { + "null_value": 0, + "type": "long" + }, + "start": { + "null_value": 0, + "type": "long" + }, + "stop": { + "null_value": 0, + "type": "long" + } + } + } + } + }, + "url": { + "properties": { + "accessCount": { + "type": "long" + }, + "accessDate": { + "type": "date" + }, + "createDate": { + "type": "date" + }, + "url": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "visualization": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "savedSearchRefName": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "visState": { + "type": "text" + } + } + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/functional/page_objects/endpoint_page.ts b/x-pack/test/functional/page_objects/endpoint_page.ts new file mode 100644 index 0000000000000..f02a899f6d37d --- /dev/null +++ b/x-pack/test/functional/page_objects/endpoint_page.ts @@ -0,0 +1,17 @@ +/* + * 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 { FtrProviderContext } from '../ftr_provider_context'; + +export function EndpointPageProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + + return { + async welcomeEndpointTitle() { + return await testSubjects.getVisibleText('welcomeTitle'); + }, + }; +} diff --git a/x-pack/test/functional/page_objects/index.ts b/x-pack/test/functional/page_objects/index.ts index 91d4a3663fa65..9479f88085222 100644 --- a/x-pack/test/functional/page_objects/index.ts +++ b/x-pack/test/functional/page_objects/index.ts @@ -22,8 +22,6 @@ import { WatcherPageProvider } from './watcher_page'; // @ts-ignore not ts yet import { ReportingPageProvider } from './reporting_page'; // @ts-ignore not ts yet -import { SpaceSelectorPageProvider } from './space_selector_page'; -// @ts-ignore not ts yet import { AccountSettingProvider } from './accountsetting_page'; import { InfraHomePageProvider } from './infra_home_page'; import { InfraLogsPageProvider } from './infra_logs_page'; @@ -46,6 +44,8 @@ import { CopySavedObjectsToSpacePageProvider } from './copy_saved_objects_to_spa import { LensPageProvider } from './lens_page'; import { InfraMetricExplorerProvider } from './infra_metric_explorer'; import { RoleMappingsPageProvider } from './role_mappings_page'; +import { SpaceSelectorPageProvider } from './space_selector_page'; +import { EndpointPageProvider } from './endpoint_page'; // just like services, PageObjects are defined as a map of // names to Providers. Merge in Kibana's or pick specific ones @@ -78,4 +78,5 @@ export const pageObjects = { copySavedObjectsToSpace: CopySavedObjectsToSpacePageProvider, lens: LensPageProvider, roleMappings: RoleMappingsPageProvider, + endpoint: EndpointPageProvider, }; diff --git a/x-pack/test/functional/page_objects/space_selector_page.js b/x-pack/test/functional/page_objects/space_selector_page.ts similarity index 86% rename from x-pack/test/functional/page_objects/space_selector_page.js rename to x-pack/test/functional/page_objects/space_selector_page.ts index ad0f48bdd50bf..74f53a1cf551f 100644 --- a/x-pack/test/functional/page_objects/space_selector_page.js +++ b/x-pack/test/functional/page_objects/space_selector_page.ts @@ -5,8 +5,9 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../ftr_provider_context'; -export function SpaceSelectorPageProvider({ getService, getPageObjects }) { +export function SpaceSelectorPageProvider({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const log = getService('log'); const testSubjects = getService('testSubjects'); @@ -19,7 +20,7 @@ export function SpaceSelectorPageProvider({ getService, getPageObjects }) { log.debug('SpaceSelectorPage:initTests'); } - async clickSpaceCard(spaceId) { + async clickSpaceCard(spaceId: string) { return await retry.try(async () => { log.info(`SpaceSelectorPage:clickSpaceCard(${spaceId})`); await testSubjects.click(`space-card-${spaceId}`); @@ -27,11 +28,11 @@ export function SpaceSelectorPageProvider({ getService, getPageObjects }) { }); } - async expectHomePage(spaceId) { + async expectHomePage(spaceId: string) { return await this.expectRoute(spaceId, `/app/kibana#/home`); } - async expectRoute(spaceId, route) { + async expectRoute(spaceId: string, route: string) { return await retry.try(async () => { log.debug(`expectRoute(${spaceId}, ${route})`); await find.byCssSelector('[data-test-subj="kibanaChrome"] nav:not(.ng-hide) ', 20000); @@ -49,7 +50,7 @@ export function SpaceSelectorPageProvider({ getService, getPageObjects }) { return await testSubjects.click('spacesNavSelector'); } - async clickSpaceAvatar(spaceId) { + async clickSpaceAvatar(spaceId: string) { return await retry.try(async () => { log.info(`SpaceSelectorPage:clickSpaceAvatar(${spaceId})`); await testSubjects.click(`space-avatar-${spaceId}`); diff --git a/x-pack/test/functional/services/machine_learning/data_frame_analytics.ts b/x-pack/test/functional/services/machine_learning/data_frame_analytics.ts index 95a4341e8a8d0..4d36db88f68ab 100644 --- a/x-pack/test/functional/services/machine_learning/data_frame_analytics.ts +++ b/x-pack/test/functional/services/machine_learning/data_frame_analytics.ts @@ -44,6 +44,15 @@ export function MachineLearningDataFrameAnalyticsProvider( await testSubjects.existOrFail('mlDFAnalyticsRegressionExplorationTablePanel'); }, + async assertClassificationEvaluatePanelElementsExists() { + await testSubjects.existOrFail('mlDFAnalyticsClassificationExplorationEvaluatePanel'); + await testSubjects.existOrFail('mlDFAnalyticsClassificationExplorationConfusionMatrix'); + }, + + async assertClassificationTablePanelExists() { + await testSubjects.existOrFail('mlDFAnalyticsClassificationExplorationTablePanel'); + }, + async assertOutlierTablePanelExists() { await testSubjects.existOrFail('mlDFAnalyticsOutlierExplorationTablePanel'); }, diff --git a/x-pack/test/plugin_api_integration/plugins/task_manager/init_routes.js b/x-pack/test/plugin_api_integration/plugins/task_manager/init_routes.js index 9e818f050c929..785fbed341423 100644 --- a/x-pack/test/plugin_api_integration/plugins/task_manager/init_routes.js +++ b/x-pack/test/plugin_api_integration/plugins/task_manager/init_routes.js @@ -24,6 +24,14 @@ const taskManagerQuery = { }; export function initRoutes(server, taskManager, legacyTaskManager, taskTestingEvents) { + const callCluster = server.plugins.elasticsearch.getCluster('admin').callWithInternalUser; + + async function ensureIndexIsRefreshed() { + return await callCluster('indices.refresh', { + index: '.kibana_task_manager', + }); + } + server.route({ path: '/api/sample_tasks/schedule', method: 'POST', @@ -198,19 +206,8 @@ export function initRoutes(server, taskManager, legacyTaskManager, taskTestingEv method: 'GET', async handler(request) { try { - return taskManager.fetch({ - query: { - bool: { - must: [ - { - ids: { - values: [`task:${request.params.taskId}`], - }, - }, - ], - }, - }, - }); + await ensureIndexIsRefreshed(); + return await taskManager.get(request.params.taskId); } catch (err) { return err; } diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js index 7ec0e9b5efa5b..e8f976d5ae6e3 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js @@ -69,7 +69,7 @@ export default function({ getService }) { .get(`/api/sample_tasks/task/${task}`) .send({ task }) .expect(200) - .then(response => response.body.docs[0]); + .then(response => response.body); } function historyDocs(taskId) { @@ -434,9 +434,7 @@ export default function({ getService }) { expect(successfulRunNowResult).to.eql({ id: originalTask.id }); await retry.try(async () => { - const [task] = (await currentTasks()).docs.filter( - taskDoc => taskDoc.id === originalTask.id - ); + const task = await currentTask(originalTask.id); expect(task.state.count).to.eql(2); }); diff --git a/x-pack/test/saved_object_api_integration/common/lib/space_test_utils.ts b/x-pack/test/saved_object_api_integration/common/lib/space_test_utils.ts index d858177dc62ca..1619d77761c84 100644 --- a/x-pack/test/saved_object_api_integration/common/lib/space_test_utils.ts +++ b/x-pack/test/saved_object_api_integration/common/lib/space_test_utils.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { DEFAULT_SPACE_ID } from '../../../../legacy/plugins/spaces/common/constants'; +import { DEFAULT_SPACE_ID } from '../../../../plugins/spaces/common/constants'; export function getUrlPrefix(spaceId: string) { return spaceId && spaceId !== DEFAULT_SPACE_ID ? `/s/${spaceId}` : ``; diff --git a/x-pack/test/saved_object_api_integration/common/suites/bulk_create.ts b/x-pack/test/saved_object_api_integration/common/suites/bulk_create.ts index d719543fa3807..b6f1bb956d72d 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/bulk_create.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/bulk_create.ts @@ -6,7 +6,7 @@ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; -import { DEFAULT_SPACE_ID } from '../../../../legacy/plugins/spaces/common/constants'; +import { DEFAULT_SPACE_ID } from '../../../../plugins/spaces/common/constants'; import { getIdPrefix, getUrlPrefix } from '../lib/space_test_utils'; import { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; diff --git a/x-pack/test/saved_object_api_integration/common/suites/bulk_get.ts b/x-pack/test/saved_object_api_integration/common/suites/bulk_get.ts index ab7babff8dead..9c5cc375502d1 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/bulk_get.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/bulk_get.ts @@ -6,7 +6,7 @@ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; -import { DEFAULT_SPACE_ID } from '../../../../legacy/plugins/spaces/common/constants'; +import { DEFAULT_SPACE_ID } from '../../../../plugins/spaces/common/constants'; import { getIdPrefix, getUrlPrefix } from '../lib/space_test_utils'; import { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; diff --git a/x-pack/test/saved_object_api_integration/common/suites/bulk_update.ts b/x-pack/test/saved_object_api_integration/common/suites/bulk_update.ts index e0cc1498d71ca..d14c5ccbd1d0e 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/bulk_update.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/bulk_update.ts @@ -6,7 +6,7 @@ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; -import { DEFAULT_SPACE_ID } from '../../../../legacy/plugins/spaces/common/constants'; +import { DEFAULT_SPACE_ID } from '../../../../plugins/spaces/common/constants'; import { getIdPrefix, getUrlPrefix } from '../lib/space_test_utils'; import { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; diff --git a/x-pack/test/saved_object_api_integration/common/suites/create.ts b/x-pack/test/saved_object_api_integration/common/suites/create.ts index 3ee0548b707bc..29960c513d40f 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/create.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/create.ts @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; -import { DEFAULT_SPACE_ID } from '../../../../legacy/plugins/spaces/common/constants'; +import { DEFAULT_SPACE_ID } from '../../../../plugins/spaces/common/constants'; import { getUrlPrefix } from '../lib/space_test_utils'; import { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; diff --git a/x-pack/test/saved_object_api_integration/common/suites/delete.ts b/x-pack/test/saved_object_api_integration/common/suites/delete.ts index 9581a2b3983ef..d96ae5446d732 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/delete.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/delete.ts @@ -6,7 +6,7 @@ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; -import { DEFAULT_SPACE_ID } from '../../../../legacy/plugins/spaces/common/constants'; +import { DEFAULT_SPACE_ID } from '../../../../plugins/spaces/common/constants'; import { getIdPrefix, getUrlPrefix } from '../lib/space_test_utils'; import { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; diff --git a/x-pack/test/saved_object_api_integration/common/suites/export.ts b/x-pack/test/saved_object_api_integration/common/suites/export.ts index 114a1fe53ccd6..4a56d18342dc9 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/export.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/export.ts @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; -import { DEFAULT_SPACE_ID } from '../../../../legacy/plugins/spaces/common/constants'; +import { DEFAULT_SPACE_ID } from '../../../../plugins/spaces/common/constants'; import { getIdPrefix, getUrlPrefix } from '../lib/space_test_utils'; import { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; diff --git a/x-pack/test/saved_object_api_integration/common/suites/find.ts b/x-pack/test/saved_object_api_integration/common/suites/find.ts index 6799f0ec63846..f270fc8f4db05 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/find.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/find.ts @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; -import { DEFAULT_SPACE_ID } from '../../../../legacy/plugins/spaces/common/constants'; +import { DEFAULT_SPACE_ID } from '../../../../plugins/spaces/common/constants'; import { getIdPrefix, getUrlPrefix } from '../lib/space_test_utils'; import { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; diff --git a/x-pack/test/saved_object_api_integration/common/suites/get.ts b/x-pack/test/saved_object_api_integration/common/suites/get.ts index 39bfc5df4d6e3..c98209ca1e105 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/get.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/get.ts @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; -import { DEFAULT_SPACE_ID } from '../../../../legacy/plugins/spaces/common/constants'; +import { DEFAULT_SPACE_ID } from '../../../../plugins/spaces/common/constants'; import { getIdPrefix, getUrlPrefix } from '../lib/space_test_utils'; import { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; diff --git a/x-pack/test/saved_object_api_integration/common/suites/import.ts b/x-pack/test/saved_object_api_integration/common/suites/import.ts index 8e4ef61cf3c12..f6723c912f82e 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/import.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/import.ts @@ -6,7 +6,7 @@ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; -import { DEFAULT_SPACE_ID } from '../../../../legacy/plugins/spaces/common/constants'; +import { DEFAULT_SPACE_ID } from '../../../../plugins/spaces/common/constants'; import { getIdPrefix, getUrlPrefix } from '../lib/space_test_utils'; import { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; diff --git a/x-pack/test/saved_object_api_integration/common/suites/resolve_import_errors.ts b/x-pack/test/saved_object_api_integration/common/suites/resolve_import_errors.ts index 8ae3a1258ab3a..1b538b9b1b65d 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/resolve_import_errors.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/resolve_import_errors.ts @@ -6,7 +6,7 @@ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; -import { DEFAULT_SPACE_ID } from '../../../../legacy/plugins/spaces/common/constants'; +import { DEFAULT_SPACE_ID } from '../../../../plugins/spaces/common/constants'; import { getIdPrefix, getUrlPrefix } from '../lib/space_test_utils'; import { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; diff --git a/x-pack/test/saved_object_api_integration/common/suites/update.ts b/x-pack/test/saved_object_api_integration/common/suites/update.ts index cd291c53c5f34..d6b7602c0114a 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/update.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/update.ts @@ -6,7 +6,7 @@ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; -import { DEFAULT_SPACE_ID } from '../../../../legacy/plugins/spaces/common/constants'; +import { DEFAULT_SPACE_ID } from '../../../../plugins/spaces/common/constants'; import { getIdPrefix, getUrlPrefix } from '../lib/space_test_utils'; import { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; diff --git a/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts b/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts index 9206e48afe9a4..f233bc1d11d7c 100644 --- a/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts +++ b/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { DEFAULT_SPACE_ID } from '../../../../legacy/plugins/spaces/common/constants'; +import { DEFAULT_SPACE_ID } from '../../../../plugins/spaces/common/constants'; export function getUrlPrefix(spaceId?: string) { return spaceId && spaceId !== DEFAULT_SPACE_ID ? `/s/${spaceId}` : ``; diff --git a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts index 20b4d024803d7..071067ffa85cb 100644 --- a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts +++ b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; import { EsArchiver } from 'src/es_archiver'; import { SavedObject } from 'src/core/server'; -import { DEFAULT_SPACE_ID } from '../../../../legacy/plugins/spaces/common/constants'; +import { DEFAULT_SPACE_ID } from '../../../../plugins/spaces/common/constants'; import { CopyResponse } from '../../../../plugins/spaces/server/lib/copy_to_spaces'; import { getUrlPrefix } from '../lib/space_test_utils'; import { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; diff --git a/x-pack/test/ui_capabilities/common/services/features.ts b/x-pack/test/ui_capabilities/common/services/features.ts index 9f644fd6d0f6e..0f796c1d0a0cc 100644 --- a/x-pack/test/ui_capabilities/common/services/features.ts +++ b/x-pack/test/ui_capabilities/common/services/features.ts @@ -22,9 +22,11 @@ export class FeaturesService { }); } - public async get(): Promise { + public async get({ ignoreValidLicenses } = { ignoreValidLicenses: false }): Promise { this.log.debug('requesting /api/features to get the features'); - const response = await this.axios.get('/api/features'); + const response = await this.axios.get( + `/api/features?ignoreValidLicenses=${ignoreValidLicenses}` + ); if (response.status !== 200) { throw new Error( diff --git a/x-pack/test/ui_capabilities/spaces_only/tests/index.ts b/x-pack/test/ui_capabilities/spaces_only/tests/index.ts index 0b40f9716dcb4..a25838ac4f76d 100644 --- a/x-pack/test/ui_capabilities/spaces_only/tests/index.ts +++ b/x-pack/test/ui_capabilities/spaces_only/tests/index.ts @@ -16,7 +16,8 @@ export default function uiCapabilitesTests({ loadTestFile, getService }: FtrProv this.tags('ciGroup9'); before(async () => { - const features = await featuresService.get(); + // we're using a basic license, so if we want to disable all features, we have to ignore the valid licenses + const features = await featuresService.get({ ignoreValidLicenses: true }); for (const space of SpaceScenarios) { const disabledFeatures = space.disabledFeatures === '*' ? Object.keys(features) : space.disabledFeatures; diff --git a/yarn.lock b/yarn.lock index a35cd1d541762..5dc4db12c5db4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20051,18 +20051,10 @@ markdown-it@^10.0.0: mdurl "^1.0.1" uc.micro "^1.0.5" -markdown-to-jsx@^6.9.1: - version "6.9.3" - resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-6.9.3.tgz#31719e3c54517ba9805db81d53701b89f5d2ed7e" - integrity sha512-iXteiv317VZd1vk/PBH5MWMD4r0XWekoWCHRVVadBcnCtxavhtfV1UaEaQgq9KyckTv31L60ASh5ZVVrOh37Qg== - dependencies: - prop-types "^15.6.2" - unquote "^1.1.0" - -markdown-to-jsx@^6.9.3: - version "6.10.3" - resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-6.10.3.tgz#7f0946684acd321125ff2de7fd258a9b9c7c40b7" - integrity sha512-PSoUyLnW/xoW6RsxZrquSSz5eGEOTwa15H5eqp3enmrp8esmgDJmhzd6zmQ9tgAA9TxJzx1Hmf3incYU/IamoQ== +markdown-to-jsx@^6.9.1, markdown-to-jsx@^6.9.3: + version "6.11.0" + resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-6.11.0.tgz#a2e3f2bc781c3402d8bb0f8e0a12a186474623b0" + integrity sha512-RH7LCJQ4RFmPqVeZEesKaO1biRzB/k4utoofmTCp3Eiw6D7qfvK8fzZq/2bjEJAtVkfPrM5SMt5APGf2rnaKMg== dependencies: prop-types "^15.6.2" unquote "^1.1.0" @@ -23809,10 +23801,10 @@ querystring@0.2.0, querystring@^0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= -querystringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.0.tgz#7ded8dfbf7879dcc60d0a644ac6754b283ad17ef" - integrity sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg== +querystringify@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" + integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== quick-lru@^1.0.0: version "1.1.0" @@ -30206,11 +30198,11 @@ url-parse-lax@^3.0.0: prepend-http "^2.0.0" url-parse@^1.4.3: - version "1.4.4" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.4.tgz#cac1556e95faa0303691fec5cf9d5a1bc34648f8" - integrity sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg== + version "1.4.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== dependencies: - querystringify "^2.0.0" + querystringify "^2.1.1" requires-port "^1.0.0" url-pattern@^1.0.3: