diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 94296d076189b..0f1136fd5334b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -28,11 +28,6 @@ # Canvas /x-pack/legacy/plugins/canvas/ @elastic/kibana-canvas -# Code -/x-pack/legacy/plugins/code/ @teams/code -/x-pack/test/functional/apps/code/ @teams/code -/x-pack/test/api_integration/apis/code/ @teams/code - # Logs & Metrics UI /x-pack/legacy/plugins/infra/ @elastic/logs-metrics-ui /x-pack/legacy/plugins/integrations_manager/ @elastic/epm diff --git a/.sass-lint.yml b/.sass-lint.yml index 64309aaae6cc3..f6c0f5bb83fcb 100644 --- a/.sass-lint.yml +++ b/.sass-lint.yml @@ -2,7 +2,6 @@ files: include: - 'src/legacy/core_plugins/metrics/**/*.s+(a|c)ss' - 'src/legacy/core_plugins/timelion/**/*.s+(a|c)ss' - - 'src/legacy/ui/public/query_bar/**/*.s+(a|c)ss' - 'src/legacy/ui/public/vislib/**/*.s+(a|c)ss' - 'x-pack/legacy/plugins/rollup/**/*.s+(a|c)ss' - 'x-pack/legacy/plugins/security/**/*.s+(a|c)ss' diff --git a/STYLEGUIDE.md b/STYLEGUIDE.md index 5fd3ef5e8ff4b..461d51a3e76e3 100644 --- a/STYLEGUIDE.md +++ b/STYLEGUIDE.md @@ -120,6 +120,7 @@ You should prefer modern language features in a lot of cases, e.g.: * Prefer arrow function over storing `this` (no `const self = this;`) * Prefer template strings over string concatenation * Prefer the spread operator for copying arrays (`[...arr]`) over `arr.slice()` +* Use optional chaining (`?.`) and nullish Coalescing (`??`) over `lodash.get` (and similar utilities) ### Avoid mutability and state diff --git a/docs/development/core/public/kibana-plugin-public.app.chromeless.md b/docs/development/core/public/kibana-plugin-public.app.chromeless.md new file mode 100644 index 0000000000000..dc1e19bab80b2 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.app.chromeless.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [App](./kibana-plugin-public.app.md) > [chromeless](./kibana-plugin-public.app.chromeless.md) + +## App.chromeless property + +Hide the UI chrome when the application is mounted. Defaults to `false`. Takes precedence over chrome service visibility settings. + +Signature: + +```typescript +chromeless?: boolean; +``` diff --git a/docs/development/core/public/kibana-plugin-public.app.md b/docs/development/core/public/kibana-plugin-public.app.md index 60cac357d1fe0..c500c080a5feb 100644 --- a/docs/development/core/public/kibana-plugin-public.app.md +++ b/docs/development/core/public/kibana-plugin-public.app.md @@ -16,5 +16,6 @@ export interface App extends AppBase | Property | Type | Description | | --- | --- | --- | +| [chromeless](./kibana-plugin-public.app.chromeless.md) | boolean | Hide the UI chrome when the application is mounted. Defaults to false. Takes precedence over chrome service visibility settings. | | [mount](./kibana-plugin-public.app.mount.md) | (context: AppMountContext, params: AppMountParameters) => AppUnmount | Promise<AppUnmount> | A mount function called when the user navigates to this app's route. | diff --git a/docs/development/core/public/kibana-plugin-public.appmountparameters.appbasepath.md b/docs/development/core/public/kibana-plugin-public.appmountparameters.appbasepath.md index 16c8ffe07fc15..31513bda2e879 100644 --- a/docs/development/core/public/kibana-plugin-public.appmountparameters.appbasepath.md +++ b/docs/development/core/public/kibana-plugin-public.appmountparameters.appbasepath.md @@ -21,12 +21,13 @@ How to configure react-router with a base path: export class MyPlugin implements Plugin { setup({ application }) { application.register({ - id: 'my-app', - async mount(context, params) { - const { renderApp } = await import('./application'); - return renderApp(context, params); - }, - }); + id: 'my-app', + async mount(context, params) { + const { renderApp } = await import('./application'); + return renderApp(context, params); + }, + }); + } } ``` diff --git a/docs/development/core/public/kibana-plugin-public.md b/docs/development/core/public/kibana-plugin-public.md index df0b963e2b627..cec307032094e 100644 --- a/docs/development/core/public/kibana-plugin-public.md +++ b/docs/development/core/public/kibana-plugin-public.md @@ -109,17 +109,18 @@ The plugin integrates with the core system via lifecycle events: `setup` | [HttpStart](./kibana-plugin-public.httpstart.md) | See [HttpServiceBase](./kibana-plugin-public.httpservicebase.md) | | [IContextProvider](./kibana-plugin-public.icontextprovider.md) | A function that returns a context value for a specific key of given context type. | | [IToasts](./kibana-plugin-public.itoasts.md) | Methods for adding and removing global toast messages. See [ToastsApi](./kibana-plugin-public.toastsapi.md). | -| [OverlayBannerMount](./kibana-plugin-public.overlaybannermount.md) | A function that will mount the banner inside the provided element. | -| [OverlayBannerUnmount](./kibana-plugin-public.overlaybannerunmount.md) | A function that will unmount the banner from the element. | +| [MountPoint](./kibana-plugin-public.mountpoint.md) | A function that should mount DOM content inside the provided container element and return a handler to unmount it. | | [PluginInitializer](./kibana-plugin-public.plugininitializer.md) | The plugin export at the root of a plugin's public directory should conform to this interface. | | [PluginOpaqueId](./kibana-plugin-public.pluginopaqueid.md) | | | [RecursiveReadonly](./kibana-plugin-public.recursivereadonly.md) | | | [SavedObjectAttribute](./kibana-plugin-public.savedobjectattribute.md) | Type definition for a Saved Object attribute value | | [SavedObjectAttributeSingle](./kibana-plugin-public.savedobjectattributesingle.md) | Don't use this type, it's simply a helper type for [SavedObjectAttribute](./kibana-plugin-public.savedobjectattribute.md) | | [SavedObjectsClientContract](./kibana-plugin-public.savedobjectsclientcontract.md) | SavedObjectsClientContract as implemented by the [SavedObjectsClient](./kibana-plugin-public.savedobjectsclient.md) | +| [Toast](./kibana-plugin-public.toast.md) | | | [ToastInput](./kibana-plugin-public.toastinput.md) | Inputs for [IToasts](./kibana-plugin-public.itoasts.md) APIs. | | [ToastInputFields](./kibana-plugin-public.toastinputfields.md) | Allowed fields for [ToastInput](./kibana-plugin-public.toastinput.md). | | [ToastsSetup](./kibana-plugin-public.toastssetup.md) | [IToasts](./kibana-plugin-public.itoasts.md) | | [ToastsStart](./kibana-plugin-public.toastsstart.md) | [IToasts](./kibana-plugin-public.itoasts.md) | | [UiSettingsClientContract](./kibana-plugin-public.uisettingsclientcontract.md) | Client-side client that provides access to the advanced settings stored in elasticsearch. The settings provide control over the behavior of the Kibana application. For example, a user can specify how to display numeric or date fields. Users can adjust the settings via Management UI. [UiSettingsClient](./kibana-plugin-public.uisettingsclient.md) | +| [UnmountCallback](./kibana-plugin-public.unmountcallback.md) | A function that will unmount the element previously mounted by the associated [MountPoint](./kibana-plugin-public.mountpoint.md) | diff --git a/docs/development/core/public/kibana-plugin-public.mountpoint.md b/docs/development/core/public/kibana-plugin-public.mountpoint.md new file mode 100644 index 0000000000000..58f407904a576 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.mountpoint.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [MountPoint](./kibana-plugin-public.mountpoint.md) + +## MountPoint type + +A function that should mount DOM content inside the provided container element and return a handler to unmount it. + +Signature: + +```typescript +export declare type MountPoint = (element: HTMLElement) => UnmountCallback; +``` diff --git a/docs/development/core/public/kibana-plugin-public.overlaybannermount.md b/docs/development/core/public/kibana-plugin-public.overlaybannermount.md deleted file mode 100644 index 0fd0aca652cf0..0000000000000 --- a/docs/development/core/public/kibana-plugin-public.overlaybannermount.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [OverlayBannerMount](./kibana-plugin-public.overlaybannermount.md) - -## OverlayBannerMount type - -A function that will mount the banner inside the provided element. - -Signature: - -```typescript -export declare type OverlayBannerMount = (element: HTMLElement) => OverlayBannerUnmount; -``` diff --git a/docs/development/core/public/kibana-plugin-public.overlaybannersstart.add.md b/docs/development/core/public/kibana-plugin-public.overlaybannersstart.add.md index 8c3e874804e08..8ce59d5d9ca78 100644 --- a/docs/development/core/public/kibana-plugin-public.overlaybannersstart.add.md +++ b/docs/development/core/public/kibana-plugin-public.overlaybannersstart.add.md @@ -9,14 +9,14 @@ Add a new banner Signature: ```typescript -add(mount: OverlayBannerMount, priority?: number): string; +add(mount: MountPoint, priority?: number): string; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| mount | OverlayBannerMount | | +| mount | MountPoint | | | priority | number | | Returns: diff --git a/docs/development/core/public/kibana-plugin-public.overlaybannersstart.replace.md b/docs/development/core/public/kibana-plugin-public.overlaybannersstart.replace.md index 8f624c285b180..a8f6915ea9bb7 100644 --- a/docs/development/core/public/kibana-plugin-public.overlaybannersstart.replace.md +++ b/docs/development/core/public/kibana-plugin-public.overlaybannersstart.replace.md @@ -9,7 +9,7 @@ Replace a banner in place Signature: ```typescript -replace(id: string | undefined, mount: OverlayBannerMount, priority?: number): string; +replace(id: string | undefined, mount: MountPoint, priority?: number): string; ``` ## Parameters @@ -17,7 +17,7 @@ replace(id: string | undefined, mount: OverlayBannerMount, priority?: number): s | Parameter | Type | Description | | --- | --- | --- | | id | string | undefined | | -| mount | OverlayBannerMount | | +| mount | MountPoint | | | priority | number | | Returns: diff --git a/docs/development/core/public/kibana-plugin-public.overlaybannerunmount.md b/docs/development/core/public/kibana-plugin-public.overlaybannerunmount.md deleted file mode 100644 index c9a7c2b8fee92..0000000000000 --- a/docs/development/core/public/kibana-plugin-public.overlaybannerunmount.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [OverlayBannerUnmount](./kibana-plugin-public.overlaybannerunmount.md) - -## OverlayBannerUnmount type - -A function that will unmount the banner from the element. - -Signature: - -```typescript -export declare type OverlayBannerUnmount = () => void; -``` diff --git a/docs/development/core/public/kibana-plugin-public.toast.md b/docs/development/core/public/kibana-plugin-public.toast.md new file mode 100644 index 0000000000000..0cbbf29df073a --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.toast.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [Toast](./kibana-plugin-public.toast.md) + +## Toast type + +Signature: + +```typescript +export declare type Toast = ToastInputFields & { + id: string; +}; +``` diff --git a/docs/development/core/public/kibana-plugin-public.toastinput.md b/docs/development/core/public/kibana-plugin-public.toastinput.md index 75f12b3d94561..9dd20b5899f3a 100644 --- a/docs/development/core/public/kibana-plugin-public.toastinput.md +++ b/docs/development/core/public/kibana-plugin-public.toastinput.md @@ -9,5 +9,5 @@ Inputs for [IToasts](./kibana-plugin-public.itoasts.md) APIs. Signature: ```typescript -export declare type ToastInput = string | ToastInputFields | Promise; +export declare type ToastInput = string | ToastInputFields; ``` diff --git a/docs/development/core/public/kibana-plugin-public.toastinputfields.md b/docs/development/core/public/kibana-plugin-public.toastinputfields.md index ffcf9e5c6dea2..3a6bc3a5e45da 100644 --- a/docs/development/core/public/kibana-plugin-public.toastinputfields.md +++ b/docs/development/core/public/kibana-plugin-public.toastinputfields.md @@ -9,7 +9,10 @@ Allowed fields for [ToastInput](./kibana-plugin-public.toastinput.md). Signature: ```typescript -export declare type ToastInputFields = Pick>; +export declare type ToastInputFields = Pick> & { + title?: string | MountPoint; + text?: string | MountPoint; +}; ``` ## Remarks diff --git a/docs/development/core/public/kibana-plugin-public.toastsapi.add.md b/docs/development/core/public/kibana-plugin-public.toastsapi.add.md index 8e9648031f0e2..6b651b310e974 100644 --- a/docs/development/core/public/kibana-plugin-public.toastsapi.add.md +++ b/docs/development/core/public/kibana-plugin-public.toastsapi.add.md @@ -22,5 +22,5 @@ add(toastOrTitle: ToastInput): Toast; `Toast` -a +a [Toast](./kibana-plugin-public.toast.md) diff --git a/docs/development/core/public/kibana-plugin-public.toastsapi.adddanger.md b/docs/development/core/public/kibana-plugin-public.toastsapi.adddanger.md index 28e596f0c09e3..67ebad919ed2a 100644 --- a/docs/development/core/public/kibana-plugin-public.toastsapi.adddanger.md +++ b/docs/development/core/public/kibana-plugin-public.toastsapi.adddanger.md @@ -22,5 +22,5 @@ addDanger(toastOrTitle: ToastInput): Toast; `Toast` -a +a [Toast](./kibana-plugin-public.toast.md) diff --git a/docs/development/core/public/kibana-plugin-public.toastsapi.adderror.md b/docs/development/core/public/kibana-plugin-public.toastsapi.adderror.md index c8a48b3fa46c9..39090fb8f1bbe 100644 --- a/docs/development/core/public/kibana-plugin-public.toastsapi.adderror.md +++ b/docs/development/core/public/kibana-plugin-public.toastsapi.adderror.md @@ -23,5 +23,5 @@ addError(error: Error, options: ErrorToastOptions): Toast; `Toast` -a +a [Toast](./kibana-plugin-public.toast.md) diff --git a/docs/development/core/public/kibana-plugin-public.toastsapi.addsuccess.md b/docs/development/core/public/kibana-plugin-public.toastsapi.addsuccess.md index 0e01dc1364d07..ce9a9a2fae691 100644 --- a/docs/development/core/public/kibana-plugin-public.toastsapi.addsuccess.md +++ b/docs/development/core/public/kibana-plugin-public.toastsapi.addsuccess.md @@ -22,5 +22,5 @@ addSuccess(toastOrTitle: ToastInput): Toast; `Toast` -a +a [Toast](./kibana-plugin-public.toast.md) diff --git a/docs/development/core/public/kibana-plugin-public.toastsapi.addwarning.md b/docs/development/core/public/kibana-plugin-public.toastsapi.addwarning.md index 0e236f2737b12..948181f825763 100644 --- a/docs/development/core/public/kibana-plugin-public.toastsapi.addwarning.md +++ b/docs/development/core/public/kibana-plugin-public.toastsapi.addwarning.md @@ -22,5 +22,5 @@ addWarning(toastOrTitle: ToastInput): Toast; `Toast` -a +a [Toast](./kibana-plugin-public.toast.md) diff --git a/docs/development/core/public/kibana-plugin-public.toastsapi.md b/docs/development/core/public/kibana-plugin-public.toastsapi.md index e47f6d5c8ac59..ae4a2de9fc75c 100644 --- a/docs/development/core/public/kibana-plugin-public.toastsapi.md +++ b/docs/development/core/public/kibana-plugin-public.toastsapi.md @@ -28,5 +28,5 @@ export declare class ToastsApi implements IToasts | [addSuccess(toastOrTitle)](./kibana-plugin-public.toastsapi.addsuccess.md) | | Adds a new toast pre-configured with the success color and check icon. | | [addWarning(toastOrTitle)](./kibana-plugin-public.toastsapi.addwarning.md) | | Adds a new toast pre-configured with the warning color and help icon. | | [get$()](./kibana-plugin-public.toastsapi.get_.md) | | Observable of the toast messages to show to the user. | -| [remove(toast)](./kibana-plugin-public.toastsapi.remove.md) | | Removes a toast from the current array of toasts if present. | +| [remove(toastOrId)](./kibana-plugin-public.toastsapi.remove.md) | | Removes a toast from the current array of toasts if present. | diff --git a/docs/development/core/public/kibana-plugin-public.toastsapi.remove.md b/docs/development/core/public/kibana-plugin-public.toastsapi.remove.md index 5025c83a666c8..9f27041175207 100644 --- a/docs/development/core/public/kibana-plugin-public.toastsapi.remove.md +++ b/docs/development/core/public/kibana-plugin-public.toastsapi.remove.md @@ -9,14 +9,14 @@ Removes a toast from the current array of toasts if present. Signature: ```typescript -remove(toast: Toast): void; +remove(toastOrId: Toast | string): void; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| toast | Toast | a returned by | +| toastOrId | Toast | string | a [Toast](./kibana-plugin-public.toast.md) returned by [ToastsApi.add()](./kibana-plugin-public.toastsapi.add.md) or its id | Returns: diff --git a/docs/development/core/public/kibana-plugin-public.unmountcallback.md b/docs/development/core/public/kibana-plugin-public.unmountcallback.md new file mode 100644 index 0000000000000..f44562120c9ee --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.unmountcallback.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [UnmountCallback](./kibana-plugin-public.unmountcallback.md) + +## UnmountCallback type + +A function that will unmount the element previously mounted by the associated [MountPoint](./kibana-plugin-public.mountpoint.md) + +Signature: + +```typescript +export declare type UnmountCallback = () => void; +``` diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index caff7f5b1fdc6..38fceeb47d6fd 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -9,11 +9,12 @@ for displayed decimal values. . Scroll or search for the setting you want to modify. . Enter a new value for the setting. + [float] [[settings-read-only-access]] === [xpack]#Read only access# -When you have insufficient privileges to edit advanced settings, the following -indicator in Kibana will be displayed. The buttons to edit settings won't be visible. +When you have insufficient privileges to edit advanced settings, the following +indicator in Kibana will be displayed. The buttons to edit settings won't be visible. For more information on granting access to Kibana see <>. [role="screenshot"] @@ -25,9 +26,9 @@ image::images/settings-read-only-badge.png[Example of Advanced Settings Manageme WARNING: Modifying a setting can affect {kib} performance and cause problems that are -difficult to diagnose. Setting a property value to a blank field reverts +difficult to diagnose. Setting a property value to a blank field reverts to the default behavior, which might not be -compatible with other configuration settings. Deleting a custom setting +compatible with other configuration settings. Deleting a custom setting removes it from {kib} permanently. @@ -44,7 +45,7 @@ removes it from {kib} permanently. adapt to the interval between measurements. Keys are http://en.wikipedia.org/wiki/ISO_8601#Time_intervals[ISO8601 intervals]. `dateFormat:tz`:: The timezone that Kibana uses. The default value of `Browser` uses the timezone detected by the browser. `dateNanosFormat`:: The format to use for displaying https://momentjs.com/docs/#/displaying/format/[pretty formatted dates] of {ref}/date_nanos.html[Elasticsearch date_nanos type]. -`defaultIndex`:: The index to access if no index is set. The default is `null`. +`defaultIndex`:: The index to access if no index is set. The default is `null`. `fields:popularLimit`:: The top N most popular fields to show. `filterEditor:suggestValues`:: Set this property to `false` to prevent the filter editor from suggesting values for fields. `filters:pinnedByDefault`:: Set this property to `true` to make filters have a global state (be pinned) by default. @@ -59,46 +60,46 @@ mentioned use "\_default_". `histogram:maxBars`:: Date histograms are not generated with more bars than the value of this property, scaling values when necessary. `history:limit`:: In fields that have history, such as query inputs, show this many recent values. -`indexPattern:fieldMapping:lookBack`:: For index patterns containing timestamps in their names, +`indexPattern:fieldMapping:lookBack`:: For index patterns containing timestamps in their names, look for this many recent matching patterns from which to query the field mapping. `indexPattern:placeholder`:: The default placeholder value to use in Management > Index Patterns > Create Index Pattern. -`metaFields`:: Fields that exist outside of `_source`. Kibana merges these fields +`metaFields`:: Fields that exist outside of `_source`. Kibana merges these fields into the document when displaying it. `metrics:max_buckets`:: The maximum numbers of buckets that a single -data source can return. This might arise when the user selects a +data source can return. This might arise when the user selects a short interval (for example, 1s) for a long time period (1 year). -`query:allowLeadingWildcards`:: Allows a wildcard (*) as the first character -in a query clause. Only applies when experimental query features are -enabled in the query bar. To disallow leading wildcards in Lucene queries, +`query:allowLeadingWildcards`:: Allows a wildcard (*) as the first character +in a query clause. Only applies when experimental query features are +enabled in the query bar. To disallow leading wildcards in Lucene queries, use `query:queryString:options`. `query:queryString:options`:: Options for the Lucene query string parser. Only used when "Query language" is set to Lucene. -`savedObjects:listingLimit`:: The number of objects to fetch for lists of saved objects. +`savedObjects:listingLimit`:: The number of objects to fetch for lists of saved objects. The default value is 1000. Do not set above 10000. -`savedObjects:perPage`:: The number of objects to show on each page of the +`savedObjects:perPage`:: The number of objects to show on each page of the list of saved objects. The default is 5. `search:queryLanguage`:: The query language to use in the query bar. -Choices are <>, a language built specifically for {kib}, and the <>, a language built specifically for {kib}, and the <>. -`shortDots:enable`:: Set this property to `true` to shorten long +`shortDots:enable`:: Set this property to `true` to shorten long field names in visualizations. For example, show `f.b.baz` instead of `foo.bar.baz`. `sort:options`:: Options for the Elasticsearch {ref}/search-request-body.html#request-body-search-sort[sort] parameter. -`state:storeInSessionStorage`:: [experimental] Kibana tracks UI state in the -URL, which can lead to problems when there is a lot of state information, -and the URL gets very long. -Enabling this setting stores part of the URL in your browser session to keep the +`state:storeInSessionStorage`:: [experimental] Kibana tracks UI state in the +URL, which can lead to problems when there is a lot of state information, +and the URL gets very long. +Enabling this setting stores part of the URL in your browser session to keep the URL short. `theme:darkMode`:: Set to `true` to enable a dark mode for the {kib} UI. You must refresh the page to apply the setting. -`timepicker:quickRanges`:: The list of ranges to show in the Quick section of -the time filter. This should be an array of objects, with each object containing -`from`, `to` (see {ref}/common-options.html#date-math[accepted formats]), +`timepicker:quickRanges`:: The list of ranges to show in the Quick section of +the time filter. This should be an array of objects, with each object containing +`from`, `to` (see {ref}/common-options.html#date-math[accepted formats]), and `display` (the title to be displayed). `timepicker:refreshIntervalDefaults`:: The default refresh interval for the time filter. Example: `{ "display": "15 seconds", "pause": true, "value": 15000 }`. `timepicker:timeDefaults`:: The default selection in the time filter. `truncate:maxHeight`:: The maximum height that a cell occupies in a table. Set to 0 to disable truncation. -`xPack:defaultAdminEmail`:: Email address for X-Pack admin operations, such as +`xPack:defaultAdminEmail`:: Email address for X-Pack admin operations, such as cluster alert notifications from Monitoring. @@ -107,7 +108,7 @@ cluster alert notifications from Monitoring. === Accessibility settings [horizontal] -`accessibility:disableAnimations`:: Turns off all unnecessary animations in the +`accessibility:disableAnimations`:: Turns off all unnecessary animations in the {kib} UI. Refresh the page to apply the changes. [float] @@ -124,21 +125,21 @@ cluster alert notifications from Monitoring. [horizontal] `context:defaultSize`:: The number of surrounding entries to display in the context view. The default value is 5. `context:step`:: The number by which to increment or decrement the context size. The default value is 5. -`context:tieBreakerFields`:: A comma-separated list of fields to use -for breaking a tie between documents that have the same timestamp value. The first +`context:tieBreakerFields`:: A comma-separated list of fields to use +for breaking a tie between documents that have the same timestamp value. The first field that is present and sortable in the current index pattern is used. `defaultColumns`:: The columns that appear by default on the Discover page. -The default is `_source`. -`discover:aggs:terms:size`:: The number terms that are visualized when clicking +The default is `_source`. +`discover:aggs:terms:size`:: The number terms that are visualized when clicking the Visualize button in the field drop down. The default is `20`. `discover:sampleSize`:: The number of rows to show in the Discover table. `discover:sort:defaultOrder`:: The default sort direction for time-based index patterns. -`discover:searchOnPageLoad`:: Controls whether a search is executed when Discover first loads. +`discover:searchOnPageLoad`:: Controls whether a search is executed when Discover first loads. This setting does not have an effect when loading a saved search. `doc_table:hideTimeColumn`:: Hides the "Time" column in Discover and in all saved searches on dashboards. -`doc_table:highlight`:: Highlights results in Discover and saved searches on dashboards. +`doc_table:highlight`:: Highlights results in Discover and saved searches on dashboards. Highlighting slows requests when -working on big documents. +working on big documents. @@ -150,14 +151,14 @@ working on big documents. [horizontal] `notifications:banner`:: A custom banner intended for temporary notices to all users. Supports https://help.github.com/en/articles/basic-writing-and-formatting-syntax[Markdown]. -`notifications:lifetime:banner`:: The duration, in milliseconds, for banner -notification displays. The default value is 3000000. Set this field to `Infinity` +`notifications:lifetime:banner`:: The duration, in milliseconds, for banner +notification displays. The default value is 3000000. Set this field to `Infinity` to disable banner notifications. -`notifications:lifetime:error`:: The duration, in milliseconds, for error +`notifications:lifetime:error`:: The duration, in milliseconds, for error notification displays. The default value is 300000. Set this field to `Infinity` to disable error notifications. -`notifications:lifetime:info`:: The duration, in milliseconds, for information notification displays. +`notifications:lifetime:info`:: The duration, in milliseconds, for information notification displays. The default value is 5000. Set this field to `Infinity` to disable information notifications. -`notifications:lifetime:warning`:: The duration, in milliseconds, for warning notification +`notifications:lifetime:warning`:: The duration, in milliseconds, for warning notification displays. The default value is 10000. Set this field to `Infinity` to disable warning notifications. @@ -175,8 +176,8 @@ displays. The default value is 10000. Set this field to `Infinity` to disable wa === Rollup settings [horizontal] -`rollups:enableIndexPatterns`:: Enables the creation of index patterns that -capture rollup indices, which in turn enables visualizations based on rollup data. +`rollups:enableIndexPatterns`:: Enables the creation of index patterns that +capture rollup indices, which in turn enables visualizations based on rollup data. Refresh the page to apply the changes. @@ -188,22 +189,22 @@ Refresh the page to apply the changes. `courier:batchSearches`:: When disabled, dashboard panels will load individually, and search requests will terminate when users navigate away or update the query. When enabled, dashboard panels will load together when all of the data is loaded, and searches will not terminate. -`courier:customRequestPreference`:: {ref}/search-request-body.html#request-body-search-preference[Request preference] +`courier:customRequestPreference`:: {ref}/search-request-body.html#request-body-search-preference[Request preference] to use when `courier:setRequestPreference` is set to "custom". -`courier:ignoreFilterIfFieldNotInIndex`:: Skips filters that apply to fields that don't exist in the index for a visualization. +`courier:ignoreFilterIfFieldNotInIndex`:: Skips filters that apply to fields that don't exist in the index for a visualization. Useful when dashboards consist of visualizations from multiple index patterns. -`courier:maxConcurrentShardRequests`:: Controls the {ref}/search-multi-search.html[max_concurrent_shard_requests] -setting used for `_msearch` requests sent by {kib}. Set to 0 to disable this +`courier:maxConcurrentShardRequests`:: Controls the {ref}/search-multi-search.html[max_concurrent_shard_requests] +setting used for `_msearch` requests sent by {kib}. Set to 0 to disable this config and use the {es} default. `courier:setRequestPreference`:: Enables you to set which shards handle your search requests. -* *Session ID:* Restricts operations to execute all search requests on the same shards. +* *Session ID:* Restricts operations to execute all search requests on the same shards. This has the benefit of reusing shard caches across requests. -* *Custom:* Allows you to define your own preference. Use `courier:customRequestPreference` +* *Custom:* Allows you to define your own preference. Use `courier:customRequestPreference` to customize your preference value. -* *None:* Do not set a preference. This might provide better performance -because requests can be spread across all shard copies. However, results might +* *None:* Do not set a preference. This might provide better performance +because requests can be spread across all shard copies. However, results might be inconsistent because different shards might be in different refresh states. -`search:includeFrozen`:: Includes {ref}/frozen-indices.html[frozen indices] in results. +`search:includeFrozen`:: Includes {ref}/frozen-indices.html[frozen indices] in results. Searching through frozen indices might increase the search time. This setting is off by default. Users must opt-in to include frozen indices. @@ -212,8 +213,8 @@ might increase the search time. This setting is off by default. Users must opt-i === SIEM settings [horizontal] -`siem:defaultAnomalyScore`:: The threshold above which anomalies are displayed in the SIEM app. -`siem:defaultIndex`:: A comma-delimited list of Elasticsearch indices from which the SIEM app collects events. +`siem:defaultAnomalyScore`:: The threshold above which Machine Learning job anomalies are displayed in the SIEM app. +`siem:defaultIndex`:: A comma-delimited list of Elasticsearch indices from which the SIEM app collects events. `siem:refreshIntervalDefaults`:: The default refresh interval for the SIEM time filter, in milliseconds. `siem:timeDefaults`:: The default period of time in the SIEM time filter. @@ -226,16 +227,16 @@ might increase the search time. This setting is off by default. Users must opt-i `timelion:default_rows`:: The default number of rows to use on a Timelion sheet. `timelion:es.default_index`:: The default index when using the `.es()` query. `timelion:es.timefield`:: The default field containing a timestamp when using the `.es()` query. -`timelion:graphite.url`:: [experimental] Used with graphite queries, this is the URL of your graphite host -in the form https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite. This URL can be +`timelion:graphite.url`:: [experimental] Used with graphite queries, this is the URL of your graphite host +in the form https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite. This URL can be selected from a whitelist configured in the `kibana.yml` under `timelion.graphiteUrls`. `timelion:max_buckets`:: The maximum number of buckets a single data source can return. This value is used for calculating automatic intervals in visualizations. `timelion:min_interval`:: The smallest interval to calculate when using "auto". `timelion:quandl.key`:: [experimental] Used with quandl queries, this is your API key from https://www.quandl.com/[www.quandl.com]. -`timelion:showTutorial`:: Shows the Timelion tutorial +`timelion:showTutorial`:: Shows the Timelion tutorial to users when they first open the Timelion app. -`timelion:target_buckets`:: Used for calculating automatic intervals in visualizations, +`timelion:target_buckets`:: Used for calculating automatic intervals in visualizations, this is the number of buckets to try to represent. @@ -246,18 +247,18 @@ this is the number of buckets to try to represent. [horizontal] `visualization:colorMapping`:: Maps values to specified colors in visualizations. -`visualization:dimmingOpacity`:: The opacity of the chart items that are dimmed -when highlighting another element of the chart. The lower this number, the more +`visualization:dimmingOpacity`:: The opacity of the chart items that are dimmed +when highlighting another element of the chart. The lower this number, the more the highlighted element stands out. This must be a number between 0 and 1. -`visualization:loadingDelay`:: The time to wait before dimming visualizations +`visualization:loadingDelay`:: The time to wait before dimming visualizations during a query. -`visualization:regionmap:showWarnings`:: Shows +`visualization:regionmap:showWarnings`:: Shows a warning in a region map when terms cannot be joined to a shape. `visualization:tileMap:WMSdefaults`:: The default properties for the WMS map server support in the coordinate map. `visualization:tileMap:maxPrecision`:: The maximum geoHash precision displayed on tile maps: 7 is high, 10 is very high, -and 12 is the maximum. See this +and 12 is the maximum. See this {ref}/search-aggregations-bucket-geohashgrid-aggregation.html#_cell_dimensions_at_the_equator[explanation of cell dimensions]. -`visualize:enableLabs`:: Enables users to create, view, and edit experimental visualizations. +`visualize:enableLabs`:: Enables users to create, view, and edit experimental visualizations. If disabled, only visualizations that are considered production-ready are available to the user. @@ -265,6 +266,5 @@ If disabled, only visualizations that are considered production-ready are availa [[kibana-telemetry-settings]] === Usage data settings -Helps improve the Elastic Stack by providing usage statistics for +Helps improve the Elastic Stack by providing usage statistics for basic features. This data will not be shared outside of Elastic. - diff --git a/docs/management/dashboard_only_mode/images/advanced_dashboard_mode_role_setup.png b/docs/management/dashboard_only_mode/images/advanced_dashboard_mode_role_setup.png deleted file mode 100644 index 1e9600e4d4eb4..0000000000000 Binary files a/docs/management/dashboard_only_mode/images/advanced_dashboard_mode_role_setup.png and /dev/null differ diff --git a/docs/management/dashboard_only_mode/images/custom_dashboard_mode_role.png b/docs/management/dashboard_only_mode/images/custom_dashboard_mode_role.png deleted file mode 100644 index e9285ac914308..0000000000000 Binary files a/docs/management/dashboard_only_mode/images/custom_dashboard_mode_role.png and /dev/null differ diff --git a/docs/management/dashboard_only_mode/images/dashboard-only-user-role.png b/docs/management/dashboard_only_mode/images/dashboard-only-user-role.png deleted file mode 100644 index 0773f0f3a3407..0000000000000 Binary files a/docs/management/dashboard_only_mode/images/dashboard-only-user-role.png and /dev/null differ diff --git a/docs/management/dashboard_only_mode/images/view_only_dashboard.png b/docs/management/dashboard_only_mode/images/view_only_dashboard.png deleted file mode 100644 index a82a09c27e6e8..0000000000000 Binary files a/docs/management/dashboard_only_mode/images/view_only_dashboard.png and /dev/null differ diff --git a/docs/management/dashboard_only_mode/index.asciidoc b/docs/management/dashboard_only_mode/index.asciidoc deleted file mode 100644 index 97ac4392827dd..0000000000000 --- a/docs/management/dashboard_only_mode/index.asciidoc +++ /dev/null @@ -1,85 +0,0 @@ -[role="xpack"] -[[xpack-dashboard-only-mode]] -== Dashboard-only mode - -deprecated[7.4.0, "Using the `kibana_dashboard_only_user` role is deprecated. Use <> instead."] - -In dashboard-only mode, users have access to only the *Dashboard* app. -Users can view and filter the dashboards, but cannot create, edit, or delete -them. This enables you to: - -* Show off your dashboards without giving users access to all of {kib} - -* Share your {kib} dashboards without the risk of users accidentally -editing or deleting them - -Dashboard-only mode pairs well with fullscreen mode. -You can share your dashboard with the team responsible -for showing the dashboard on a big-screen monitor, and not worry about it being modified. - -[role="screenshot"] -image:management/dashboard_only_mode/images/view_only_dashboard.png["View Only Dashboard"] - -[[setup-dashboard-only-mode]] -[float] -=== Assign dashboard-only mode -With {security} enabled, you can restrict users to dashboard-only mode by assigning -them the built-in `kibana_dashboard_only_user` role. - -. Go to *Management > Security > Users*. -. Create or edit a user. -. Assign the `kibana_dashboard_only_user` role and a role that <>. -+ -For example, -to enable users to view the dashboards in the sample data sets, you must assign them -the `kibana_dashboard_only_user` role and a role that has -`read` access to the kibana_* indices. -+ -[role="screenshot"] -image:management/dashboard_only_mode/images/dashboard-only-user-role.png["Dashboard Only mode has no editing controls"] - -[IMPORTANT] -=========================================== -* If you assign users the `kibana_dashboard_only_user` role and a role -with write permissions to {kib}, they *will* have write access, -even though the controls remain hidden in {kib}. - -* If you also assign users the reserved `superuser` role, they will have full -access to {kib}. - -=========================================== - -[float] -[[grant-read-access-to-indices]] -=== Grant read access to indices - -The `kibana_dashboard_only_user` role -does not provide access to data indices. -You must also assign the user a role that grants `read` access -to each index you are using. Use *Management > Security > Roles* to create or edit a -role and assign index privileges. -For information on roles and privileges, see {ref}/authorization.html[User authorization]. - -[role="screenshot"] -image:management/dashboard_only_mode/images/custom_dashboard_mode_role.png["Dashboard Only mode has no editing controls"] - - -[float] -[[advanced-dashboard-mode-configuration]] -=== Advanced settings for dashboard only mode - -The `kibana_dashboard_only_user` role grants access to all spaces. -If your setup requires access to a -subset of spaces, you can create a custom role, and then tag it as Dashboard only mode. - -. Go to *Management > Advanced Settings*, and search for `xpackDashboardMode:roles`. -+ -By -default, this is set to -`kibana_dashboard_only_user`. - -. Add as many roles as you require. -+ -[role="screenshot"] -image:management/dashboard_only_mode/images/advanced_dashboard_mode_role_setup.png["Advanced dashboard mode role setup"] - diff --git a/docs/maps/images/grid_to_docs.gif b/docs/maps/images/grid_to_docs.gif new file mode 100644 index 0000000000000..11b396a4fe872 Binary files /dev/null and b/docs/maps/images/grid_to_docs.gif differ diff --git a/docs/maps/maps-aggregations.asciidoc b/docs/maps/maps-aggregations.asciidoc index 923b6cc1d6649..cd01acb2df7de 100644 --- a/docs/maps/maps-aggregations.asciidoc +++ b/docs/maps/maps-aggregations.asciidoc @@ -7,6 +7,14 @@ Use {ref}/search-aggregations.html[aggregations] to plot large data sets without Aggregations group your documents into buckets and calculate metrics for each bucket. Your documents stay in Elasticsearch and only the metrics for each group are returned to your computer. +Use aggregated layers with document layers to show aggregated views when the map shows larger +amounts of the globe and individual documents when the map shows smaller regions. + +In the following example, the Grid aggregation layer is only visible when the map is at zoom levels 0 through 5. The Documents layer is only visible when the map is at zoom levels 4 through 24. +See the <> tutorial for more details on configuring the layers. + +[role="screenshot"] +image::maps/images/grid_to_docs.gif[] [role="xpack"] [[maps-grid-aggregation]] diff --git a/docs/maps/maps-getting-started.asciidoc b/docs/maps/maps-getting-started.asciidoc index 3ff44b97de635..e6908ca773a2f 100644 --- a/docs/maps/maps-getting-started.asciidoc +++ b/docs/maps/maps-getting-started.asciidoc @@ -150,6 +150,7 @@ image::maps/images/grid_metrics_both.png[] . In the map legend, click *Add layer*. . Click the *Grid aggregation* data source. . Set *Index pattern* to *kibana_sample_data_logs*. +. Set *Show as* to *points*. . Click the *Add layer* button. . Set *Layer name* to `Total Requests and Bytes`. . Set *Zoom range for layer visibility* to the range [0, 9]. @@ -181,7 +182,7 @@ Now that your map is complete, you'll want to save it so others can use it. . In the application toolbar, click *Save*. . Enter `Tutorial web logs map` for the title. -. Click *Confirm Save*. +. Click *Save*. + You have completed the steps for re-creating the sample data map. diff --git a/docs/redirects.asciidoc b/docs/redirects.asciidoc index af67ff70c81b5..920c448acf6db 100644 --- a/docs/redirects.asciidoc +++ b/docs/redirects.asciidoc @@ -38,4 +38,10 @@ This page has moved. Please see <>. [role="exclude",id="extend"] == Extend your use case -This page was deleted. See <> and <>. \ No newline at end of file +This page was deleted. See <> and <>. + +[role="exclude",id="xpack-dashboard-only-mode"] +== Dashboard-only mode + +Using the `kibana_dashboard_only_user` role is deprecated. +Use <> instead. diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 0f17ffcf26930..f4434ea7a09f4 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -32,7 +32,8 @@ strongly recommend that you keep the default CSP rules that ship with Kibana. `csp.strict:`:: *Default: `true`* Blocks access to Kibana to any browser that does not enforce even rudimentary CSP rules. In practice, this will disable -support for older, less safe browsers like Internet Explorer. +support for older, less safe browsers like Internet Explorer. +See <> for more information. `csp.warnLegacyBrowsers:`:: *Default: `true`* Shows a warning message after loading Kibana to any browser that does not enforce even rudimentary CSP rules, @@ -319,6 +320,18 @@ supported protocols with versions. Valid protocols: `TLSv1`, `TLSv1.1`, `TLSv1.2 setting this to `true` enables unauthenticated users to access the Kibana server status API and status page. +`telemetry.allowChangingOptInStatus`:: *Default: true*. If `true`, +users are able to change the telemetry setting at a later time in +<>. If `false`, +{kib} looks at the value of `telemetry.optIn` to determine whether to send +telemetry data or not. `telemetry.allowChangingOptInStatus` and `telemetry.optIn` +cannot be `false` at the same time. + +`telemetry.optIn`:: *Default: true* If `true`, telemetry data is sent to Elastic. + If `false`, collection of telemetry data is disabled. + To enable telemetry and prevent users from disabling it, + set `telemetry.allowChangingOptInStatus` to `false` and `telemetry.optIn` to `true`. + `vega.enableExternalUrls:`:: *Default: false* Set this value to true to allow Vega to use any URL to access external data sources and images. If false, Vega can only get data from Elasticsearch. `xpack.license_management.enabled`:: *Default: true* Set this value to false to diff --git a/docs/setup/upgrade.asciidoc b/docs/setup/upgrade.asciidoc index ce0259c690b82..8c03032bb8ac3 100644 --- a/docs/setup/upgrade.asciidoc +++ b/docs/setup/upgrade.asciidoc @@ -2,7 +2,15 @@ == Upgrading {kib} Depending on the {kib} version you're upgrading from, the upgrade process to 7.0 -varies. +varies. + +NOTE: {kib} upgrades automatically when starting a new version, as described in +<>. +Although you do not need to manually back up {kib} before upgrading, we recommend +that you have a backup on hand. You can use +<> to back up {kib} +data by targeting `.kibana*` indices. If you are using the Reporting plugin, +you can also target `.reporting*` indices. [float] [[upgrade-before-you-begin]] @@ -12,7 +20,7 @@ Before you upgrade {kib}: * Consult the <>. * Before you upgrade production servers, test the upgrades in a dev environment. -* Backup your data with {es} {ref}/modules-snapshots.html[snapshots]. +* Back up your data with {es} {ref}/modules-snapshots.html[snapshots]. To roll back to an earlier version, you **must** have a backup of your data. * If you are using custom plugins, check that a compatible version is available. diff --git a/docs/user/dashboard.asciidoc b/docs/user/dashboard.asciidoc index 3f9d8fa17bddd..2923e42c1ab34 100644 --- a/docs/user/dashboard.asciidoc +++ b/docs/user/dashboard.asciidoc @@ -154,7 +154,6 @@ To open an element for editing, put the dashboard in *Edit* mode, and then select *Edit* from the panel menu. The changes you make appear in every dashboard that uses the element. -include::{kib-repo-dir}/management/dashboard_only_mode/index.asciidoc[] diff --git a/docs/user/graph/configuring-graph.asciidoc b/docs/user/graph/configuring-graph.asciidoc index d521f9d8d2846..5427bdee79ecb 100644 --- a/docs/user/graph/configuring-graph.asciidoc +++ b/docs/user/graph/configuring-graph.asciidoc @@ -59,14 +59,14 @@ image::user/graph/images/graph-read-only-badge.png[Example of Graph's read only [discrete] [[disable-drill-down]] -=== Disabling drill down configuration +=== Disabling drilldown configuration -By default, users can configure _drill down_ URLs to display additional +By default, users can configure _drilldown_ URLs to display additional information about a selected vertex in a new browser window. For example, -you could configure a drill down URL to perform a web search for the selected +you could configure a drilldown URL to perform a web search for the selected vertex term. -To prevent users from adding drill down URLs, set +To prevent users from adding drilldown URLs, set `xpack.graph.canEditDrillDownUrls` to `false` in `kibana.yml`: [source,yaml] diff --git a/docs/user/graph/getting-started.asciidoc b/docs/user/graph/getting-started.asciidoc index 19f3df341338e..7b3bd10147966 100644 --- a/docs/user/graph/getting-started.asciidoc +++ b/docs/user/graph/getting-started.asciidoc @@ -2,30 +2,30 @@ [[graph-getting-started]] == Using Graph -Graph is automatically enabled in {es} and {kib}. +You must index data into {es} before you can create a graph. +<> or get started with a <>. +[float] [[exploring-connections]] -To start exploring connections in your data: +=== Graph connections in your data -. From the side navigation, open the graph explorer. - -. Select an index pattern to specify what indices you want to explore. +. From the side navigation, open *Graph*. + -For example, if you are indexing log data with Logstash, you could select the -`logstash-*` index pattern to visualize connections within the log entries. +If this is your first graph, follow the prompts to create it. +For subsequent graphs, click *New*. + +. Select a data source to explore. -. Select one or more multi-value fields that contain the terms you want to +. Add one or more multi-value fields that contain the terms you want to graph. + -The vertices in the graph are selected from these terms. If you're -visualizing connections between Apache log entries, you could select the -`url.raw` field and the `geo.src` field so you can look at which pages are -being accessed from different locations. +The vertices in the graph are selected from these terms. . Enter a search query to discover relationships between terms in the selected fields. + -For example, to generate a graph of the successful requests to +For example, if you are using the {kib} sample web logs data set, and you want +to generate a graph of the successful requests to particular pages from different locations, you could search for the 200 response code. The weight of the connection between two vertices indicates how strongly they are related. @@ -38,25 +38,86 @@ image::user/graph/images/graph-url-connections.png["URL connections"] [role="screenshot"] image::user/graph/images/graph-link-summary.png["Link summary"] -. Use the toolbar buttons to explore +. Use the control bar on the right to explore additional connections: + -* To display additional vertices that connect to your graph, click Expand -image:user/graph/images/graph-expand-button.jpg[Expand Selection]. +* To display additional vertices that connect to your graph, click the expand icon +image:user/graph/images/graph-expand-button.png[Expand Selection]. * To display additional -connections between the displayed vertices, click Link -image:user/graph/images/graph-link-button.jpg[Add links to existing terms] +connections between the displayed vertices, click the link icon +image:user/graph/images/graph-link-button.png[Add links to existing terms]. * To explore a particular area of the -graph, select the vertices you are interested in, and click Expand or Link. -* To step back through your changes to the graph, click Undo -image:user/graph/images/graph-undo-button.jpg[Undo]. +graph, select the vertices you are interested in, and then click expand or link. +* To step back through your changes to the graph, click undo +image:user/graph/images/graph-undo-button.png[Undo] and redo +image:user/graph/images/graph-redo-button.png[Redo]. . To see more relationships in your data, submit additional queries. + [role="screenshot"] image::user/graph/images/graph-add-query.png["Adding networks"] +. *Save* your graph. + +[float] +[[style-vertex-properties]] +=== Style vertex properties + +Each vertex has a color, icon, and label. To change +the color or icon of all vertices +of a certain field, click the field badge below the search bar, and then +select *Edit settings*. + +To change the color and label of selected vertices, +click the style icon image:user/graph/images/graph-style-button.png[Style] +in the control bar on the right. + + +[float] +[[edit-graph-settings]] +=== Edit graph settings + +By default, *Graph* is configured to tune out noise in your data. +If this isn't a good fit for your data, use *Settings > Advanced settings* +to adjust the way *Graph* queries your data. You can tune the graph to show +only the results relevant to you and to improve performance. +For more information, see <>. + +You can configure the number of vertices that a search or +expand operation adds to the graph. +By default, only the five most relevant terms for any given field are added +at a time. This keeps the graph from overflowing. To increase this number, click +a field below the search bar, select *Edit Settings*, and change *Terms per hop*. + +[float] +[[graph-block-terms]] +=== Block terms from the graph +Documents that match a blocked term are not allowed in the graph. +To block a term, select its vertex and click +the block icon +image:user/graph/images/graph-block-button.png[Block selection] +in the control panel. +For a list of blocked terms, go to *Settings > Blocked terms*. + +[float] +[[graph-drill-down]] +=== Drill down into raw documents +With drilldowns, you can display additional information about a +selected vertex in a new browser window. For example, you might +configure a drilldown URL to perform a web search for the selected vertex term. + +Use the drilldown icon image:user/graph/images/graph-info-icon.png[Drilldown selection] +in the control panel to show the drilldown buttons for the selected vertices. +To configure drilldowns, go to *Settings > Drilldowns*. See also +<>. -NOTE: By default, when you submit a search query, Graph searches all available -fields. You can constrain your search to a particular field using the Lucene -query syntax. For example, `machine.os: osx`. +[float] +[[graph-run-layout]] +=== Run and pause layout +Graph uses a "force layout", where vertices behave like magnets, +pushing off of one another. By default, when you add a new vertex to +the graph, all vertices begin moving. In some cases, the movement might +go on for some time. To freeze the current vertex position, +click the pause icon +image:user/graph/images/graph-pause-button.png[Block selection] +in the control panel. diff --git a/docs/user/graph/images/graph-add-query.png b/docs/user/graph/images/graph-add-query.png old mode 100644 new mode 100755 index 7f0cea7fb3aa4..0b978462ae75e Binary files a/docs/user/graph/images/graph-add-query.png and b/docs/user/graph/images/graph-add-query.png differ diff --git a/docs/user/graph/images/graph-block-button.png b/docs/user/graph/images/graph-block-button.png new file mode 100755 index 0000000000000..b378883572f3c Binary files /dev/null and b/docs/user/graph/images/graph-block-button.png differ diff --git a/docs/user/graph/images/graph-expand-button.jpg b/docs/user/graph/images/graph-expand-button.jpg deleted file mode 100644 index 296ba9d25ad5e..0000000000000 Binary files a/docs/user/graph/images/graph-expand-button.jpg and /dev/null differ diff --git a/docs/user/graph/images/graph-expand-button.png b/docs/user/graph/images/graph-expand-button.png new file mode 100755 index 0000000000000..7d9816a8adb93 Binary files /dev/null and b/docs/user/graph/images/graph-expand-button.png differ diff --git a/docs/user/graph/images/graph-info-icon.png b/docs/user/graph/images/graph-info-icon.png new file mode 100644 index 0000000000000..c1e89384b738c Binary files /dev/null and b/docs/user/graph/images/graph-info-icon.png differ diff --git a/docs/user/graph/images/graph-link-button.jpg b/docs/user/graph/images/graph-link-button.jpg deleted file mode 100644 index 7c7f177011576..0000000000000 Binary files a/docs/user/graph/images/graph-link-button.jpg and /dev/null differ diff --git a/docs/user/graph/images/graph-link-button.png b/docs/user/graph/images/graph-link-button.png new file mode 100755 index 0000000000000..43ac85537b9bb Binary files /dev/null and b/docs/user/graph/images/graph-link-button.png differ diff --git a/docs/user/graph/images/graph-link-summary.png b/docs/user/graph/images/graph-link-summary.png old mode 100644 new mode 100755 index e669a2d79e0e3..ca07f350f975e Binary files a/docs/user/graph/images/graph-link-summary.png and b/docs/user/graph/images/graph-link-summary.png differ diff --git a/docs/user/graph/images/graph-pause-button.png b/docs/user/graph/images/graph-pause-button.png new file mode 100755 index 0000000000000..e05418d7df7f9 Binary files /dev/null and b/docs/user/graph/images/graph-pause-button.png differ diff --git a/docs/user/graph/images/graph-redo-button.png b/docs/user/graph/images/graph-redo-button.png new file mode 100755 index 0000000000000..5adcfa478652e Binary files /dev/null and b/docs/user/graph/images/graph-redo-button.png differ diff --git a/docs/user/graph/images/graph-style-button.png b/docs/user/graph/images/graph-style-button.png new file mode 100644 index 0000000000000..5015e39b8095a Binary files /dev/null and b/docs/user/graph/images/graph-style-button.png differ diff --git a/docs/user/graph/images/graph-undo-button.jpg b/docs/user/graph/images/graph-undo-button.jpg deleted file mode 100644 index 5c87a2809a480..0000000000000 Binary files a/docs/user/graph/images/graph-undo-button.jpg and /dev/null differ diff --git a/docs/user/graph/images/graph-undo-button.png b/docs/user/graph/images/graph-undo-button.png new file mode 100755 index 0000000000000..e8a72b8b358c5 Binary files /dev/null and b/docs/user/graph/images/graph-undo-button.png differ diff --git a/docs/user/graph/images/graph-url-connections.png b/docs/user/graph/images/graph-url-connections.png old mode 100644 new mode 100755 index 737c081cd691b..94c9c4341d9b8 Binary files a/docs/user/graph/images/graph-url-connections.png and b/docs/user/graph/images/graph-url-connections.png differ diff --git a/docs/user/graph/troubleshooting.asciidoc b/docs/user/graph/troubleshooting.asciidoc index ff3568ed41afa..4ce287396f809 100644 --- a/docs/user/graph/troubleshooting.asciidoc +++ b/docs/user/graph/troubleshooting.asciidoc @@ -1,6 +1,6 @@ [role="xpack"] [[graph-troubleshooting]] -== Graph Troubleshooting +== Graph troubleshooting ++++ Troubleshooting ++++ diff --git a/docs/user/monitoring/monitoring-troubleshooting.asciidoc b/docs/user/monitoring/monitoring-troubleshooting.asciidoc index 12a775ad047b0..7e1d6f94f15fa 100644 --- a/docs/user/monitoring/monitoring-troubleshooting.asciidoc +++ b/docs/user/monitoring/monitoring-troubleshooting.asciidoc @@ -23,6 +23,23 @@ You cannot monitor a version 6.3 or later cluster from {kib} version 6.2 or earl To resolve this issue, upgrade {kib} to 6.3 or later. See {stack-ref}/upgrading-elastic-stack.html[Upgrading the {stack}]. +[float] +=== {filebeat} index is corrupt + +*Symptoms:* + +The *Stack Monitoring* application displays a Monitoring Request error indicating +that an illegal argument exception has occurred because fielddata is disabled on +text fields by default. + +*Resolution* + + . Stop all your {filebeat} instances. + . Delete indices beginning with `filebeat-$VERSION`, where `VERSION` corresponds + to the version of {filebeat} running. +. Restart all your {filebeat} instances. + + [float] === No monitoring data is visible in {kib} diff --git a/package.json b/package.json index cf9158c3a59b8..c0e2acdb578c4 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "**/@types/react": "16.8.3", "**/@types/hapi": "^17.0.18", "**/@types/angular": "^1.6.56", - "**/typescript": "3.5.3", + "**/typescript": "3.7.2", "**/graphql-toolkit/lodash": "^4.17.13", "**/isomorphic-git/**/base64-js": "^1.2.1", "**/image-diff/gm/debug": "^2.6.9" @@ -109,7 +109,7 @@ "@elastic/charts": "^14.0.0", "@elastic/datemath": "5.0.2", "@elastic/ems-client": "1.0.5", - "@elastic/eui": "14.8.0", + "@elastic/eui": "14.9.0", "@elastic/filesaver": "1.1.2", "@elastic/good": "8.1.1-kibana2", "@elastic/numeral": "2.3.3", @@ -346,8 +346,8 @@ "@types/uuid": "^3.4.4", "@types/vinyl-fs": "^2.4.11", "@types/zen-observable": "^0.8.0", - "@typescript-eslint/eslint-plugin": "^2.5.0", - "@typescript-eslint/parser": "^2.5.0", + "@typescript-eslint/eslint-plugin": "^2.7.0", + "@typescript-eslint/parser": "^2.7.0", "angular-mocks": "^1.7.8", "archiver": "^3.1.1", "axe-core": "^3.3.2", @@ -433,7 +433,7 @@ "pngjs": "^3.4.0", "postcss": "^7.0.5", "postcss-url": "^8.0.0", - "prettier": "^1.18.2", + "prettier": "^1.19.1", "proxyquire": "1.8.0", "regenerate": "^1.4.0", "sass-lint": "^1.12.1", @@ -444,7 +444,7 @@ "supertest": "^3.1.0", "supertest-as-promised": "^4.0.2", "tree-kill": "^1.2.1", - "typescript": "3.5.3", + "typescript": "3.7.2", "typings-tester": "^0.3.2", "vinyl-fs": "^3.0.3", "xml2js": "^0.4.22", diff --git a/packages/eslint-config-kibana/package.json b/packages/eslint-config-kibana/package.json index b5079a49c8385..c67629f058d5a 100644 --- a/packages/eslint-config-kibana/package.json +++ b/packages/eslint-config-kibana/package.json @@ -15,8 +15,8 @@ }, "homepage": "https://github.com/elastic/eslint-config-kibana#readme", "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^2.5.0", - "@typescript-eslint/parser": "^2.5.0", + "@typescript-eslint/eslint-plugin": "^2.7.0", + "@typescript-eslint/parser": "^2.7.0", "babel-eslint": "^10.0.3", "eslint": "^6.5.1", "eslint-plugin-babel": "^5.3.0", diff --git a/packages/kbn-analytics/package.json b/packages/kbn-analytics/package.json index b0ac86b465a62..f59fbf4720835 100644 --- a/packages/kbn-analytics/package.json +++ b/packages/kbn-analytics/package.json @@ -17,6 +17,6 @@ "@babel/cli": "7.5.5", "@kbn/dev-utils": "1.0.0", "@kbn/babel-preset": "1.0.0", - "typescript": "3.5.3" + "typescript": "3.7.2" } } diff --git a/packages/kbn-config-schema/package.json b/packages/kbn-config-schema/package.json index 4880fb4ebfdee..71c0ae4bff1f9 100644 --- a/packages/kbn-config-schema/package.json +++ b/packages/kbn-config-schema/package.json @@ -10,7 +10,7 @@ "kbn:bootstrap": "yarn build" }, "devDependencies": { - "typescript": "3.5.3" + "typescript": "3.7.2" }, "peerDependencies": { "joi": "^13.5.2", diff --git a/packages/kbn-config-schema/src/types/__snapshots__/uri_type.test.ts.snap b/packages/kbn-config-schema/src/types/__snapshots__/uri_type.test.ts.snap index 2836c80f5b5ad..81fafdb4a7b33 100644 --- a/packages/kbn-config-schema/src/types/__snapshots__/uri_type.test.ts.snap +++ b/packages/kbn-config-schema/src/types/__snapshots__/uri_type.test.ts.snap @@ -1,8 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`#scheme returns error when shorter string 1`] = `"expected URI with scheme [http|https] but but got [ftp://elastic.co]."`; +exports[`#scheme returns error when shorter string 1`] = `"expected URI with scheme [http|https] but got [ftp://elastic.co]."`; -exports[`#scheme returns error when shorter string 2`] = `"expected URI with scheme [http|https] but but got [file:///kibana.log]."`; +exports[`#scheme returns error when shorter string 2`] = `"expected URI with scheme [http|https] but got [file:///kibana.log]."`; exports[`#validate throws when returns string 1`] = `"validator failure"`; diff --git a/packages/kbn-config-schema/src/types/uri_type.ts b/packages/kbn-config-schema/src/types/uri_type.ts index f283554de527e..df1ce9e869d3b 100644 --- a/packages/kbn-config-schema/src/types/uri_type.ts +++ b/packages/kbn-config-schema/src/types/uri_type.ts @@ -36,7 +36,7 @@ export class URIType extends Type { case 'string.base': return `expected value of type [string] but got [${typeDetect(value)}].`; case 'string.uriCustomScheme': - return `expected URI with scheme [${scheme}] but but got [${value}].`; + return `expected URI with scheme [${scheme}] but got [${value}].`; case 'string.uri': return `value is [${value}] but it must be a valid URI (see RFC 3986).`; } diff --git a/packages/kbn-dev-utils/package.json b/packages/kbn-dev-utils/package.json index e8781f6d901d9..09753afeb120f 100644 --- a/packages/kbn-dev-utils/package.json +++ b/packages/kbn-dev-utils/package.json @@ -21,7 +21,7 @@ "tslib": "^1.9.3" }, "devDependencies": { - "typescript": "3.5.3", + "typescript": "3.7.2", "@kbn/expect": "1.0.0", "chance": "1.0.18" } diff --git a/packages/kbn-dev-utils/src/proc_runner/observe_readable.ts b/packages/kbn-dev-utils/src/proc_runner/observe_readable.ts index 8feab74d36321..1a292aff303af 100644 --- a/packages/kbn-dev-utils/src/proc_runner/observe_readable.ts +++ b/packages/kbn-dev-utils/src/proc_runner/observe_readable.ts @@ -29,10 +29,7 @@ import { first, ignoreElements, mergeMap } from 'rxjs/operators'; */ export function observeReadable(readable: Readable): Rx.Observable { return Rx.race( - Rx.fromEvent(readable, 'end').pipe( - first(), - ignoreElements() - ), + Rx.fromEvent(readable, 'end').pipe(first(), ignoreElements()), Rx.fromEvent(readable, 'error').pipe( first(), diff --git a/packages/kbn-dev-utils/src/tooling_log/tooling_log.test.ts b/packages/kbn-dev-utils/src/tooling_log/tooling_log.test.ts index 259bfd782d3fb..21f02325cac66 100644 --- a/packages/kbn-dev-utils/src/tooling_log/tooling_log.test.ts +++ b/packages/kbn-dev-utils/src/tooling_log/tooling_log.test.ts @@ -112,10 +112,7 @@ describe('#getWritten$()', () => { const done$ = new Rx.Subject(); const promise = log .getWritten$() - .pipe( - takeUntil(done$), - toArray() - ) + .pipe(takeUntil(done$), toArray()) .toPromise(); log.debug('foo'); diff --git a/packages/kbn-elastic-idx/package.json b/packages/kbn-elastic-idx/package.json index abfaea75357dd..9532983942d6b 100644 --- a/packages/kbn-elastic-idx/package.json +++ b/packages/kbn-elastic-idx/package.json @@ -20,7 +20,7 @@ "@babel/core": "^7.5.5", "@babel/plugin-transform-async-to-generator": "^7.5.0", "jest": "^24.9.0", - "typescript": "3.5.3" + "typescript": "3.7.2" }, "jest": { "testEnvironment": "node" diff --git a/packages/kbn-i18n/package.json b/packages/kbn-i18n/package.json index 8a88626bffbe8..3e25ceb8714df 100644 --- a/packages/kbn-i18n/package.json +++ b/packages/kbn-i18n/package.json @@ -21,7 +21,7 @@ "del": "^5.1.0", "getopts": "^2.2.4", "supports-color": "^7.0.0", - "typescript": "3.5.3" + "typescript": "3.7.2" }, "dependencies": { "intl-format-cache": "^2.1.0", diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index 34a56615ed43a..2f9b177be6532 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -50,7 +50,7 @@ "log-symbols": "^2.2.0", "ncp": "^2.0.0", "ora": "^1.4.0", - "prettier": "^1.18.2", + "prettier": "^1.19.1", "read-pkg": "^5.2.0", "rxjs": "^6.5.3", "spawn-sync": "^1.0.15", @@ -58,7 +58,7 @@ "strip-ansi": "^4.0.0", "strong-log-transformer": "^2.1.0", "tempy": "^0.3.0", - "typescript": "3.5.3", + "typescript": "3.7.2", "unlazy-loader": "^0.1.3", "webpack": "^4.41.0", "webpack-cli": "^3.3.9", diff --git a/packages/kbn-pm/src/commands/bootstrap.test.ts b/packages/kbn-pm/src/commands/bootstrap.test.ts index b6d9a540ac940..b36246d97c1ad 100644 --- a/packages/kbn-pm/src/commands/bootstrap.test.ts +++ b/packages/kbn-pm/src/commands/bootstrap.test.ts @@ -101,7 +101,12 @@ test('handles dependencies of dependencies', async () => { 'packages/baz' ); - const projects = new Map([['kibana', kibana], ['foo', foo], ['bar', bar], ['baz', baz]]); + const projects = new Map([ + ['kibana', kibana], + ['foo', foo], + ['bar', bar], + ['baz', baz], + ]); const projectGraph = buildProjectGraph(projects); const logMock = jest.spyOn(console, 'log').mockImplementation(noop); @@ -133,7 +138,10 @@ test('does not run installer if no deps in package', async () => { 'packages/bar' ); - const projects = new Map([['kibana', kibana], ['bar', bar]]); + const projects = new Map([ + ['kibana', kibana], + ['bar', bar], + ]); const projectGraph = buildProjectGraph(projects); const logMock = jest.spyOn(console, 'log').mockImplementation(noop); @@ -193,7 +201,10 @@ test('calls "kbn:bootstrap" scripts and links executables after installing deps' 'packages/bar' ); - const projects = new Map([['kibana', kibana], ['bar', bar]]); + const projects = new Map([ + ['kibana', kibana], + ['bar', bar], + ]); const projectGraph = buildProjectGraph(projects); jest.spyOn(console, 'log').mockImplementation(noop); diff --git a/packages/kbn-spec-to-console/package.json b/packages/kbn-spec-to-console/package.json index 2e5f897894a90..a6b3e8f96f7db 100644 --- a/packages/kbn-spec-to-console/package.json +++ b/packages/kbn-spec-to-console/package.json @@ -18,7 +18,7 @@ "homepage": "https://github.com/jbudz/spec-to-console#readme", "devDependencies": { "jest": "^24.9.0", - "prettier": "^1.18.2" + "prettier": "^1.19.1" }, "dependencies": { "commander": "^3.0.0", diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md index c26a383719fa1..366a5b65fbb99 100644 --- a/src/core/MIGRATION.md +++ b/src/core/MIGRATION.md @@ -1130,7 +1130,7 @@ import { setup, start } from '../core_plugins/visualizations/public/legacy'; | ------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `import 'ui/apply_filters'` | `import { ApplyFiltersPopover } from '../data/public'` | `import '../data/public/legacy` should be called to load legacy directives | | `import 'ui/filter_bar'` | `import { FilterBar } from '../data/public'` | `import '../data/public/legacy` should be called to load legacy directives | -| `import 'ui/query_bar'` | `import { QueryBar, QueryBarInput } from '../data/public'` | Directives are deprecated. | +| `import 'ui/query_bar'` | `import { QueryBarInput } from '../data/public'` | Directives are deprecated. | | `import 'ui/search_bar'` | `import { SearchBar } from '../data/public'` | Directive is deprecated. | | `import 'ui/kbn_top_nav'` | `import { TopNavMenu } from '../navigation/public'` | Directive is still available in `ui/kbn_top_nav`. | | `ui/saved_objects/components/saved_object_finder` | `import { SavedObjectFinder } from '../kibana_react/public'` | | diff --git a/src/core/public/application/types.ts b/src/core/public/application/types.ts index 5b1d4affe8840..5be22ea151c32 100644 --- a/src/core/public/application/types.ts +++ b/src/core/public/application/types.ts @@ -80,6 +80,12 @@ export interface App extends AppBase { * @returns An unmounting function that will be called to unmount the application. */ mount: (context: AppMountContext, params: AppMountParameters) => AppUnmount | Promise; + + /** + * Hide the UI chrome when the application is mounted. Defaults to `false`. + * Takes precedence over chrome service visibility settings. + */ + chromeless?: boolean; } /** @internal */ @@ -145,12 +151,13 @@ export interface AppMountParameters { * export class MyPlugin implements Plugin { * setup({ application }) { * application.register({ - * id: 'my-app', - * async mount(context, params) { - * const { renderApp } = await import('./application'); - * return renderApp(context, params); - * }, - * }); + * id: 'my-app', + * async mount(context, params) { + * const { renderApp } = await import('./application'); + * return renderApp(context, params); + * }, + * }); + * } * } * ``` * diff --git a/src/core/public/chrome/chrome_service.test.ts b/src/core/public/chrome/chrome_service.test.ts index 45e94040eeb4a..3390480e56bdd 100644 --- a/src/core/public/chrome/chrome_service.test.ts +++ b/src/core/public/chrome/chrome_service.test.ts @@ -26,351 +26,423 @@ import { applicationServiceMock } from '../application/application_service.mock' import { httpServiceMock } from '../http/http_service.mock'; import { injectedMetadataServiceMock } from '../injected_metadata/injected_metadata_service.mock'; import { notificationServiceMock } from '../notifications/notifications_service.mock'; -import { ChromeService } from './chrome_service'; import { docLinksServiceMock } from '../doc_links/doc_links_service.mock'; +import { ChromeService } from './chrome_service'; +import { App } from '../application'; +class FakeApp implements App { + public title = `${this.id} App`; + public mount = () => () => {}; + constructor(public id: string, public chromeless?: boolean) {} +} const store = new Map(); +const originalLocalStorage = window.localStorage; + (window as any).localStorage = { setItem: (key: string, value: string) => store.set(String(key), String(value)), getItem: (key: string) => store.get(String(key)), removeItem: (key: string) => store.delete(String(key)), }; -function defaultStartDeps() { - return { +function defaultStartDeps(availableApps?: App[]) { + const deps = { application: applicationServiceMock.createInternalStartContract(), docLinks: docLinksServiceMock.createStartContract(), http: httpServiceMock.createStartContract(), injectedMetadata: injectedMetadataServiceMock.createStartContract(), notifications: notificationServiceMock.createStartContract(), }; + + if (availableApps) { + deps.application.availableApps = new Map(availableApps.map(app => [app.id, app])); + } + + return deps; +} + +async function start({ + options = { browserSupportsCsp: true }, + cspConfigMock = { warnLegacyBrowsers: true }, + startDeps = defaultStartDeps(), +}: { options?: any; cspConfigMock?: any; startDeps?: ReturnType } = {}) { + const service = new ChromeService(options); + + if (cspConfigMock) { + startDeps.injectedMetadata.getCspConfig.mockReturnValue(cspConfigMock); + } + + return { + service, + startDeps, + chrome: await service.start(startDeps), + }; } beforeEach(() => { store.clear(); + window.history.pushState(undefined, '', '#/home?a=b'); +}); + +afterAll(() => { + (window as any).localStorage = originalLocalStorage; }); describe('start', () => { it('adds legacy browser warning if browserSupportsCsp is disabled and warnLegacyBrowsers is enabled', async () => { - const service = new ChromeService({ browserSupportsCsp: false }); - const startDeps = defaultStartDeps(); - startDeps.injectedMetadata.getCspConfig.mockReturnValue({ warnLegacyBrowsers: true }); - await service.start(startDeps); + const { startDeps } = await start({ options: { browserSupportsCsp: false } }); + expect(startDeps.notifications.toasts.addWarning.mock.calls).toMatchInlineSnapshot(` -Array [ - Array [ - "Your browser does not meet the security requirements for Kibana.", - ], -] -`); + Array [ + Array [ + "Your browser does not meet the security requirements for Kibana.", + ], + ] + `); }); it('does not add legacy browser warning if browser supports CSP', async () => { - const service = new ChromeService({ browserSupportsCsp: true }); - const startDeps = defaultStartDeps(); - startDeps.injectedMetadata.getCspConfig.mockReturnValue({ warnLegacyBrowsers: true }); - await service.start(startDeps); + const { startDeps } = await start(); + expect(startDeps.notifications.toasts.addWarning).not.toBeCalled(); }); it('does not add legacy browser warning if warnLegacyBrowsers is disabled', async () => { - const service = new ChromeService({ browserSupportsCsp: false }); - const startDeps = defaultStartDeps(); - startDeps.injectedMetadata.getCspConfig.mockReturnValue({ warnLegacyBrowsers: false }); - await service.start(startDeps); + const { startDeps } = await start({ + options: { browserSupportsCsp: false }, + cspConfigMock: { warnLegacyBrowsers: false }, + }); + expect(startDeps.notifications.toasts.addWarning).not.toBeCalled(); }); describe('getComponent', () => { it('returns a renderable React component', async () => { - const service = new ChromeService({ browserSupportsCsp: true }); - const start = await service.start(defaultStartDeps()); + const { chrome } = await start(); + // Have to do some fanagling to get the type system and enzyme to accept this. // Don't capture the snapshot because it's 600+ lines long. - expect(shallow(React.createElement(() => start.getHeaderComponent()))).toBeDefined(); + expect(shallow(React.createElement(() => chrome.getHeaderComponent()))).toBeDefined(); }); }); describe('brand', () => { it('updates/emits the brand as it changes', async () => { - const service = new ChromeService({ browserSupportsCsp: true }); - const start = await service.start(defaultStartDeps()); - const promise = start + const { chrome, service } = await start(); + const promise = chrome .getBrand$() .pipe(toArray()) .toPromise(); - start.setBrand({ + chrome.setBrand({ logo: 'big logo', smallLogo: 'not so big logo', }); - start.setBrand({ + chrome.setBrand({ logo: 'big logo without small logo', }); service.stop(); await expect(promise).resolves.toMatchInlineSnapshot(` -Array [ - Object {}, - Object { - "logo": "big logo", - "smallLogo": "not so big logo", - }, - Object { - "logo": "big logo without small logo", - "smallLogo": undefined, - }, -] -`); + Array [ + Object {}, + Object { + "logo": "big logo", + "smallLogo": "not so big logo", + }, + Object { + "logo": "big logo without small logo", + "smallLogo": undefined, + }, + ] + `); }); }); describe('visibility', () => { it('updates/emits the visibility', async () => { - const service = new ChromeService({ browserSupportsCsp: true }); - const start = await service.start(defaultStartDeps()); - const promise = start + const { chrome, service } = await start(); + const promise = chrome .getIsVisible$() .pipe(toArray()) .toPromise(); - start.setIsVisible(true); - start.setIsVisible(false); - start.setIsVisible(true); + chrome.setIsVisible(true); + chrome.setIsVisible(false); + chrome.setIsVisible(true); service.stop(); await expect(promise).resolves.toMatchInlineSnapshot(` -Array [ - true, - true, - false, - true, -] -`); + Array [ + true, + true, + false, + true, + ] + `); }); - it('always emits false if embed query string is in hash when set up', async () => { + it('always emits false if embed query string is preset when set up', async () => { window.history.pushState(undefined, '', '#/home?a=b&embed=true'); - const service = new ChromeService({ browserSupportsCsp: true }); - const start = await service.start(defaultStartDeps()); - const promise = start + const { chrome, service } = await start(); + const promise = chrome + .getIsVisible$() + .pipe(toArray()) + .toPromise(); + + chrome.setIsVisible(true); + chrome.setIsVisible(false); + chrome.setIsVisible(true); + service.stop(); + + await expect(promise).resolves.toMatchInlineSnapshot(` + Array [ + false, + false, + false, + false, + ] + `); + }); + + it('application-specified visibility on mount', async () => { + const startDeps = defaultStartDeps([ + new FakeApp('alpha'), // An undefined `chromeless` is the same as setting to false. + new FakeApp('beta', true), + new FakeApp('gamma', false), + ]); + const { availableApps, currentAppId$ } = startDeps.application; + const { chrome, service } = await start({ startDeps }); + const promise = chrome + .getIsVisible$() + .pipe(toArray()) + .toPromise(); + + [...availableApps.keys()].forEach(appId => currentAppId$.next(appId)); + service.stop(); + + await expect(promise).resolves.toMatchInlineSnapshot(` + Array [ + true, + true, + false, + true, + ] + `); + }); + + it('changing visibility has no effect on chrome-hiding application', async () => { + const startDeps = defaultStartDeps([new FakeApp('alpha', true)]); + const { currentAppId$ } = startDeps.application; + const { chrome, service } = await start({ startDeps }); + const promise = chrome .getIsVisible$() .pipe(toArray()) .toPromise(); - start.setIsVisible(true); - start.setIsVisible(false); - start.setIsVisible(true); + currentAppId$.next('alpha'); + chrome.setIsVisible(true); service.stop(); await expect(promise).resolves.toMatchInlineSnapshot(` -Array [ - false, - false, - false, - false, -] -`); + Array [ + true, + false, + false, + ] + `); }); }); describe('is collapsed', () => { it('updates/emits isCollapsed', async () => { - const service = new ChromeService({ browserSupportsCsp: true }); - const start = await service.start(defaultStartDeps()); - const promise = start + const { chrome, service } = await start(); + const promise = chrome .getIsCollapsed$() .pipe(toArray()) .toPromise(); - start.setIsCollapsed(true); - start.setIsCollapsed(false); - start.setIsCollapsed(true); + chrome.setIsCollapsed(true); + chrome.setIsCollapsed(false); + chrome.setIsCollapsed(true); service.stop(); await expect(promise).resolves.toMatchInlineSnapshot(` -Array [ - false, - true, - false, - true, -] -`); + Array [ + false, + true, + false, + true, + ] + `); }); it('only stores true in localStorage', async () => { - const service = new ChromeService({ browserSupportsCsp: true }); - const start = await service.start(defaultStartDeps()); + const { chrome } = await start(); - start.setIsCollapsed(true); + chrome.setIsCollapsed(true); expect(store.size).toBe(1); - start.setIsCollapsed(false); + chrome.setIsCollapsed(false); expect(store.size).toBe(0); }); }); describe('application classes', () => { it('updates/emits the application classes', async () => { - const service = new ChromeService({ browserSupportsCsp: true }); - const start = await service.start(defaultStartDeps()); - const promise = start + const { chrome, service } = await start(); + const promise = chrome .getApplicationClasses$() .pipe(toArray()) .toPromise(); - start.addApplicationClass('foo'); - start.addApplicationClass('foo'); - start.addApplicationClass('bar'); - start.addApplicationClass('bar'); - start.addApplicationClass('baz'); - start.removeApplicationClass('bar'); - start.removeApplicationClass('foo'); + chrome.addApplicationClass('foo'); + chrome.addApplicationClass('foo'); + chrome.addApplicationClass('bar'); + chrome.addApplicationClass('bar'); + chrome.addApplicationClass('baz'); + chrome.removeApplicationClass('bar'); + chrome.removeApplicationClass('foo'); service.stop(); await expect(promise).resolves.toMatchInlineSnapshot(` -Array [ - Array [], - Array [ - "foo", - ], - Array [ - "foo", - ], - Array [ - "foo", - "bar", - ], - Array [ - "foo", - "bar", - ], - Array [ - "foo", - "bar", - "baz", - ], - Array [ - "foo", - "baz", - ], - Array [ - "baz", - ], -] -`); + Array [ + Array [], + Array [ + "foo", + ], + Array [ + "foo", + ], + Array [ + "foo", + "bar", + ], + Array [ + "foo", + "bar", + ], + Array [ + "foo", + "bar", + "baz", + ], + Array [ + "foo", + "baz", + ], + Array [ + "baz", + ], + ] + `); }); }); describe('badge', () => { it('updates/emits the current badge', async () => { - const service = new ChromeService({ browserSupportsCsp: true }); - const start = await service.start(defaultStartDeps()); - const promise = start + const { chrome, service } = await start(); + const promise = chrome .getBadge$() .pipe(toArray()) .toPromise(); - start.setBadge({ text: 'foo', tooltip: `foo's tooltip` }); - start.setBadge({ text: 'bar', tooltip: `bar's tooltip` }); - start.setBadge(undefined); + chrome.setBadge({ text: 'foo', tooltip: `foo's tooltip` }); + chrome.setBadge({ text: 'bar', tooltip: `bar's tooltip` }); + chrome.setBadge(undefined); service.stop(); await expect(promise).resolves.toMatchInlineSnapshot(` -Array [ - undefined, - Object { - "text": "foo", - "tooltip": "foo's tooltip", - }, - Object { - "text": "bar", - "tooltip": "bar's tooltip", - }, - undefined, -] -`); + Array [ + undefined, + Object { + "text": "foo", + "tooltip": "foo's tooltip", + }, + Object { + "text": "bar", + "tooltip": "bar's tooltip", + }, + undefined, + ] + `); }); }); describe('breadcrumbs', () => { it('updates/emits the current set of breadcrumbs', async () => { - const service = new ChromeService({ browserSupportsCsp: true }); - const start = await service.start(defaultStartDeps()); - const promise = start + const { chrome, service } = await start(); + const promise = chrome .getBreadcrumbs$() .pipe(toArray()) .toPromise(); - start.setBreadcrumbs([{ text: 'foo' }, { text: 'bar' }]); - start.setBreadcrumbs([{ text: 'foo' }]); - start.setBreadcrumbs([{ text: 'bar' }]); - start.setBreadcrumbs([]); + chrome.setBreadcrumbs([{ text: 'foo' }, { text: 'bar' }]); + chrome.setBreadcrumbs([{ text: 'foo' }]); + chrome.setBreadcrumbs([{ text: 'bar' }]); + chrome.setBreadcrumbs([]); service.stop(); await expect(promise).resolves.toMatchInlineSnapshot(` -Array [ - Array [], - Array [ - Object { - "text": "foo", - }, - Object { - "text": "bar", - }, - ], - Array [ - Object { - "text": "foo", - }, - ], - Array [ - Object { - "text": "bar", - }, - ], - Array [], -] -`); + Array [ + Array [], + Array [ + Object { + "text": "foo", + }, + Object { + "text": "bar", + }, + ], + Array [ + Object { + "text": "foo", + }, + ], + Array [ + Object { + "text": "bar", + }, + ], + Array [], + ] + `); }); }); describe('help extension', () => { it('updates/emits the current help extension', async () => { - const service = new ChromeService({ browserSupportsCsp: true }); - const start = await service.start(defaultStartDeps()); - const promise = start + const { chrome, service } = await start(); + const promise = chrome .getHelpExtension$() .pipe(toArray()) .toPromise(); - start.setHelpExtension(() => () => undefined); - start.setHelpExtension(undefined); + chrome.setHelpExtension(() => () => undefined); + chrome.setHelpExtension(undefined); service.stop(); await expect(promise).resolves.toMatchInlineSnapshot(` -Array [ - undefined, - [Function], - undefined, -] -`); + Array [ + undefined, + [Function], + undefined, + ] + `); }); }); }); describe('stop', () => { it('completes applicationClass$, isCollapsed$, breadcrumbs$, isVisible$, and brand$ observables', async () => { - const service = new ChromeService({ browserSupportsCsp: true }); - const start = await service.start(defaultStartDeps()); + const { chrome, service } = await start(); const promise = Rx.combineLatest( - start.getBrand$(), - start.getApplicationClasses$(), - start.getIsCollapsed$(), - start.getBreadcrumbs$(), - start.getIsVisible$(), - start.getHelpExtension$() + chrome.getBrand$(), + chrome.getApplicationClasses$(), + chrome.getIsCollapsed$(), + chrome.getBreadcrumbs$(), + chrome.getIsVisible$(), + chrome.getHelpExtension$() ).toPromise(); service.stop(); @@ -378,18 +450,17 @@ describe('stop', () => { }); it('completes immediately if service already stopped', async () => { - const service = new ChromeService({ browserSupportsCsp: true }); - const start = await service.start(defaultStartDeps()); + const { chrome, service } = await start(); service.stop(); await expect( Rx.combineLatest( - start.getBrand$(), - start.getApplicationClasses$(), - start.getIsCollapsed$(), - start.getBreadcrumbs$(), - start.getIsVisible$(), - start.getHelpExtension$() + chrome.getBrand$(), + chrome.getApplicationClasses$(), + chrome.getIsCollapsed$(), + chrome.getBreadcrumbs$(), + chrome.getIsVisible$(), + chrome.getHelpExtension$() ).toPromise() ).resolves.toBe(undefined); }); diff --git a/src/core/public/chrome/chrome_service.tsx b/src/core/public/chrome/chrome_service.tsx index a5532faec19ed..e686f03413dd5 100644 --- a/src/core/public/chrome/chrome_service.tsx +++ b/src/core/public/chrome/chrome_service.tsx @@ -18,9 +18,9 @@ */ import React from 'react'; -import { BehaviorSubject, Observable, ReplaySubject } from 'rxjs'; +import { BehaviorSubject, Observable, ReplaySubject, combineLatest, of, merge } from 'rxjs'; import { map, takeUntil } from 'rxjs/operators'; -import * as Url from 'url'; +import { parse } from 'url'; import { i18n } from '@kbn/i18n'; import { IconType, Breadcrumb as EuiBreadcrumb } from '@elastic/eui'; @@ -41,11 +41,6 @@ export { ChromeNavControls, ChromeRecentlyAccessed, ChromeDocTitle }; const IS_COLLAPSED_KEY = 'core.chrome.isCollapsed'; -function isEmbedParamInHash() { - const { query } = Url.parse(String(window.location.hash).slice(1), true); - return Boolean(query.embed); -} - /** @public */ export interface ChromeBadge { text: string; @@ -79,6 +74,9 @@ interface StartDeps { /** @internal */ export class ChromeService { + private isVisible$!: Observable; + private appHidden$!: Observable; + private toggleHidden$!: BehaviorSubject; private readonly stop$ = new ReplaySubject(1); private readonly navControls = new NavControlsService(); private readonly navLinks = new NavLinksService(); @@ -87,6 +85,38 @@ export class ChromeService { constructor(private readonly params: ConstructorParams) {} + /** + * These observables allow consumers to toggle the chrome visibility via either: + * 1. Using setIsVisible() to trigger the next chromeHidden$ + * 2. Setting `chromeless` when registering an application, which will + * reset the visibility whenever the next application is mounted + * 3. Having "embed" in the query string + */ + private initVisibility(application: StartDeps['application']) { + // Start off the chrome service hidden if "embed" is in the hash query string. + const isEmbedded = 'embed' in parse(location.hash.slice(1), true).query; + + this.toggleHidden$ = new BehaviorSubject(isEmbedded); + this.appHidden$ = merge( + // Default the app being hidden to the same value initial value as the chrome visibility + // in case the application service has not emitted an app ID yet, since we want to trigger + // combineLatest below regardless of having an application value yet. + of(isEmbedded), + application.currentAppId$.pipe( + map( + appId => + !!appId && + application.availableApps.has(appId) && + !!application.availableApps.get(appId)!.chromeless + ) + ) + ); + this.isVisible$ = combineLatest(this.appHidden$, this.toggleHidden$).pipe( + map(([appHidden, chromeHidden]) => !(appHidden || chromeHidden)), + takeUntil(this.stop$) + ); + } + public async start({ application, docLinks, @@ -94,11 +124,10 @@ export class ChromeService { injectedMetadata, notifications, }: StartDeps): Promise { - const FORCE_HIDDEN = isEmbedParamInHash(); + this.initVisibility(application); const appTitle$ = new BehaviorSubject('Kibana'); const brand$ = new BehaviorSubject({}); - const isVisible$ = new BehaviorSubject(true); const isCollapsed$ = new BehaviorSubject(!!localStorage.getItem(IS_COLLAPSED_KEY)); const applicationClasses$ = new BehaviorSubject>(new Set()); const helpExtension$ = new BehaviorSubject(undefined); @@ -139,10 +168,7 @@ export class ChromeService { forceAppSwitcherNavigation$={navLinks.getForceAppSwitcherNavigation$()} helpExtension$={helpExtension$.pipe(takeUntil(this.stop$))} homeHref={http.basePath.prepend('/app/kibana#/home')} - isVisible$={isVisible$.pipe( - map(visibility => (FORCE_HIDDEN ? false : visibility)), - takeUntil(this.stop$) - )} + isVisible$={this.isVisible$} kibanaVersion={injectedMetadata.getKibanaVersion()} legacyMode={injectedMetadata.getLegacyMode()} navLinks$={navLinks.getNavLinks$()} @@ -166,15 +192,9 @@ export class ChromeService { ); }, - getIsVisible$: () => - isVisible$.pipe( - map(visibility => (FORCE_HIDDEN ? false : visibility)), - takeUntil(this.stop$) - ), + getIsVisible$: () => this.isVisible$, - setIsVisible: (visibility: boolean) => { - isVisible$.next(visibility); - }, + setIsVisible: (isVisible: boolean) => this.toggleHidden$.next(!isVisible), getIsCollapsed$: () => isCollapsed$.pipe(takeUntil(this.stop$)), diff --git a/src/core/public/chrome/constants.ts b/src/core/public/chrome/constants.ts index 3411f6f629a13..c8e53b38c618e 100644 --- a/src/core/public/chrome/constants.ts +++ b/src/core/public/chrome/constants.ts @@ -18,6 +18,6 @@ */ export const ELASTIC_SUPPORT_LINK = 'https://support.elastic.co/'; -export const KIBANA_FEEDBACK_LINK = 'https://www.elastic.co/kibana/feedback'; -export const KIBANA_ASK_ELASTIC_LINK = 'https://www.elastic.co/kibana/ask-elastic'; +export const KIBANA_FEEDBACK_LINK = 'https://www.elastic.co/products/kibana/feedback'; +export const KIBANA_ASK_ELASTIC_LINK = 'https://www.elastic.co/products/kibana/ask-elastic'; export const GITHUB_CREATE_ISSUE_LINK = 'https://github.com/elastic/kibana/issues/new/choose'; diff --git a/src/core/public/chrome/nav_links/nav_links_service.test.ts b/src/core/public/chrome/nav_links/nav_links_service.test.ts index 8c135b3c4c49f..5a45491df28e7 100644 --- a/src/core/public/chrome/nav_links/nav_links_service.test.ts +++ b/src/core/public/chrome/nav_links/nav_links_service.test.ts @@ -19,27 +19,34 @@ import { NavLinksService } from './nav_links_service'; import { take, map, takeLast } from 'rxjs/operators'; -import { LegacyApp } from '../../application'; +import { App, LegacyApp } from '../../application'; const mockAppService = { - availableApps: new Map(), - availableLegacyApps: new Map([ - [ - 'legacyApp1', - { id: 'legacyApp1', order: 0, title: 'Legacy App 1', icon: 'legacyApp1', appUrl: '/app1' }, - ], - [ - 'legacyApp2', + availableApps: new Map( + ([ + { id: 'app1', order: 0, title: 'App 1', icon: 'app1' }, { - id: 'legacyApp2', + id: 'app2', order: -10, + title: 'App 2', + euiIconType: 'canvasApp', + }, + { id: 'chromelessApp', order: 20, title: 'Chromless App', chromeless: true }, + ] as App[]).map(app => [app.id, app]) + ), + availableLegacyApps: new Map( + ([ + { id: 'legacyApp1', order: 5, title: 'Legacy App 1', icon: 'legacyApp1', appUrl: '/app1' }, + { + id: 'legacyApp2', + order: -5, title: 'Legacy App 2', euiIconType: 'canvasApp', appUrl: '/app2', }, - ], - ['legacyApp3', { id: 'legacyApp3', order: 20, title: 'Legacy App 3', appUrl: '/app3' }], - ]), + { id: 'legacyApp3', order: 15, title: 'Legacy App 3', appUrl: '/app3' }, + ] as LegacyApp[]).map(app => [app.id, app]) + ), } as any; const mockHttp = { @@ -58,6 +65,18 @@ describe('NavLinksService', () => { }); describe('#getNavLinks$()', () => { + it('does not include `chromeless` applications', async () => { + expect( + await start + .getNavLinks$() + .pipe( + take(1), + map(links => links.map(l => l.id)) + ) + .toPromise() + ).not.toContain('chromelessApp'); + }); + it('sorts navlinks by `order` property', async () => { expect( await start @@ -67,7 +86,7 @@ describe('NavLinksService', () => { map(links => links.map(l => l.id)) ) .toPromise() - ).toEqual(['legacyApp2', 'legacyApp1', 'legacyApp3']); + ).toEqual(['app2', 'legacyApp2', 'app1', 'legacyApp1', 'legacyApp3']); }); it('emits multiple values', async () => { @@ -78,8 +97,8 @@ describe('NavLinksService', () => { service.stop(); expect(emittedLinks).toEqual([ - ['legacyApp2', 'legacyApp1', 'legacyApp3'], - ['legacyApp2', 'legacyApp1', 'legacyApp3'], + ['app2', 'legacyApp2', 'app1', 'legacyApp1', 'legacyApp3'], + ['app2', 'legacyApp2', 'app1', 'legacyApp1', 'legacyApp3'], ]); }); @@ -105,7 +124,13 @@ describe('NavLinksService', () => { describe('#getAll()', () => { it('returns a sorted array of navlinks', () => { - expect(start.getAll().map(l => l.id)).toEqual(['legacyApp2', 'legacyApp1', 'legacyApp3']); + expect(start.getAll().map(l => l.id)).toEqual([ + 'app2', + 'legacyApp2', + 'app1', + 'legacyApp1', + 'legacyApp3', + ]); }); }); @@ -130,7 +155,20 @@ describe('NavLinksService', () => { map(links => links.map(l => l.id)) ) .toPromise() - ).toEqual(['legacyApp2', 'legacyApp1', 'legacyApp3']); + ).toEqual(['app2', 'legacyApp2', 'app1', 'legacyApp1', 'legacyApp3']); + }); + + it('does nothing on chromeless applications', async () => { + start.showOnly('chromelessApp'); + expect( + await start + .getNavLinks$() + .pipe( + take(1), + map(links => links.map(l => l.id)) + ) + .toPromise() + ).toEqual(['app2', 'legacyApp2', 'app1', 'legacyApp1', 'legacyApp3']); }); it('removes all other links', async () => { @@ -157,7 +195,7 @@ describe('NavLinksService', () => { "icon": "legacyApp1", "id": "legacyApp1", "legacy": true, - "order": 0, + "order": 5, "title": "Legacy App 1", } `); diff --git a/src/core/public/chrome/nav_links/nav_links_service.ts b/src/core/public/chrome/nav_links/nav_links_service.ts index affc639faf0b8..31a729f90cd93 100644 --- a/src/core/public/chrome/nav_links/nav_links_service.ts +++ b/src/core/public/chrome/nav_links/nav_links_service.ts @@ -99,17 +99,19 @@ export class NavLinksService { private readonly stop$ = new ReplaySubject(1); public start({ application, http }: StartDeps): ChromeNavLinks { - const appLinks = [...application.availableApps].map( - ([appId, app]) => - [ - appId, - new NavLinkWrapper({ - ...app, - legacy: false, - baseUrl: relativeToAbsolute(http.basePath.prepend(`/app/${appId}`)), - }), - ] as [string, NavLinkWrapper] - ); + const appLinks = [...application.availableApps] + .filter(([, app]) => !app.chromeless) + .map( + ([appId, app]) => + [ + appId, + new NavLinkWrapper({ + ...app, + legacy: false, + baseUrl: relativeToAbsolute(http.basePath.prepend(`/app/${appId}`)), + }), + ] as [string, NavLinkWrapper] + ); const legacyAppLinks = [...application.availableLegacyApps].map( ([appId, app]) => @@ -130,10 +132,7 @@ export class NavLinksService { return { getNavLinks$: () => { - return navLinks$.pipe( - map(sortNavLinks), - takeUntil(this.stop$) - ); + return navLinks$.pipe(map(sortNavLinks), takeUntil(this.stop$)); }, get(id: string) { @@ -192,7 +191,10 @@ export class NavLinksService { } function sortNavLinks(navLinks: ReadonlyMap) { - return sortBy([...navLinks.values()].map(link => link.properties), 'order'); + return sortBy( + [...navLinks.values()].map(link => link.properties), + 'order' + ); } function relativeToAbsolute(url: string) { diff --git a/src/core/public/chrome/recently_accessed/recently_accessed_service.test.ts b/src/core/public/chrome/recently_accessed/recently_accessed_service.test.ts index cca16ddcd2a81..3c9713a93144a 100644 --- a/src/core/public/chrome/recently_accessed/recently_accessed_service.test.ts +++ b/src/core/public/chrome/recently_accessed/recently_accessed_service.test.ts @@ -106,10 +106,7 @@ describe('RecentlyAccessed#start()', () => { const stop$ = new Subject(); const observedValues$ = recentlyAccessed .get$() - .pipe( - bufferCount(3), - takeUntil(stop$) - ) + .pipe(bufferCount(3), takeUntil(stop$)) .toPromise(); recentlyAccessed.add('/app/item1', 'Item 1', 'item1'); recentlyAccessed.add('/app/item2', 'Item 2', 'item2'); diff --git a/src/core/public/core_system.test.ts b/src/core/public/core_system.test.ts index d78504a899a34..1ee41fe64418e 100644 --- a/src/core/public/core_system.test.ts +++ b/src/core/public/core_system.test.ts @@ -174,7 +174,10 @@ describe('#setup()', () => { it('injects legacy dependency to context#setup()', async () => { const pluginA = Symbol(); const pluginB = Symbol(); - const pluginDependencies = new Map([[pluginA, []], [pluginB, [pluginA]]]); + const pluginDependencies = new Map([ + [pluginA, []], + [pluginB, [pluginA]], + ]); MockPluginsService.getOpaqueIds.mockReturnValue(pluginDependencies); await setupCore(); diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 7391cf7f9454c..e040b29814900 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -117,13 +117,7 @@ export { InterceptedHttpResponse, } from './http'; -export { - OverlayStart, - OverlayBannerMount, - OverlayBannerUnmount, - OverlayBannersStart, - OverlayRef, -} from './overlays'; +export { OverlayStart, OverlayBannersStart, OverlayRef } from './overlays'; export { Toast, @@ -136,6 +130,8 @@ export { ErrorToastOptions, } from './notifications'; +export { MountPoint, UnmountCallback } from './types'; + /** * Core services exposed to the `Plugin` setup lifecycle * diff --git a/src/core/public/injected_metadata/injected_metadata_service.test.ts b/src/core/public/injected_metadata/injected_metadata_service.test.ts index ef35fd2aa78ac..1110097c1c92b 100644 --- a/src/core/public/injected_metadata/injected_metadata_service.test.ts +++ b/src/core/public/injected_metadata/injected_metadata_service.test.ts @@ -68,18 +68,27 @@ describe('setup.getPlugins()', () => { it('returns injectedMetadata.uiPlugins', () => { const injectedMetadata = new InjectedMetadataService({ injectedMetadata: { - uiPlugins: [{ id: 'plugin-1', plugin: {} }, { id: 'plugin-2', plugin: {} }], + uiPlugins: [ + { id: 'plugin-1', plugin: {} }, + { id: 'plugin-2', plugin: {} }, + ], }, } as any); const plugins = injectedMetadata.setup().getPlugins(); - expect(plugins).toEqual([{ id: 'plugin-1', plugin: {} }, { id: 'plugin-2', plugin: {} }]); + expect(plugins).toEqual([ + { id: 'plugin-1', plugin: {} }, + { id: 'plugin-2', plugin: {} }, + ]); }); it('returns frozen version of uiPlugins', () => { const injectedMetadata = new InjectedMetadataService({ injectedMetadata: { - uiPlugins: [{ id: 'plugin-1', plugin: {} }, { id: 'plugin-2', plugin: {} }], + uiPlugins: [ + { id: 'plugin-1', plugin: {} }, + { id: 'plugin-2', plugin: {} }, + ], }, } as any); diff --git a/src/core/public/notifications/toasts/__snapshots__/global_toast_list.test.tsx.snap b/src/core/public/notifications/toasts/__snapshots__/global_toast_list.test.tsx.snap index 29b289592b2ef..ca09d4a14bd7a 100644 --- a/src/core/public/notifications/toasts/__snapshots__/global_toast_list.test.tsx.snap +++ b/src/core/public/notifications/toasts/__snapshots__/global_toast_list.test.tsx.snap @@ -3,7 +3,7 @@ exports[`renders matching snapshot 1`] = ` diff --git a/src/core/public/notifications/toasts/global_toast_list.test.tsx b/src/core/public/notifications/toasts/global_toast_list.test.tsx index c6c127acbb033..61d73ac233188 100644 --- a/src/core/public/notifications/toasts/global_toast_list.test.tsx +++ b/src/core/public/notifications/toasts/global_toast_list.test.tsx @@ -57,9 +57,9 @@ it('subscribes to toasts$ on mount and unsubscribes on unmount', () => { it('passes latest value from toasts$ to ', () => { const el = shallow( render({ - toasts$: Rx.from([[], [1], [1, 2]]) as any, + toasts$: Rx.from([[], [{ id: 1 }], [{ id: 1 }, { id: 2 }]]) as any, }) ); - expect(el.find(EuiGlobalToastList).prop('toasts')).toEqual([1, 2]); + expect(el.find(EuiGlobalToastList).prop('toasts')).toEqual([{ id: 1 }, { id: 2 }]); }); diff --git a/src/core/public/notifications/toasts/global_toast_list.tsx b/src/core/public/notifications/toasts/global_toast_list.tsx index 57dc899016264..f96a0a6f362bf 100644 --- a/src/core/public/notifications/toasts/global_toast_list.tsx +++ b/src/core/public/notifications/toasts/global_toast_list.tsx @@ -17,20 +17,28 @@ * under the License. */ -import { EuiGlobalToastList, EuiGlobalToastListToast as Toast } from '@elastic/eui'; - +import { EuiGlobalToastList, EuiGlobalToastListToast as EuiToast } from '@elastic/eui'; import React from 'react'; import * as Rx from 'rxjs'; +import { MountWrapper } from '../../utils'; +import { Toast } from './toasts_api'; + interface Props { toasts$: Rx.Observable; - dismissToast: (t: Toast) => void; + dismissToast: (toastId: string) => void; } interface State { toasts: Toast[]; } +const convertToEui = (toast: Toast): EuiToast => ({ + ...toast, + title: typeof toast.title === 'function' ? : toast.title, + text: typeof toast.text === 'function' ? : toast.text, +}); + export class GlobalToastList extends React.Component { public state: State = { toasts: [], @@ -54,8 +62,8 @@ export class GlobalToastList extends React.Component { return ( this.props.dismissToast(id)} /** * This prop is overriden by the individual toasts that are added. * Use `Infinity` here so that it's obvious a timeout hasn't been diff --git a/src/core/public/notifications/toasts/index.ts b/src/core/public/notifications/toasts/index.ts index 83c2d52f3d77a..6e9de11683364 100644 --- a/src/core/public/notifications/toasts/index.ts +++ b/src/core/public/notifications/toasts/index.ts @@ -18,5 +18,11 @@ */ export { ToastsService, ToastsSetup, ToastsStart } from './toasts_service'; -export { ErrorToastOptions, ToastsApi, ToastInput, IToasts, ToastInputFields } from './toasts_api'; -export { EuiGlobalToastListToast as Toast } from '@elastic/eui'; +export { + ErrorToastOptions, + ToastsApi, + ToastInput, + IToasts, + ToastInputFields, + Toast, +} from './toasts_api'; diff --git a/src/core/public/notifications/toasts/toasts_api.test.ts b/src/core/public/notifications/toasts/toasts_api.test.ts index 38e6d2a222990..f99a28617aa5c 100644 --- a/src/core/public/notifications/toasts/toasts_api.test.ts +++ b/src/core/public/notifications/toasts/toasts_api.test.ts @@ -91,7 +91,7 @@ describe('#get$()', () => { toasts.add('foo'); onToasts.mockClear(); - toasts.remove({ id: 'bar' }); + toasts.remove('bar'); expect(onToasts).not.toHaveBeenCalled(); }); }); @@ -136,7 +136,7 @@ describe('#remove()', () => { it('ignores unknown toast', async () => { const toasts = new ToastsApi(toastDeps()); toasts.add('Test'); - toasts.remove({ id: 'foo' }); + toasts.remove('foo'); const currentToasts = await getCurrentToasts(toasts); expect(currentToasts).toHaveLength(1); diff --git a/src/core/public/notifications/toasts/toasts_api.tsx b/src/core/public/notifications/toasts/toasts_api.tsx index 24514cb11548b..b49bafda5b26e 100644 --- a/src/core/public/notifications/toasts/toasts_api.tsx +++ b/src/core/public/notifications/toasts/toasts_api.tsx @@ -17,11 +17,13 @@ * under the License. */ -import { EuiGlobalToastListToast as Toast } from '@elastic/eui'; +import { EuiGlobalToastListToast as EuiToast } from '@elastic/eui'; import React from 'react'; import * as Rx from 'rxjs'; import { ErrorToast } from './error_toast'; +import { MountPoint } from '../../types'; +import { mountReactNode } from '../../utils'; import { UiSettingsClientContract } from '../../ui_settings'; import { OverlayStart } from '../../overlays'; @@ -33,13 +35,20 @@ import { OverlayStart } from '../../overlays'; * * @public */ -export type ToastInputFields = Pick>; +export type ToastInputFields = Pick> & { + title?: string | MountPoint; + text?: string | MountPoint; +}; + +export type Toast = ToastInputFields & { + id: string; +}; /** * Inputs for {@link IToasts} APIs. * @public */ -export type ToastInput = string | ToastInputFields | Promise; +export type ToastInput = string | ToastInputFields; /** * Options available for {@link IToasts} APIs. @@ -59,13 +68,12 @@ export interface ErrorToastOptions { toastMessage?: string; } -const normalizeToast = (toastOrTitle: ToastInput) => { +const normalizeToast = (toastOrTitle: ToastInput): ToastInputFields => { if (typeof toastOrTitle === 'string') { return { title: toastOrTitle, }; } - return toastOrTitle; }; @@ -123,11 +131,12 @@ export class ToastsApi implements IToasts { /** * Removes a toast from the current array of toasts if present. - * @param toast - a {@link Toast} returned by {@link ToastApi.add} + * @param toastOrId - a {@link Toast} returned by {@link ToastsApi.add} or its id */ - public remove(toast: Toast) { + public remove(toastOrId: Toast | string) { + const toRemove = typeof toastOrId === 'string' ? toastOrId : toastOrId.id; const list = this.toasts$.getValue(); - const listWithoutToast = list.filter(t => t !== toast); + const listWithoutToast = list.filter(t => t.id !== toRemove); if (listWithoutToast.length !== list.length) { this.toasts$.next(listWithoutToast); } @@ -191,7 +200,7 @@ export class ToastsApi implements IToasts { iconType: 'alert', title: options.title, toastLifeTimeMs: this.uiSettings.get('notifications:lifetime:error'), - text: ( + text: mountReactNode( this.api!.remove(toast)} + dismissToast={(toastId: string) => this.api!.remove(toastId)} toasts$={this.api!.get$()} /> , diff --git a/src/core/public/overlays/banners/banners_service.tsx b/src/core/public/overlays/banners/banners_service.tsx index 799ca43c7fa93..31d49b5952e87 100644 --- a/src/core/public/overlays/banners/banners_service.tsx +++ b/src/core/public/overlays/banners/banners_service.tsx @@ -25,33 +25,20 @@ import { PriorityMap } from './priority_map'; import { BannersList } from './banners_list'; import { UiSettingsClientContract } from '../../ui_settings'; import { I18nStart } from '../../i18n'; +import { MountPoint } from '../../types'; import { UserBannerService } from './user_banner_service'; -/** - * A function that will unmount the banner from the element. - * @public - */ -export type OverlayBannerUnmount = () => void; - -/** - * A function that will mount the banner inside the provided element. - * @param element an element to render into - * @returns a {@link OverlayBannerUnmount} - * @public - */ -export type OverlayBannerMount = (element: HTMLElement) => OverlayBannerUnmount; - /** @public */ export interface OverlayBannersStart { /** * Add a new banner * - * @param mount {@link OverlayBannerMount} + * @param mount {@link MountPoint} * @param priority optional priority order to display this banner. Higher priority values are shown first. * @returns a unique identifier for the given banner to be used with {@link OverlayBannersStart.remove} and * {@link OverlayBannersStart.replace} */ - add(mount: OverlayBannerMount, priority?: number): string; + add(mount: MountPoint, priority?: number): string; /** * Remove a banner @@ -65,12 +52,12 @@ export interface OverlayBannersStart { * Replace a banner in place * * @param id the unique identifier for the banner returned by {@link OverlayBannersStart.add} - * @param mount {@link OverlayBannerMount} + * @param mount {@link MountPoint} * @param priority optional priority order to display this banner. Higher priority values are shown first. * @returns a new identifier for the given banner to be used with {@link OverlayBannersStart.remove} and * {@link OverlayBannersStart.replace} */ - replace(id: string | undefined, mount: OverlayBannerMount, priority?: number): string; + replace(id: string | undefined, mount: MountPoint, priority?: number): string; /** @internal */ get$(): Observable; @@ -80,7 +67,7 @@ export interface OverlayBannersStart { /** @internal */ export interface OverlayBanner { readonly id: string; - readonly mount: OverlayBannerMount; + readonly mount: MountPoint; readonly priority: number; } @@ -116,7 +103,7 @@ export class OverlayBannersService { return true; }, - replace(id: string | undefined, mount: OverlayBannerMount, priority = 0) { + replace(id: string | undefined, mount: MountPoint, priority = 0) { if (!id || !banners$.value.has(id)) { return this.add(mount, priority); } diff --git a/src/core/public/overlays/banners/index.ts b/src/core/public/overlays/banners/index.ts index 9e908bd628003..a68dfa7ebadac 100644 --- a/src/core/public/overlays/banners/index.ts +++ b/src/core/public/overlays/banners/index.ts @@ -17,9 +17,4 @@ * under the License. */ -export { - OverlayBannerMount, - OverlayBannerUnmount, - OverlayBannersStart, - OverlayBannersService, -} from './banners_service'; +export { OverlayBannersStart, OverlayBannersService } from './banners_service'; diff --git a/src/core/public/overlays/banners/priority_map.test.ts b/src/core/public/overlays/banners/priority_map.test.ts index 13d81989417f1..2b16682c13aad 100644 --- a/src/core/public/overlays/banners/priority_map.test.ts +++ b/src/core/public/overlays/banners/priority_map.test.ts @@ -42,7 +42,10 @@ describe('PriorityMap', () => { map = map.add('b', { priority: 3 }); map = map.add('c', { priority: 2 }); map = map.remove('c'); - expect([...map]).toEqual([['b', { priority: 3 }], ['a', { priority: 1 }]]); + expect([...map]).toEqual([ + ['b', { priority: 3 }], + ['a', { priority: 1 }], + ]); }); it('adds duplicate priorities to end', () => { diff --git a/src/core/public/overlays/index.ts b/src/core/public/overlays/index.ts index c49548abee0df..ff03e5dffb2ca 100644 --- a/src/core/public/overlays/index.ts +++ b/src/core/public/overlays/index.ts @@ -17,5 +17,5 @@ * under the License. */ -export { OverlayBannerMount, OverlayBannerUnmount, OverlayBannersStart } from './banners'; +export { OverlayBannersStart } from './banners'; export { OverlayService, OverlayStart, OverlayRef } from './overlay_service'; diff --git a/src/core/public/plugins/plugins_service.test.ts b/src/core/public/plugins/plugins_service.test.ts index cfac4c3648053..0d8887774e900 100644 --- a/src/core/public/plugins/plugins_service.test.ts +++ b/src/core/public/plugins/plugins_service.test.ts @@ -223,10 +223,13 @@ test('`PluginsService.setup` exposes dependent setup contracts to plugins', asyn test('`PluginsService.setup` does not set missing dependent setup contracts', async () => { plugins = [{ id: 'pluginD', plugin: createManifest('pluginD', { optional: ['missing'] }) }]; - mockPluginInitializers.set('pluginD', jest.fn(() => ({ - setup: jest.fn(), - start: jest.fn(), - })) as any); + mockPluginInitializers.set( + 'pluginD', + jest.fn(() => ({ + setup: jest.fn(), + start: jest.fn(), + })) as any + ); const pluginsService = new PluginsService(mockCoreContext, plugins); await pluginsService.setup(mockSetupDeps); @@ -268,10 +271,13 @@ test('`PluginsService.start` exposes dependent start contracts to plugins', asyn test('`PluginsService.start` does not set missing dependent start contracts', async () => { plugins = [{ id: 'pluginD', plugin: createManifest('pluginD', { optional: ['missing'] }) }]; - mockPluginInitializers.set('pluginD', jest.fn(() => ({ - setup: jest.fn(), - start: jest.fn(), - })) as any); + mockPluginInitializers.set( + 'pluginD', + jest.fn(() => ({ + setup: jest.fn(), + start: jest.fn(), + })) as any + ); const pluginsService = new PluginsService(mockCoreContext, plugins); await pluginsService.setup(mockSetupDeps); diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index a596ea394abda..1e97d8e066d09 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -5,17 +5,18 @@ ```ts import { Breadcrumb } from '@elastic/eui'; +import { EuiGlobalToastListToast } from '@elastic/eui'; import { IconType } from '@elastic/eui'; import { Observable } from 'rxjs'; import React from 'react'; import * as Rx from 'rxjs'; import { ShallowPromise } from '@kbn/utility-types'; -import { EuiGlobalToastListToast as Toast } from '@elastic/eui'; import { UiSettingsParams as UiSettingsParams_2 } from 'src/core/server/types'; import { UserProvidedValues as UserProvidedValues_2 } from 'src/core/server/types'; // @public export interface App extends AppBase { + chromeless?: boolean; mount: (context: AppMountContext, params: AppMountParameters) => AppUnmount | Promise; } @@ -618,6 +619,9 @@ export interface LegacyNavLink { url: string; } +// @public +export type MountPoint = (element: HTMLElement) => UnmountCallback; + // @public (undocumented) export interface NotificationsSetup { // (undocumented) @@ -630,12 +634,9 @@ export interface NotificationsStart { toasts: ToastsStart; } -// @public -export type OverlayBannerMount = (element: HTMLElement) => OverlayBannerUnmount; - // @public (undocumented) export interface OverlayBannersStart { - add(mount: OverlayBannerMount, priority?: number): string; + add(mount: MountPoint, priority?: number): string; // Warning: (ae-forgotten-export) The symbol "OverlayBanner" needs to be exported by the entry point index.d.ts // // @internal (undocumented) @@ -643,12 +644,9 @@ export interface OverlayBannersStart { // (undocumented) getComponent(): JSX.Element; remove(id: string): boolean; - replace(id: string | undefined, mount: OverlayBannerMount, priority?: number): string; + replace(id: string | undefined, mount: MountPoint, priority?: number): string; } -// @public -export type OverlayBannerUnmount = () => void; - // @public export interface OverlayRef { close(): Promise; @@ -916,35 +914,36 @@ export class SimpleSavedObject { _version?: SavedObject['version']; } -export { Toast } +// Warning: (ae-missing-release-tag) "Toast" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type Toast = ToastInputFields & { + id: string; +}; // @public -export type ToastInput = string | ToastInputFields | Promise; +export type ToastInput = string | ToastInputFields; // @public -export type ToastInputFields = Pick>; +export type ToastInputFields = Pick> & { + title?: string | MountPoint; + text?: string | MountPoint; +}; // @public export class ToastsApi implements IToasts { constructor(deps: { uiSettings: UiSettingsClientContract; }); - // Warning: (ae-unresolved-link) The @link reference could not be resolved: Reexported declarations are not supported add(toastOrTitle: ToastInput): Toast; - // Warning: (ae-unresolved-link) The @link reference could not be resolved: Reexported declarations are not supported addDanger(toastOrTitle: ToastInput): Toast; - // Warning: (ae-unresolved-link) The @link reference could not be resolved: Reexported declarations are not supported addError(error: Error, options: ErrorToastOptions): Toast; - // Warning: (ae-unresolved-link) The @link reference could not be resolved: Reexported declarations are not supported addSuccess(toastOrTitle: ToastInput): Toast; - // Warning: (ae-unresolved-link) The @link reference could not be resolved: Reexported declarations are not supported addWarning(toastOrTitle: ToastInput): Toast; get$(): Rx.Observable; // @internal (undocumented) registerOverlays(overlays: OverlayStart): void; - // Warning: (ae-unresolved-link) The @link reference could not be resolved: Reexported declarations are not supported - // Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "ToastApi" - remove(toast: Toast): void; + remove(toastOrId: Toast | string): void; } // @public (undocumented) @@ -990,5 +989,8 @@ export interface UiSettingsState { [key: string]: UiSettingsParams_2 & UserProvidedValues_2; } +// @public +export type UnmountCallback = () => void; + ``` diff --git a/src/legacy/core_plugins/data/public/query/query_service.ts b/src/core/public/types.ts similarity index 62% rename from src/legacy/core_plugins/data/public/query/query_service.ts rename to src/core/public/types.ts index f6f229496f49d..4b12d5bc6da51 100644 --- a/src/legacy/core_plugins/data/public/query/query_service.ts +++ b/src/core/public/types.ts @@ -17,34 +17,21 @@ * under the License. */ -import { fromUser, toUser, getQueryLog } from './query_bar'; - /** - * Query Service + * A function that should mount DOM content inside the provided container element + * and return a handler to unmount it. + * + * @param element the container element to render into + * @returns a {@link UnmountCallback} that unmount the element on call. * - * @internal + * @public */ -export class QueryService { - public setup() { - return { - helpers: { - fromUser, - toUser, - getQueryLog, - }, - }; - } - - public start() { - // nothing to do here yet - } - - public stop() { - // nothing to do here yet - } -} +export type MountPoint = (element: HTMLElement) => UnmountCallback; -/** @public */ -export type QuerySetup = ReturnType; - -export * from './query_bar'; +/** + * A function that will unmount the element previously mounted by + * the associated {@link MountPoint} + * + * @public + */ +export type UnmountCallback = () => void; diff --git a/src/core/public/ui_settings/ui_settings_api.test.ts b/src/core/public/ui_settings/ui_settings_api.test.ts index 048ae2ccbae7f..1170c42cea704 100644 --- a/src/core/public/ui_settings/ui_settings_api.test.ts +++ b/src/core/public/ui_settings/ui_settings_api.test.ts @@ -183,10 +183,7 @@ describe('#getLoadingCount$()', () => { const done$ = new Rx.Subject(); const promise = uiSettingsApi .getLoadingCount$() - .pipe( - takeUntil(done$), - toArray() - ) + .pipe(takeUntil(done$), toArray()) .toPromise(); await uiSettingsApi.batchSet('foo', 'bar'); @@ -214,10 +211,7 @@ describe('#getLoadingCount$()', () => { const done$ = new Rx.Subject(); const promise = uiSettingsApi .getLoadingCount$() - .pipe( - takeUntil(done$), - toArray() - ) + .pipe(takeUntil(done$), toArray()) .toPromise(); await uiSettingsApi.batchSet('foo', 'bar'); @@ -250,7 +244,10 @@ describe('#stop', () => { uiSettingsApi.stop(); // both observables should emit the same values, and complete before the request is done loading - await expect(promise).resolves.toEqual([[0, 1], [0, 1]]); + await expect(promise).resolves.toEqual([ + [0, 1], + [0, 1], + ]); await batchSetPromise; }); }); diff --git a/src/core/public/ui_settings/ui_settings_client.test.ts b/src/core/public/ui_settings/ui_settings_client.test.ts index 8a481fe1704dd..c58ba14d0da3e 100644 --- a/src/core/public/ui_settings/ui_settings_client.test.ts +++ b/src/core/public/ui_settings/ui_settings_client.test.ts @@ -83,10 +83,7 @@ describe('#get$', () => { const { config } = setup(); const values = await config .get$('dateFormat') - .pipe( - take(1), - toArray() - ) + .pipe(take(1), toArray()) .toPromise(); expect(values).toEqual(['Browser']); @@ -122,10 +119,7 @@ You can use \`config.get("unknown key", defaultValue)\`, which will just return const values = await config .get$('dateFormat') - .pipe( - take(2), - toArray() - ) + .pipe(take(2), toArray()) .toPromise(); expect(values).toEqual(['Browser', 'new format']); @@ -144,10 +138,7 @@ You can use \`config.get("unknown key", defaultValue)\`, which will just return const values = await config .get$('dateFormat', 'my default') - .pipe( - take(3), - toArray() - ) + .pipe(take(3), toArray()) .toPromise(); expect(values).toEqual(['my default', 'new format', 'my default']); diff --git a/src/core/public/utils/index.ts b/src/core/public/utils/index.ts index a432094b15048..cf826eb276252 100644 --- a/src/core/public/utils/index.ts +++ b/src/core/public/utils/index.ts @@ -19,3 +19,4 @@ export { shareWeakReplay } from './share_weak_replay'; export { Sha256 } from './crypto'; +export { MountWrapper, mountReactNode } from './mount'; diff --git a/src/core/public/utils/mount.tsx b/src/core/public/utils/mount.tsx new file mode 100644 index 0000000000000..dbd7d5da435a6 --- /dev/null +++ b/src/core/public/utils/mount.tsx @@ -0,0 +1,44 @@ +/* + * 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 React, { useEffect, useRef } from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { I18nProvider } from '@kbn/i18n/react'; +import { MountPoint } from '../types'; + +/** + * MountWrapper is a react component to mount a {@link MountPoint} inside a react tree. + */ +export const MountWrapper: React.FunctionComponent<{ mount: MountPoint }> = ({ mount }) => { + const element = useRef(null); + useEffect(() => mount(element.current!), [mount]); + return
; +}; + +/** + * Mount converter for react components. + * + * @param component to get a mount for + */ +export const mountReactNode = (component: React.ReactNode): MountPoint => ( + element: HTMLElement +) => { + render({component}, element); + return () => unmountComponentAtNode(element); +}; diff --git a/src/core/public/utils/share_weak_replay.test.ts b/src/core/public/utils/share_weak_replay.test.ts index dcf599f6d1e10..6eaa140e5afad 100644 --- a/src/core/public/utils/share_weak_replay.test.ts +++ b/src/core/public/utils/share_weak_replay.test.ts @@ -153,10 +153,7 @@ Array [ }); it('resubscribes if parent completes', async () => { - const shared = counter().pipe( - take(4), - shareWeakReplay(4) - ); + const shared = counter().pipe(take(4), shareWeakReplay(4)); await expect(Promise.all([record(shared.pipe(take(1))), record(shared)])).resolves .toMatchInlineSnapshot(` @@ -199,10 +196,7 @@ Array [ it('supports parents that complete synchronously', async () => { const next = jest.fn(); const complete = jest.fn(); - const shared = counter({ async: false }).pipe( - take(3), - shareWeakReplay(1) - ); + const shared = counter({ async: false }).pipe(take(3), shareWeakReplay(1)); shared.subscribe({ next, complete }); expect(next.mock.calls).toMatchInlineSnapshot(` diff --git a/src/core/server/config/ensure_deep_object.ts b/src/core/server/config/ensure_deep_object.ts index 0b24190741b10..58865d13c1afa 100644 --- a/src/core/server/config/ensure_deep_object.ts +++ b/src/core/server/config/ensure_deep_object.ts @@ -34,19 +34,16 @@ export function ensureDeepObject(obj: any): any { return obj.map(item => ensureDeepObject(item)); } - return Object.keys(obj).reduce( - (fullObject, propertyKey) => { - const propertyValue = obj[propertyKey]; - if (!propertyKey.includes(separator)) { - fullObject[propertyKey] = ensureDeepObject(propertyValue); - } else { - walk(fullObject, propertyKey.split(separator), propertyValue); - } - - return fullObject; - }, - {} as any - ); + return Object.keys(obj).reduce((fullObject, propertyKey) => { + const propertyValue = obj[propertyKey]; + if (!propertyKey.includes(separator)) { + fullObject[propertyKey] = ensureDeepObject(propertyValue); + } else { + walk(fullObject, propertyKey.split(separator), propertyValue); + } + + return fullObject; + }, {} as any); } function walk(obj: any, keys: string[], value: any) { diff --git a/src/core/server/plugins/plugins_system.ts b/src/core/server/plugins/plugins_system.ts index 9f7d8e4f35172..34acb66d4e931 100644 --- a/src/core/server/plugins/plugins_system.ts +++ b/src/core/server/plugins/plugins_system.ts @@ -77,18 +77,15 @@ export class PluginsSystem { this.log.debug(`Setting up plugin "${pluginName}"...`); const pluginDeps = new Set([...plugin.requiredPlugins, ...plugin.optionalPlugins]); - const pluginDepContracts = Array.from(pluginDeps).reduce( - (depContracts, dependencyName) => { - // Only set if present. Could be absent if plugin does not have server-side code or is a - // missing optional dependency. - if (contracts.has(dependencyName)) { - depContracts[dependencyName] = contracts.get(dependencyName); - } - - return depContracts; - }, - {} as Record - ); + const pluginDepContracts = Array.from(pluginDeps).reduce((depContracts, dependencyName) => { + // Only set if present. Could be absent if plugin does not have server-side code or is a + // missing optional dependency. + if (contracts.has(dependencyName)) { + depContracts[dependencyName] = contracts.get(dependencyName); + } + + return depContracts; + }, {} as Record); contracts.set( pluginName, @@ -116,18 +113,15 @@ export class PluginsSystem { this.log.debug(`Starting plugin "${pluginName}"...`); const plugin = this.plugins.get(pluginName)!; const pluginDeps = new Set([...plugin.requiredPlugins, ...plugin.optionalPlugins]); - const pluginDepContracts = Array.from(pluginDeps).reduce( - (depContracts, dependencyName) => { - // Only set if present. Could be absent if plugin does not have server-side code or is a - // missing optional dependency. - if (contracts.has(dependencyName)) { - depContracts[dependencyName] = contracts.get(dependencyName); - } - - return depContracts; - }, - {} as Record - ); + const pluginDepContracts = Array.from(pluginDeps).reduce((depContracts, dependencyName) => { + // Only set if present. Could be absent if plugin does not have server-side code or is a + // missing optional dependency. + if (contracts.has(dependencyName)) { + depContracts[dependencyName] = contracts.get(dependencyName); + } + + return depContracts; + }, {} as Record); contracts.set( pluginName, diff --git a/src/core/server/saved_objects/mappings/lib/get_root_properties_objects.ts b/src/core/server/saved_objects/mappings/lib/get_root_properties_objects.ts index 3bac17bc46686..61e4d752445c4 100644 --- a/src/core/server/saved_objects/mappings/lib/get_root_properties_objects.ts +++ b/src/core/server/saved_objects/mappings/lib/get_root_properties_objects.ts @@ -39,17 +39,14 @@ const blacklist = ['migrationVersion', 'references']; export function getRootPropertiesObjects(mappings: IndexMapping) { const rootProperties = getRootProperties(mappings); - return Object.entries(rootProperties).reduce( - (acc, [key, value]) => { - // we consider the existence of the properties or type of object to designate that this is an object datatype - if ( - !blacklist.includes(key) && - ((value as ComplexFieldMapping).properties || value.type === 'object') - ) { - acc[key] = value; - } - return acc; - }, - {} as MappingProperties - ); + return Object.entries(rootProperties).reduce((acc, [key, value]) => { + // we consider the existence of the properties or type of object to designate that this is an object datatype + if ( + !blacklist.includes(key) && + ((value as ComplexFieldMapping).properties || value.type === 'object') + ) { + acc[key] = value; + } + return acc; + }, {} as MappingProperties); } diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts index 54b9938decb0a..51d4a8ad50ad6 100644 --- a/src/core/server/saved_objects/service/lib/repository.ts +++ b/src/core/server/saved_objects/service/lib/repository.ts @@ -246,15 +246,17 @@ export class SavedObjectsRepository { const expectedResult = { esRequestIndex: requestIndexCounter++, requestedId: object.id, - rawMigratedDoc: this._serializer.savedObjectToRaw(this._migrator.migrateDocument({ - id: object.id, - type: object.type, - attributes: object.attributes, - migrationVersion: object.migrationVersion, - namespace, - updated_at: time, - references: object.references || [], - }) as SanitizedSavedObjectDoc), + rawMigratedDoc: this._serializer.savedObjectToRaw( + this._migrator.migrateDocument({ + id: object.id, + type: object.type, + attributes: object.attributes, + migrationVersion: object.migrationVersion, + namespace, + updated_at: time, + references: object.references || [], + }) as SanitizedSavedObjectDoc + ), }; bulkCreateParams.push( diff --git a/src/core/server/server.test.ts b/src/core/server/server.test.ts index aee6461580654..f912a31901ad8 100644 --- a/src/core/server/server.test.ts +++ b/src/core/server/server.test.ts @@ -70,7 +70,10 @@ test('injects legacy dependency to context#setup()', async () => { const pluginA = Symbol(); const pluginB = Symbol(); - const pluginDependencies = new Map([[pluginA, []], [pluginB, [pluginA]]]); + const pluginDependencies = new Map([ + [pluginA, []], + [pluginB, [pluginA]], + ]); mockPluginsService.discover.mockResolvedValue(pluginDependencies); await server.setup(); diff --git a/src/core/server/ui_settings/ui_settings_client.ts b/src/core/server/ui_settings/ui_settings_client.ts index 423ff2a1dfd90..1a0f29f6ae6d9 100644 --- a/src/core/server/ui_settings/ui_settings_client.ts +++ b/src/core/server/ui_settings/ui_settings_client.ts @@ -83,14 +83,11 @@ export class UiSettingsClient implements IUiSettingsClient { async getAll() { const raw = await this.getRaw(); - return Object.keys(raw).reduce( - (all, key) => { - const item = raw[key]; - all[key] = ('userValue' in item ? item.userValue : item.value) as T; - return all; - }, - {} as Record - ); + return Object.keys(raw).reduce((all, key) => { + const item = raw[key]; + all[key] = ('userValue' in item ? item.userValue : item.value) as T; + return all; + }, {} as Record); } async getUserProvided(): Promise> { diff --git a/src/core/utils/context.ts b/src/core/utils/context.ts index 022c3e4330032..775c890675410 100644 --- a/src/core/utils/context.ts +++ b/src/core/utils/context.ts @@ -254,23 +254,20 @@ export class ContextContainer> return [...this.contextProviders] .sort(sortByCoreFirst(this.coreId)) .filter(([contextName]) => contextsToBuild.has(contextName)) - .reduce( - async (contextPromise, [contextName, { provider, source: providerSource }]) => { - const resolvedContext = await contextPromise; + .reduce(async (contextPromise, [contextName, { provider, source: providerSource }]) => { + const resolvedContext = await contextPromise; - // For the next provider, only expose the context available based on the dependencies of the plugin that - // registered that provider. - const exposedContext = pick(resolvedContext, [ - ...this.getContextNamesForSource(providerSource), - ]) as Partial>; + // For the next provider, only expose the context available based on the dependencies of the plugin that + // registered that provider. + const exposedContext = pick(resolvedContext, [ + ...this.getContextNamesForSource(providerSource), + ]) as Partial>; - return { - ...resolvedContext, - [contextName]: await provider(exposedContext, ...contextArgs), - }; - }, - Promise.resolve({}) as Promise> - ); + return { + ...resolvedContext, + [contextName]: await provider(exposedContext, ...contextArgs), + }; + }, Promise.resolve({}) as Promise>); } private getContextNamesForSource( diff --git a/src/core/utils/map_utils.test.ts b/src/core/utils/map_utils.test.ts index 0d9b2a6129de0..315ae3328c47f 100644 --- a/src/core/utils/map_utils.test.ts +++ b/src/core/utils/map_utils.test.ts @@ -42,7 +42,11 @@ describe('groupIntoMap', () => { const groupBy = (item: { id: number }) => item.id; expect(groupIntoMap([{ id: 1 }, { id: 2 }, { id: 3 }], groupBy)).toEqual( - new Map([[1, [{ id: 1 }]], [2, [{ id: 2 }]], [3, [{ id: 3 }]]]) + new Map([ + [1, [{ id: 1 }]], + [2, [{ id: 2 }]], + [3, [{ id: 3 }]], + ]) ); }); @@ -93,7 +97,12 @@ describe('mapValuesOfMap', () => { map.set(even, 2); map.set(odd, 1); - expect(mapValuesOfMap(map, mapper)).toEqual(new Map([[even, 6], [odd, 3]])); + expect(mapValuesOfMap(map, mapper)).toEqual( + new Map([ + [even, 6], + [odd, 3], + ]) + ); expect(map.get(odd)).toEqual(1); expect(map.get(even)).toEqual(2); }); diff --git a/src/core/utils/merge.ts b/src/core/utils/merge.ts index aead3f35ba841..8e5d9f4860d95 100644 --- a/src/core/utils/merge.ts +++ b/src/core/utils/merge.ts @@ -66,20 +66,17 @@ const mergeObjects = , U extends Record - [...new Set([...Object.keys(baseObj), ...Object.keys(overrideObj)])].reduce( - (merged, key) => { - const baseVal = baseObj[key]; - const overrideVal = overrideObj[key]; + [...new Set([...Object.keys(baseObj), ...Object.keys(overrideObj)])].reduce((merged, key) => { + const baseVal = baseObj[key]; + const overrideVal = overrideObj[key]; - if (isMergable(baseVal) && isMergable(overrideVal)) { - merged[key] = mergeObjects(baseVal, overrideVal); - } else if (overrideVal !== undefined) { - merged[key] = overrideVal; - } else if (baseVal !== undefined) { - merged[key] = baseVal; - } + if (isMergable(baseVal) && isMergable(overrideVal)) { + merged[key] = mergeObjects(baseVal, overrideVal); + } else if (overrideVal !== undefined) { + merged[key] = overrideVal; + } else if (baseVal !== undefined) { + merged[key] = baseVal; + } - return merged; - }, - {} as any - ); + return merged; + }, {} as any); diff --git a/src/core/utils/pick.ts b/src/core/utils/pick.ts index 77854f9af680b..08288343d9077 100644 --- a/src/core/utils/pick.ts +++ b/src/core/utils/pick.ts @@ -18,14 +18,11 @@ */ export function pick(obj: T, keys: K[]): Pick { - return keys.reduce( - (acc, key) => { - if (obj.hasOwnProperty(key)) { - acc[key] = obj[key]; - } + return keys.reduce((acc, key) => { + if (obj.hasOwnProperty(key)) { + acc[key] = obj[key]; + } - return acc; - }, - {} as Pick - ); + return acc; + }, {} as Pick); } diff --git a/src/dev/build/tasks/nodejs_modules/clean_client_modules_on_dll_task.js b/src/dev/build/tasks/nodejs_modules/clean_client_modules_on_dll_task.js index 3ae7d33d24d68..5c0462ce86fa9 100644 --- a/src/dev/build/tasks/nodejs_modules/clean_client_modules_on_dll_task.js +++ b/src/dev/build/tasks/nodejs_modules/clean_client_modules_on_dll_task.js @@ -49,9 +49,18 @@ export const CleanClientModulesOnDLLTask = { `${baseDir}/src/plugins/*/server/index.js`, `!${baseDir}/src/plugins/**/public` ]); + const discoveredNewPlatformXpackPlugins = await globby([ + `${baseDir}/x-pack/plugins/*/server/index.js`, + `!${baseDir}/x-pack/plugins/**/public` + ]); // Compose all the needed entries - const serverEntries = [ ...mainCodeEntries, ...discoveredLegacyCorePluginEntries, ...discoveredPluginEntries]; + const serverEntries = [ + ...mainCodeEntries, + ...discoveredLegacyCorePluginEntries, + ...discoveredPluginEntries, + ...discoveredNewPlatformXpackPlugins + ]; // Get the dependencies found searching through the server // side code entries that were provided diff --git a/src/dev/jest/config.js b/src/dev/jest/config.js index 0c785a84bb469..f5c20da89dcfa 100644 --- a/src/dev/jest/config.js +++ b/src/dev/jest/config.js @@ -103,6 +103,7 @@ export default { 'packages/kbn-pm/dist/index.js' ], snapshotSerializers: [ + '/src/plugins/kibana_react/public/util/test_helpers/react_mount_serializer.ts', '/node_modules/enzyme-to-json/serializer', ], reporters: [ diff --git a/src/dev/license_checker/valid.ts b/src/dev/license_checker/valid.ts index 8fe09db0a5874..9142955185a1a 100644 --- a/src/dev/license_checker/valid.ts +++ b/src/dev/license_checker/valid.ts @@ -36,24 +36,21 @@ interface Options { * violations or returns undefined. */ export function assertLicensesValid({ packages, validLicenses }: Options) { - const invalidMsgs = packages.reduce( - (acc, pkg) => { - const invalidLicenses = pkg.licenses.filter(license => !validLicenses.includes(license)); + const invalidMsgs = packages.reduce((acc, pkg) => { + const invalidLicenses = pkg.licenses.filter(license => !validLicenses.includes(license)); - if (pkg.licenses.length && !invalidLicenses.length) { - return acc; - } + if (pkg.licenses.length && !invalidLicenses.length) { + return acc; + } - return acc.concat(dedent` + return acc.concat(dedent` ${pkg.name} version: ${pkg.version} all licenses: ${pkg.licenses} invalid licenses: ${invalidLicenses.join(', ')} path: ${pkg.relative} `); - }, - [] as string[] - ); + }, [] as string[]); if (invalidMsgs.length) { throw createFailError( diff --git a/src/legacy/core_plugins/console/index.ts b/src/legacy/core_plugins/console/index.ts index 30a85f4e7d342..caef3ff6f99f3 100644 --- a/src/legacy/core_plugins/console/index.ts +++ b/src/legacy/core_plugins/console/index.ts @@ -56,7 +56,6 @@ export default function(kibana: any) { const npSrc = resolve(__dirname, 'np_ready/public'); let defaultVars: any; - const apps: any[] = []; return new kibana.Plugin({ id: 'console', require: ['elasticsearch'], @@ -181,8 +180,6 @@ export default function(kibana: any) { }, uiExports: { - apps, - hacks: ['plugins/console/quarantined/hacks/register'], devTools: [`${npSrc}/legacy`], styleSheetPaths: resolve(__dirname, 'public/quarantined/index.scss'), diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/main/main.tsx b/src/legacy/core_plugins/console/np_ready/public/application/containers/main/main.tsx index 4e5afbdb5821e..518630c5a07c1 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/containers/main/main.tsx +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/main/main.tsx @@ -70,7 +70,7 @@ export function Main() { }; return ( - <> +
setShowSettings(false)} /> : null} {showHelp ? setShowHelp(false)} /> : null} - +
); } diff --git a/src/legacy/core_plugins/console/np_ready/public/legacy.ts b/src/legacy/core_plugins/console/np_ready/public/legacy.ts index 1a2d312823f6f..8c60ff23648be 100644 --- a/src/legacy/core_plugins/console/np_ready/public/legacy.ts +++ b/src/legacy/core_plugins/console/np_ready/public/legacy.ts @@ -24,80 +24,31 @@ import 'brace/mode/json'; import 'brace/mode/text'; /* eslint-disable @kbn/eslint/no-restricted-paths */ -import { toastNotifications as notifications } from 'ui/notify'; import { npSetup, npStart } from 'ui/new_platform'; -import uiRoutes from 'ui/routes'; -import { DOC_LINK_VERSION } from 'ui/documentation_links'; import { I18nContext } from 'ui/i18n'; import { ResizeChecker } from 'ui/resize_checker'; -import 'ui/capabilities/route_setup'; /* eslint-enable @kbn/eslint/no-restricted-paths */ -import template from '../../public/quarantined/index.html'; -import { App, AppUnmount, NotificationsSetup } from '../../../../../core/public'; - export interface XPluginSet { + devTools: DevToolsSetup; + feature_catalogue: FeatureCatalogueSetup; __LEGACY: { I18nContext: any; ResizeChecker: any; - docLinkVersion: string; }; } import { plugin } from '.'; +import { DevToolsSetup } from '../../../../../plugins/dev_tools/public'; +import { FeatureCatalogueSetup } from '../../../../../plugins/feature_catalogue/public'; const pluginInstance = plugin({} as any); -const anyObject = {} as any; - -uiRoutes.when('/dev_tools/console', { - requireUICapability: 'dev_tools.show', - controller: function RootController($scope) { - // Stub out this config for now... - $scope.topNavMenu = []; - - $scope.initReactApp = () => { - const targetElement = document.querySelector('#consoleRoot'); - if (!targetElement) { - const message = `Could not mount Console App!`; - npSetup.core.fatalErrors.add(message); - throw new Error(message); - } - - let unmount: AppUnmount | Promise; - - const mockedSetupCore = { - ...npSetup.core, - notifications: (notifications as unknown) as NotificationsSetup, - application: { - register(app: App): void { - try { - unmount = app.mount(anyObject, { element: targetElement, appBasePath: '' }); - } catch (e) { - npSetup.core.fatalErrors.add(e); - } - }, - registerMountContext() {}, - }, - }; - - pluginInstance.setup(mockedSetupCore, { - ...npSetup.plugins, - __LEGACY: { - I18nContext, - ResizeChecker, - docLinkVersion: DOC_LINK_VERSION, - }, - }); - pluginInstance.start(npStart.core); - - $scope.$on('$destroy', async () => { - if (unmount) { - const fn = await unmount; - fn(); - } - }); - }; +pluginInstance.setup(npSetup.core, { + ...npSetup.plugins, + __LEGACY: { + I18nContext, + ResizeChecker, }, - template, }); +pluginInstance.start(npStart.core); diff --git a/src/legacy/core_plugins/console/np_ready/public/plugin.ts b/src/legacy/core_plugins/console/np_ready/public/plugin.ts index 188a738d59794..f02b0b5e72999 100644 --- a/src/legacy/core_plugins/console/np_ready/public/plugin.ts +++ b/src/legacy/core_plugins/console/np_ready/public/plugin.ts @@ -18,26 +18,55 @@ */ import { render, unmountComponentAtNode } from 'react-dom'; +import { i18n } from '@kbn/i18n'; +import { FeatureCatalogueCategory } from 'ui/registry/feature_catalogue'; import { PluginInitializerContext, Plugin, CoreStart, CoreSetup } from '../../../../../core/public'; import { XPluginSet } from './legacy'; -import { boot } from './application'; export class ConsoleUIPlugin implements Plugin { // @ts-ignore constructor(private readonly ctx: PluginInitializerContext) {} - async setup({ application, notifications }: CoreSetup, pluginSet: XPluginSet) { + async setup({ notifications }: CoreSetup, pluginSet: XPluginSet) { const { - __LEGACY: { docLinkVersion, I18nContext, ResizeChecker }, + __LEGACY: { I18nContext, ResizeChecker }, + devTools, + feature_catalogue, } = pluginSet; - application.register({ + feature_catalogue.register({ + id: 'console', + title: i18n.translate('console.devToolsTitle', { + defaultMessage: 'Console', + }), + description: i18n.translate('console.devToolsDescription', { + defaultMessage: 'Skip cURL and use this JSON interface to work with your data directly.', + }), + icon: 'consoleApp', + path: '/app/kibana#/dev_tools/console', + showOnHomePage: true, + category: FeatureCatalogueCategory.ADMIN, + }); + + devTools.register({ id: 'console', order: 1, - title: 'Console', - mount(ctx, { element }) { - render(boot({ docLinkVersion, I18nContext, ResizeChecker, notifications }), element); + title: i18n.translate('console.consoleDisplayName', { + defaultMessage: 'Console', + }), + enableRouting: false, + async mount(ctx, { element }) { + const { boot } = await import('./application'); + render( + boot({ + docLinkVersion: ctx.core.docLinks.DOC_LINK_VERSION, + I18nContext, + ResizeChecker, + notifications, + }), + element + ); return () => { unmountComponentAtNode(element); }; diff --git a/src/legacy/core_plugins/console/public/quarantined/index.html b/src/legacy/core_plugins/console/public/quarantined/index.html deleted file mode 100644 index 66a693d4b2af7..0000000000000 --- a/src/legacy/core_plugins/console/public/quarantined/index.html +++ /dev/null @@ -1,3 +0,0 @@ - -
-
diff --git a/src/legacy/core_plugins/console/server/request.test.ts b/src/legacy/core_plugins/console/server/request.test.ts index 463649a090295..d5504c0f3a3c2 100644 --- a/src/legacy/core_plugins/console/server/request.test.ts +++ b/src/legacy/core_plugins/console/server/request.test.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import http from 'http'; +import http, { ClientRequest } from 'http'; import * as sinon from 'sinon'; import { sendRequest } from './request'; import { URL } from 'url'; @@ -24,7 +24,7 @@ import { fail } from 'assert'; describe(`Console's send request`, () => { let sandbox: sinon.SinonSandbox; - let stub: sinon.SinonStub; + let stub: sinon.SinonStub, ClientRequest>; let fakeRequest: http.ClientRequest; beforeEach(() => { diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/_index.scss b/src/legacy/core_plugins/data/public/filter/filter_bar/_index.scss index 5333aff8b87da..9e2478cb0704e 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/_index.scss +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/_index.scss @@ -1,3 +1,4 @@ @import 'variables'; @import 'global_filter_group'; @import 'global_filter_item'; +@import 'filter_editor/index'; diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/_filter_editor.scss b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/_filter_editor.scss new file mode 100644 index 0000000000000..a5fac10e4693f --- /dev/null +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/_filter_editor.scss @@ -0,0 +1,3 @@ +.globalFilterEditor__fieldInput { + max-width: $euiSize * 13; +} diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/_index.scss b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/_index.scss new file mode 100644 index 0000000000000..21ba32ec6a6fe --- /dev/null +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/_index.scss @@ -0,0 +1 @@ +@import 'filter_editor'; \ No newline at end of file diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/index.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/index.tsx index 5dd5c05647789..84da576e8205c 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/index.tsx +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/index.tsx @@ -30,6 +30,7 @@ import { EuiPopoverTitle, EuiSpacer, EuiSwitch, + EuiSwitchEvent, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; @@ -245,6 +246,7 @@ class FilterEditorUI extends Component { private renderFieldInput() { const { selectedIndexPattern, selectedField } = this.state; const fields = selectedIndexPattern ? getFilterableFields(selectedIndexPattern) : []; + return ( { onChange={this.onFieldChange} singleSelection={{ asPlainText: true }} isClearable={false} + className="globalFilterEditor__fieldInput" data-test-subj="filterFieldSuggestionList" /> @@ -431,7 +434,7 @@ class FilterEditorUI extends Component { this.setState({ selectedOperator, params }); }; - private onCustomLabelSwitchChange = (event: React.ChangeEvent) => { + private onCustomLabelSwitchChange = (event: EuiSwitchEvent) => { const useCustomLabel = event.target.checked; const customLabel = event.target.checked ? '' : null; this.setState({ useCustomLabel, customLabel }); diff --git a/src/legacy/core_plugins/data/public/index.ts b/src/legacy/core_plugins/data/public/index.ts index c3892fa581fc4..2412541e8c5c8 100644 --- a/src/legacy/core_plugins/data/public/index.ts +++ b/src/legacy/core_plugins/data/public/index.ts @@ -38,7 +38,7 @@ export { IndexPatterns, StaticIndexPattern, } from './index_patterns'; -export { Query, QueryBarInput } from './query'; +export { QueryBarInput } from './query'; export { SearchBar, SearchBarProps, SavedQueryAttributes, SavedQuery } from './search'; /** @public static code */ diff --git a/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_patterns.ts b/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_patterns.ts index 4767b6d3a3ca7..2c58af9deaf49 100644 --- a/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_patterns.ts +++ b/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_patterns.ts @@ -52,11 +52,13 @@ export class IndexPatterns { } private async refreshSavedObjectsCache() { - this.savedObjectsCache = (await this.savedObjectsClient.find({ - type: 'index-pattern', - fields: [], - perPage: 10000, - })).savedObjects; + this.savedObjectsCache = ( + await this.savedObjectsClient.find({ + type: 'index-pattern', + fields: [], + perPage: 10000, + }) + ).savedObjects; } getIds = async (refresh: boolean = false) => { diff --git a/src/legacy/core_plugins/data/public/mocks.ts b/src/legacy/core_plugins/data/public/mocks.ts index d3b5944127965..39d1296ddf8bc 100644 --- a/src/legacy/core_plugins/data/public/mocks.ts +++ b/src/legacy/core_plugins/data/public/mocks.ts @@ -18,12 +18,10 @@ */ import { indexPatternsServiceMock } from './index_patterns/index_patterns_service.mock'; -import { queryServiceMock } from './query/query_service.mock'; function createDataSetupMock() { return { indexPatterns: indexPatternsServiceMock.createSetupContract(), - query: queryServiceMock.createSetupContract(), }; } diff --git a/src/legacy/core_plugins/data/public/plugin.ts b/src/legacy/core_plugins/data/public/plugin.ts index 76beb4ee56053..2059f61fde59e 100644 --- a/src/legacy/core_plugins/data/public/plugin.ts +++ b/src/legacy/core_plugins/data/public/plugin.ts @@ -19,7 +19,6 @@ import { CoreSetup, CoreStart, Plugin } from 'kibana/public'; import { SearchService, SearchStart, createSearchBar, StatetfulSearchBarProps } from './search'; -import { QueryService, QuerySetup } from './query'; import { IndexPatternsService, IndexPatternsSetup, IndexPatternsStart } from './index_patterns'; import { Storage, IStorageWrapper } from '../../../../../src/plugins/kibana_utils/public'; import { DataPublicPluginStart } from '../../../../plugins/data/public'; @@ -42,7 +41,6 @@ export interface DataPluginStartDependencies { * @public */ export interface DataSetup { - query: QuerySetup; indexPatterns: IndexPatternsSetup; } @@ -52,7 +50,6 @@ export interface DataSetup { * @public */ export interface DataStart { - query: QuerySetup; indexPatterns: IndexPatternsStart; search: SearchStart; ui: { @@ -74,7 +71,6 @@ export interface DataStart { export class DataPlugin implements Plugin { private readonly indexPatterns: IndexPatternsService = new IndexPatternsService(); - private readonly query: QueryService = new QueryService(); private readonly search: SearchService = new SearchService(); private setupApi!: DataSetup; @@ -85,7 +81,6 @@ export class DataPlugin implements Plugin PersistedLog: mockPersistedLogFactory, })); -jest.mock('../lib/fetch_index_patterns', () => ({ +jest.mock('./fetch_index_patterns', () => ({ fetchIndexPatterns: mockFetchIndexPatterns, })); diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index 5576427b1592a..31a17315db7dd 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -38,18 +38,22 @@ import { AutocompleteSuggestion, AutocompleteSuggestionType, PersistedLog, + toUser, + fromUser, + matchPairs, + getQueryLog, + Query, } from '../../../../../../../plugins/data/public'; import { withKibana, KibanaReactContextValue, + toMountPoint, } from '../../../../../../../plugins/kibana_react/public'; import { IndexPattern, StaticIndexPattern } from '../../../index_patterns'; -import { Query, getQueryLog } from '../index'; -import { fromUser, matchPairs, toUser } from '../lib'; import { QueryLanguageSwitcher } from './language_switcher'; import { SuggestionsComponent } from './typeahead/suggestions_component'; -import { fetchIndexPatterns } from '../lib/fetch_index_patterns'; import { IDataPluginServices } from '../../../types'; +import { fetchIndexPatterns } from './fetch_index_patterns'; interface Props { kibana: KibanaReactContextValue; @@ -361,7 +365,7 @@ export class QueryBarInputUI extends Component { id: 'data.query.queryBar.KQLNestedQuerySyntaxInfoTitle', defaultMessage: 'KQL nested query syntax', }), - text: ( + text: toMountPoint(

(queryLanguage ? getQueryLog(uiSettings!, storage, appName, queryLanguage) : undefined), - [queryLanguage] + () => + queryLanguage && uiSettings && storage && appName + ? getQueryLog(uiSettings!, storage, appName, queryLanguage) + : undefined, + [appName, queryLanguage, uiSettings, storage] ); function onClickSubmitButton(event: React.MouseEvent) { @@ -298,7 +305,7 @@ function QueryBarTopRowUI(props: Props) { id: 'data.query.queryBar.luceneSyntaxWarningTitle', defaultMessage: 'Lucene syntax warning', }), - text: ( + text: toMountPoint(

; - -const createSetupContractMock = () => { - const setupContract: jest.Mocked = { - helpers: { - fromUser: jest.fn(), - toUser: jest.fn(), - getQueryLog: jest.fn(), - }, - }; - - return setupContract; -}; - -const createMock = () => { - const mocked: jest.Mocked = { - setup: jest.fn(), - start: jest.fn(), - stop: jest.fn(), - }; - - mocked.setup.mockReturnValue(createSetupContractMock()); - return mocked; -}; - -export const queryServiceMock = { - create: createMock, - createSetupContract: createSetupContractMock, - createStartContract: createSetupContractMock, -}; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index a57b7b17a0da6..d713139366eef 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -24,9 +24,8 @@ import React, { Component } from 'react'; import ResizeObserver from 'resize-observer-polyfill'; import { get, isEqual } from 'lodash'; -import { TimeRange } from 'src/plugins/data/common/types'; -import { TimeHistoryContract } from 'src/plugins/data/public'; -import { IndexPattern, Query, FilterBar } from '../../../../../data/public'; +import { TimeRange, Query, TimeHistoryContract } from 'src/plugins/data/public'; +import { IndexPattern, FilterBar } from '../../../../../data/public'; import { QueryBarTopRow } from '../../../query'; import { SavedQuery, SavedQueryAttributes } from '../index'; import { SavedQueryMeta, SaveQueryForm } from './saved_query_management/save_query_form'; @@ -73,6 +72,7 @@ export interface SearchBarOwnProps { // Show when user has privileges to save showSaveQuery?: boolean; savedQuery?: SavedQuery; + onQueryChange?: (payload: { dateRange: TimeRange; query?: Query }) => void; onQuerySubmit?: (payload: { dateRange: TimeRange; query?: Query }) => void; // User has saved the current state as a saved query onSaved?: (savedQuery: SavedQuery) => void; @@ -207,6 +207,18 @@ class SearchBarUI extends Component { ); } + /* + * This Function is here to show the toggle in saved query form + * in case you the date range (from/to) + */ + private shouldRenderTimeFilterInSavedQueryForm() { + const { dateRangeFrom, dateRangeTo, showDatePicker } = this.props; + return ( + showDatePicker || + (!showDatePicker && dateRangeFrom !== undefined && dateRangeTo !== undefined) + ); + } + public setFilterBarHeight = () => { requestAnimationFrame(() => { const height = @@ -300,6 +312,9 @@ class SearchBarUI extends Component { dateRangeFrom: queryAndDateRange.dateRange.from, dateRangeTo: queryAndDateRange.dateRange.to, }); + if (this.props.onQueryChange) { + this.props.onQueryChange(queryAndDateRange); + } }; public onQueryBarSubmit = (queryAndDateRange: { dateRange?: TimeRange; query?: Query }) => { @@ -441,7 +456,7 @@ class SearchBarUI extends Component { onSave={this.onSave} onClose={() => this.setState({ showSaveQueryModal: false })} showFilterOption={this.props.showFilterBar} - showTimeFilterOption={this.props.showDatePicker} + showTimeFilterOption={this.shouldRenderTimeFilterInSavedQueryForm()} /> ) : null} {this.state.showSaveNewQueryModal ? ( @@ -450,7 +465,7 @@ class SearchBarUI extends Component { onSave={savedQueryMeta => this.onSave(savedQueryMeta, true)} onClose={() => this.setState({ showSaveNewQueryModal: false })} showFilterOption={this.props.showFilterBar} - showTimeFilterOption={this.props.showDatePicker} + showTimeFilterOption={this.shouldRenderTimeFilterInSavedQueryForm()} /> ) : null}

diff --git a/src/legacy/core_plugins/data/public/search/search_bar/index.tsx b/src/legacy/core_plugins/data/public/search/search_bar/index.tsx index ebde9d60b0b51..f369bf997c1a9 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/index.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/index.tsx @@ -17,9 +17,7 @@ * under the License. */ -import { RefreshInterval, TimeRange } from 'src/plugins/data/public'; -import { Query } from '../../query/query_bar'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { RefreshInterval, TimeRange, Query, esFilters } from 'src/plugins/data/public'; export * from './components'; diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/list_control_editor.test.js b/src/legacy/core_plugins/input_control_vis/public/components/editor/list_control_editor.test.js index 96c0802d3772a..ea029af9e4890 100644 --- a/src/legacy/core_plugins/input_control_vis/public/components/editor/list_control_editor.test.js +++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/list_control_editor.test.js @@ -236,7 +236,7 @@ test('handleCheckboxOptionChange - multiselect', async () => { component.update(); const checkbox = findTestSubject(component, 'listControlMultiselectInput'); - checkbox.simulate('change', { target: { checked: true } }); + checkbox.simulate('click'); sinon.assert.notCalled(handleFieldNameChange); sinon.assert.notCalled(handleIndexPatternChange); sinon.assert.notCalled(handleNumberOptionChange); @@ -247,7 +247,9 @@ test('handleCheckboxOptionChange - multiselect', async () => { expectedControlIndex, expectedOptionName, sinon.match((evt) => { - if (evt.target.checked === true) { + // Synthetic `evt.target.checked` does not get altered by EuiSwitch, + // but its aria attribute is correctly updated + if (evt.target.getAttribute('aria-checked') === 'true') { return true; } return false; diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.test.js b/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.test.js index 39f5f6a50a5a6..8784f0e79ca8d 100644 --- a/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.test.js +++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.test.js @@ -47,8 +47,8 @@ describe('OptionsTab', () => { it('should update updateFiltersOnChange', () => { const component = mountWithIntl(); - const checkbox = component.find('[data-test-subj="inputControlEditorUpdateFiltersOnChangeCheckbox"] input[type="checkbox"]'); - checkbox.simulate('change', { target: { checked: true } }); + const checkbox = component.find('[data-test-subj="inputControlEditorUpdateFiltersOnChangeCheckbox"] button'); + checkbox.simulate('click'); expect(props.setValue).toHaveBeenCalledTimes(1); expect(props.setValue).toHaveBeenCalledWith('updateFiltersOnChange', true); @@ -56,8 +56,8 @@ describe('OptionsTab', () => { it('should update useTimeFilter', () => { const component = mountWithIntl(); - const checkbox = component.find('[data-test-subj="inputControlEditorUseTimeFilterCheckbox"] input[type="checkbox"]'); - checkbox.simulate('change', { target: { checked: true } }); + const checkbox = component.find('[data-test-subj="inputControlEditorUseTimeFilterCheckbox"] button'); + checkbox.simulate('click'); expect(props.setValue).toHaveBeenCalledTimes(1); expect(props.setValue).toHaveBeenCalledWith('useTimeFilter', true); @@ -65,8 +65,8 @@ describe('OptionsTab', () => { it('should update pinFilters', () => { const component = mountWithIntl(); - const checkbox = component.find('[data-test-subj="inputControlEditorPinFiltersCheckbox"] input[type="checkbox"]'); - checkbox.simulate('change', { target: { checked: true } }); + const checkbox = component.find('[data-test-subj="inputControlEditorPinFiltersCheckbox"] button'); + checkbox.simulate('click'); expect(props.setValue).toHaveBeenCalledTimes(1); expect(props.setValue).toHaveBeenCalledWith('pinFilters', true); diff --git a/src/legacy/core_plugins/interpreter/public/renderers/visualization.tsx b/src/legacy/core_plugins/interpreter/public/renderers/visualization.tsx index 9de6cdeaf5ec3..f15cdf23fe15b 100644 --- a/src/legacy/core_plugins/interpreter/public/renderers/visualization.tsx +++ b/src/legacy/core_plugins/interpreter/public/renderers/visualization.tsx @@ -21,7 +21,7 @@ import chrome from 'ui/chrome'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; // @ts-ignore -import { VisProvider } from '../../../../ui/public/visualize/loader/vis'; +import { Vis } from '../../../../ui/public/visualize/loader/vis'; import { Visualization } from '../../../../ui/public/visualize/components'; export const visualization = () => ({ @@ -33,8 +33,6 @@ export const visualization = () => ({ const visType = config.visType || visConfig.type; const $injector = await chrome.dangerouslyGetActiveInjector(); const $rootScope = $injector.get('$rootScope') as any; - const Private = $injector.get('Private') as any; - const Vis = Private(VisProvider); if (handlers.vis) { // special case in visualize, we need to render first (without executing the expression), for maps to work diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/index.tsx b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/index.tsx index c7ada18f9e1f2..2ca4ed1e2343d 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/index.tsx +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/index.tsx @@ -83,9 +83,11 @@ function MetricsAxisOptions(props: ValidationVisOptionsProps) // stores previous aggs' custom labels const [lastCustomLabels, setLastCustomLabels] = useState({} as { [key: string]: string }); // stores previous aggs' field and type - const [lastSeriesAgg, setLastSeriesAgg] = useState({} as { - [key: string]: { type: string; field: string }; - }); + const [lastSeriesAgg, setLastSeriesAgg] = useState( + {} as { + [key: string]: { type: string; field: string }; + } + ); const updateAxisTitle = () => { const axes = cloneDeep(stateParams.valueAxes); diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index 24cd436912395..c7cda8aec0165 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -62,7 +62,7 @@ export default function (kibana) { uiExports: { hacks: [ - 'plugins/kibana/dev_tools/hacks/hide_empty_tools', + 'plugins/kibana/dev_tools', ], fieldFormats: ['plugins/kibana/field_formats/register'], savedObjectTypes: [ diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx index 5fa3a938ed9df..656b54040ad99 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx @@ -26,19 +26,16 @@ import { IInjector } from 'ui/chrome'; // @ts-ignore import * as filterActions from 'plugins/kibana/discover/doc_table/actions/filter'; -// @ts-ignore -import { getFilterGenerator } from 'ui/filter_manager'; - import { AppStateClass as TAppStateClass, AppState as TAppState, } from 'ui/state_management/app_state'; import { KbnUrl } from 'ui/url/kbn_url'; -import { TimeRange } from 'src/plugins/data/public'; +import { TimeRange, Query } from 'src/plugins/data/public'; import { IndexPattern } from 'ui/index_patterns'; import { IPrivate } from 'ui/private'; -import { StaticIndexPattern, Query, SavedQuery } from 'plugins/data'; +import { StaticIndexPattern, SavedQuery } from 'plugins/data'; import moment from 'moment'; import { Subscription } from 'rxjs'; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx index 548a66297a3f9..d82b89339b0d0 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx @@ -50,12 +50,13 @@ import { import { KbnUrl } from 'ui/url/kbn_url'; import { IndexPattern } from 'ui/index_patterns'; import { IPrivate } from 'ui/private'; -import { Query, SavedQuery } from 'src/legacy/core_plugins/data/public'; +import { SavedQuery } from 'src/legacy/core_plugins/data/public'; import { SaveOptions } from 'ui/saved_objects/saved_object'; import { capabilities } from 'ui/capabilities'; import { Subscription } from 'rxjs'; import { npStart } from 'ui/new_platform'; import { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder'; +import { Query } from '../../../../../plugins/data/public'; import { start as data } from '../../../data/public/legacy'; import { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts index 8ffabe5add1c3..1a42ed837a9de 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts @@ -27,9 +27,9 @@ import { migrateLegacyQuery } from 'ui/utils/migrate_legacy_query'; import { Moment } from 'moment'; import { DashboardContainer } from 'src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public'; +import { Query } from 'src/plugins/data/public'; import { ViewMode } from '../../../../../../src/plugins/embeddable/public'; import { esFilters } from '../../../../../../src/plugins/data/public'; -import { Query } from '../../../data/public'; import { getAppStateDefaults, migrateAppState } from './lib'; import { convertPanelStateToSavedDashboardPanel } from './lib/embeddable_saved_object_converters'; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/migrations/move_filters_to_query.ts b/src/legacy/core_plugins/kibana/public/dashboard/migrations/move_filters_to_query.ts index 8522495b9dedb..e82fc58670e39 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/migrations/move_filters_to_query.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/migrations/move_filters_to_query.ts @@ -17,8 +17,7 @@ * under the License. */ -import { Query } from 'src/legacy/core_plugins/data/public'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters, Query } from '../../../../../../plugins/data/public'; export interface Pre600FilterQuery { // pre 6.0.0 global query:queryString:options were stored per dashboard and would diff --git a/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboard.d.ts b/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboard.d.ts index 5b860b0a2cc7c..5b24aa13f4f77 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboard.d.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboard.d.ts @@ -19,9 +19,7 @@ import { SearchSource } from 'ui/courier'; import { SavedObject } from 'ui/saved_objects/saved_object'; -import { RefreshInterval } from 'src/plugins/data/public'; -import { Query } from 'src/legacy/core_plugins/data/public'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters, Query, RefreshInterval } from '../../../../../../plugins/data/public'; export interface SavedObjectDashboard extends SavedObject { id?: string; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/types.ts b/src/legacy/core_plugins/kibana/public/dashboard/types.ts index 5aaca7b62094f..3c2c87a502da4 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/types.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/types.ts @@ -18,7 +18,6 @@ */ import { AppState } from 'ui/state_management/app_state'; -import { Query } from 'src/legacy/core_plugins/data/public'; import { AppState as TAppState } from 'ui/state_management/app_state'; import { ViewMode } from 'src/plugins/embeddable/public'; import { @@ -29,7 +28,7 @@ import { RawSavedDashboardPanel640To720, RawSavedDashboardPanel730ToLatest, } from './migrations/types'; -import { esFilters } from '../../../../../plugins/data/public'; +import { Query, esFilters } from '../../../../../plugins/data/public'; export type NavAction = (anchorElement?: any) => void; diff --git a/src/legacy/core_plugins/kibana/public/dev_tools/_index.scss b/src/legacy/core_plugins/kibana/public/dev_tools/_index.scss index 563b140fd2ead..2e88d2e1285e3 100644 --- a/src/legacy/core_plugins/kibana/public/dev_tools/_index.scss +++ b/src/legacy/core_plugins/kibana/public/dev_tools/_index.scss @@ -16,3 +16,6 @@ } } +.devApp { + height: 100%; +} diff --git a/src/legacy/core_plugins/kibana/public/dev_tools/application.tsx b/src/legacy/core_plugins/kibana/public/dev_tools/application.tsx new file mode 100644 index 0000000000000..3945d8d8dc856 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/dev_tools/application.tsx @@ -0,0 +1,184 @@ +/* + * 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 { I18nProvider } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { EuiTab, EuiTabs, EuiToolTip } from '@elastic/eui'; +import { HashRouter as Router, Switch, Route, Redirect } from 'react-router-dom'; +import * as React from 'react'; +import ReactDOM from 'react-dom'; +import { useEffect, useRef } from 'react'; + +import { AppMountContext } from 'kibana/public'; +import { DevTool } from '../../../../../plugins/dev_tools/public'; + +interface DevToolsWrapperProps { + devTools: readonly DevTool[]; + activeDevTool: DevTool; + appMountContext: AppMountContext; + updateRoute: (newRoute: string) => void; +} + +interface MountedDevToolDescriptor { + devTool: DevTool; + mountpoint: HTMLElement; + unmountHandler: () => void; +} + +function DevToolsWrapper({ + devTools, + activeDevTool, + appMountContext, + updateRoute, +}: DevToolsWrapperProps) { + const mountedTool = useRef(null); + + useEffect( + () => () => { + if (mountedTool.current) { + mountedTool.current.unmountHandler(); + } + }, + [] + ); + + return ( +
+ + {devTools.map(currentDevTool => ( + + { + if (!currentDevTool.disabled) { + updateRoute(`/dev_tools/${currentDevTool.id}`); + } + }} + > + {currentDevTool.title} + + + ))} + +
{ + if ( + element && + (mountedTool.current === null || + mountedTool.current.devTool !== activeDevTool || + mountedTool.current.mountpoint !== element) + ) { + if (mountedTool.current) { + mountedTool.current.unmountHandler(); + } + const unmountHandler = await activeDevTool.mount(appMountContext, { + element, + appBasePath: '', + }); + mountedTool.current = { + devTool: activeDevTool, + mountpoint: element, + unmountHandler, + }; + } + }} + /> +
+ ); +} + +function redirectOnMissingCapabilities(appMountContext: AppMountContext) { + if (!appMountContext.core.application.capabilities.dev_tools.show) { + window.location.hash = '/home'; + return true; + } + return false; +} + +function setBadge(appMountContext: AppMountContext) { + if (appMountContext.core.application.capabilities.dev_tools.save) { + return; + } + appMountContext.core.chrome.setBadge({ + text: i18n.translate('kbn.devTools.badge.readOnly.text', { + defaultMessage: 'Read only', + }), + tooltip: i18n.translate('kbn.devTools.badge.readOnly.tooltip', { + defaultMessage: 'Unable to save', + }), + iconType: 'glasses', + }); +} + +function setBreadcrumbs(appMountContext: AppMountContext) { + appMountContext.core.chrome.setBreadcrumbs([ + { + text: i18n.translate('kbn.devTools.k7BreadcrumbsDevToolsLabel', { + defaultMessage: 'Dev Tools', + }), + href: '#/dev_tools', + }, + ]); +} + +export function renderApp( + element: HTMLElement, + appMountContext: AppMountContext, + basePath: string, + devTools: readonly DevTool[] +) { + if (redirectOnMissingCapabilities(appMountContext)) { + return () => {}; + } + setBadge(appMountContext); + setBreadcrumbs(appMountContext); + ReactDOM.render( + + + + {devTools.map(devTool => ( + ( + + )} + /> + ))} + + + + + + , + element + ); + + return () => ReactDOM.unmountComponentAtNode(element); +} diff --git a/src/legacy/core_plugins/kibana/public/dev_tools/hacks/__tests__/hide_empty_tools.js b/src/legacy/core_plugins/kibana/public/dev_tools/hacks/__tests__/hide_empty_tools.js deleted file mode 100644 index 25c7b945b9dfb..0000000000000 --- a/src/legacy/core_plugins/kibana/public/dev_tools/hacks/__tests__/hide_empty_tools.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import sinon from 'sinon'; - -import { hideEmptyDevTools } from '../hide_empty_tools'; -import { npStart } from 'ui/new_platform'; - -describe('hide dev tools', function () { - let updateNavLink; - - function PrivateWithoutTools() { - return []; - } - - function PrivateWithTools() { - return ['tool1', 'tool2']; - } - - function isHidden() { - return updateNavLink.calledWith('kibana:dev_tools', { hidden: true }); - } - - beforeEach(function () { - const coreNavLinks = npStart.core.chrome.navLinks; - updateNavLink = sinon.spy(coreNavLinks, 'update'); - }); - - it('should hide the app if there are no dev tools', function () { - hideEmptyDevTools(PrivateWithTools); - expect(isHidden()).to.be(false); - }); - - it('should not hide the app if there are tools', function () { - hideEmptyDevTools(PrivateWithoutTools); - expect(isHidden()).to.be(true); - }); - - afterEach(function () { - updateNavLink.restore(); - }); -}); diff --git a/src/legacy/core_plugins/kibana/public/dev_tools/index.js b/src/legacy/core_plugins/kibana/public/dev_tools/index.js deleted file mode 100644 index e36e75f6837ab..0000000000000 --- a/src/legacy/core_plugins/kibana/public/dev_tools/index.js +++ /dev/null @@ -1,77 +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 uiRoutes from 'ui/routes'; -import { i18n } from '@kbn/i18n'; -import { DevToolsRegistryProvider } from 'ui/registry/dev_tools'; -import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue'; -import 'ui/directives/kbn_href'; -import './directives/dev_tools_app'; - -uiRoutes - .when('/dev_tools', { - resolve: { - redirect(Private, kbnUrl) { - const items = Private(DevToolsRegistryProvider).inOrder; - kbnUrl.redirect(items[0].url.substring(1)); - } - } - }); - -uiRoutes.defaults(/^\/dev_tools(\/|$)/, { - badge: uiCapabilities => { - if (uiCapabilities.dev_tools.save) { - return undefined; - } - - return { - text: i18n.translate('kbn.devTools.badge.readOnly.text', { - defaultMessage: 'Read only', - }), - tooltip: i18n.translate('kbn.devTools.badge.readOnly.tooltip', { - defaultMessage: 'Unable to save', - }), - iconType: 'glasses' - }; - }, - k7Breadcrumbs: () => [ - { - text: i18n.translate('kbn.devTools.k7BreadcrumbsDevToolsLabel', { - defaultMessage: 'Dev Tools' - }), - href: '#/dev_tools' - } - ] -}); - -FeatureCatalogueRegistryProvider.register(() => { - return { - id: 'console', - title: i18n.translate('kbn.devTools.consoleTitle', { - defaultMessage: 'Console' - }), - description: i18n.translate('kbn.devTools.consoleDescription', { - defaultMessage: 'Skip cURL and use this JSON interface to work with your data directly.' - }), - icon: 'consoleApp', - path: '/app/kibana#/dev_tools/console', - showOnHomePage: true, - category: FeatureCatalogueCategory.ADMIN - }; -}); diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_stats.ts b/src/legacy/core_plugins/kibana/public/dev_tools/index.ts similarity index 68% rename from src/legacy/core_plugins/telemetry/server/telemetry_collection/get_stats.ts rename to src/legacy/core_plugins/kibana/public/dev_tools/index.ts index b739b20545678..74708e36a98aa 100644 --- a/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_stats.ts +++ b/src/legacy/core_plugins/kibana/public/dev_tools/index.ts @@ -17,12 +17,18 @@ * under the License. */ -// @ts-ignore -import { getLocalStats } from './get_local_stats'; -import { StatsGetter, getStatsCollectionConfig } from '../collection_manager'; +import { npSetup, npStart } from 'ui/new_platform'; -export const getStats: StatsGetter = async function(config) { - const { callCluster, server } = getStatsCollectionConfig(config, 'data'); +import { DevToolsPlugin } from './plugin'; +import { localApplicationService } from '../local_application_service'; - return [await getLocalStats({ callCluster, server })]; -}; +const instance = new DevToolsPlugin(); + +instance.setup(npSetup.core, { + __LEGACY: { + localApplicationService, + }, +}); +instance.start(npStart.core, { + newPlatformDevTools: npStart.plugins.devTools, +}); diff --git a/src/legacy/core_plugins/kibana/public/dev_tools/partials/dev_tools_app.html b/src/legacy/core_plugins/kibana/public/dev_tools/partials/dev_tools_app.html deleted file mode 100644 index 6c076092c76d5..0000000000000 --- a/src/legacy/core_plugins/kibana/public/dev_tools/partials/dev_tools_app.html +++ /dev/null @@ -1,22 +0,0 @@ -
- - -
-
diff --git a/src/legacy/core_plugins/kibana/public/dev_tools/plugin.ts b/src/legacy/core_plugins/kibana/public/dev_tools/plugin.ts new file mode 100644 index 0000000000000..ec9af1a6acd92 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/dev_tools/plugin.ts @@ -0,0 +1,71 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// This import makes sure dev tools are registered before the app is. +import 'uiExports/devTools'; + +import { CoreSetup, CoreStart, Plugin } from 'kibana/public'; + +import { LocalApplicationService } from '../local_application_service'; +import { DevTool, DevToolsStart } from '../../../../../plugins/dev_tools/public'; + +export interface DevToolsPluginSetupDependencies { + __LEGACY: { + localApplicationService: LocalApplicationService; + }; +} + +export interface DevToolsPluginStartDependencies { + newPlatformDevTools: DevToolsStart; +} + +export class DevToolsPlugin implements Plugin { + private getSortedDevTools: (() => readonly DevTool[]) | null = null; + + public setup( + core: CoreSetup, + { __LEGACY: { localApplicationService } }: DevToolsPluginSetupDependencies + ) { + localApplicationService.register({ + id: 'dev_tools', + title: 'Dev Tools', + mount: async (appMountContext, params) => { + if (!this.getSortedDevTools) { + throw new Error('not started yet'); + } + const { renderApp } = await import('./application'); + return renderApp( + params.element, + appMountContext, + params.appBasePath, + this.getSortedDevTools() + ); + }, + }); + } + + public start(core: CoreStart, { newPlatformDevTools }: DevToolsPluginStartDependencies) { + this.getSortedDevTools = newPlatformDevTools.getSortedDevTools; + if (this.getSortedDevTools().length === 0) { + core.chrome.navLinks.update('kibana:dev_tools', { + hidden: true, + }); + } + } +} diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/api/__tests__/_stubs.js b/src/legacy/core_plugins/kibana/public/discover/angular/context/api/__tests__/_stubs.js index f472ff9250eb5..b3d37083b37f7 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/api/__tests__/_stubs.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/api/__tests__/_stubs.js @@ -26,7 +26,8 @@ export function createIndexPatternsStub() { get: sinon.spy(indexPatternId => Promise.resolve({ id: indexPatternId, - isTimeNanosBased: () => false + isTimeNanosBased: () => false, + popularizeField: () => {}, }) ), }; diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_add_filter.js b/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_add_filter.js index b136b03bd500b..5a445a65939ed 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_add_filter.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/__tests__/action_add_filter.js @@ -19,32 +19,33 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; -import sinon from 'sinon'; import { getServices } from '../../../../kibana_services'; import { createStateStub } from './_utils'; import { QueryParameterActionsProvider } from '../actions'; - +import { createIndexPatternsStub } from '../../api/__tests__/_stubs'; +import { npStart } from 'ui/new_platform'; describe('context app', function () { beforeEach(ngMock.module('kibana')); + beforeEach(ngMock.module(function createServiceStubs($provide) { + $provide.value('indexPatterns', createIndexPatternsStub()); + })); + describe('action addFilter', function () { - let filterManagerStub; let addFilter; beforeEach(ngMock.inject(function createPrivateStubs(Private) { - filterManagerStub = createQueryFilterStub(); - Private.stub(getServices().FilterBarQueryFilterProvider, filterManagerStub); - + Private.stub(getServices().FilterBarQueryFilterProvider); addFilter = Private(QueryParameterActionsProvider).addFilter; })); it('should pass the given arguments to the filterManager', function () { const state = createStateStub(); + const filterManagerAddStub = npStart.plugins.data.query.filterManager.addFilters; addFilter(state)('FIELD_NAME', 'FIELD_VALUE', 'FILTER_OPERATION'); - const filterManagerAddStub = filterManagerStub.addFilters; //get the generated filter const generatedFilter = filterManagerAddStub.firstCall.args[0][0]; const queryKeys = Object.keys(generatedFilter.query.match_phrase); @@ -55,20 +56,12 @@ describe('context app', function () { it('should pass the index pattern id to the filterManager', function () { const state = createStateStub(); + const filterManagerAddStub = npStart.plugins.data.query.filterManager.addFilters; addFilter(state)('FIELD_NAME', 'FIELD_VALUE', 'FILTER_OPERATION'); - const filterManagerAddStub = filterManagerStub.addFilters; const generatedFilter = filterManagerAddStub.firstCall.args[0][0]; - expect(filterManagerAddStub.calledOnce).to.be(true); expect(generatedFilter.meta.index).to.eql('INDEX_PATTERN_ID'); }); }); }); - -function createQueryFilterStub() { - return { - addFilters: sinon.stub(), - getAppFilters: sinon.stub(), - }; -} diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/actions.js b/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/actions.js index 9f7b180e8fe7d..10fe6c0e2eda1 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/actions.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/query_parameters/actions.js @@ -18,7 +18,8 @@ */ import _ from 'lodash'; -import { getServices, getFilterGenerator } from '../../../kibana_services'; +import { generateFilters } from '../../../../../../../../plugins/data/public'; +import { npStart } from 'ui/new_platform'; import { MAX_CONTEXT_SIZE, @@ -27,9 +28,8 @@ import { } from './constants'; -export function QueryParameterActionsProvider(indexPatterns, Private) { - const queryFilter = Private(getServices().FilterBarQueryFilterProvider); - const filterGen = getFilterGenerator(queryFilter); +export function QueryParameterActionsProvider(indexPatterns) { + const { filterManager } = npStart.plugins.data.query; const setPredecessorCount = (state) => (predecessorCount) => ( state.queryParameters.predecessorCount = clamp( @@ -55,13 +55,13 @@ export function QueryParameterActionsProvider(indexPatterns, Private) { ); const updateFilters = () => filters => { - queryFilter.setFilters(filters); + filterManager.setFilters(filters); }; const addFilter = (state) => async (field, values, operation) => { const indexPatternId = state.queryParameters.indexPatternId; - const newFilters = filterGen.generate(field, values, operation, indexPatternId); - queryFilter.addFilters(newFilters); + const newFilters = generateFilters(filterManager, field, values, operation, indexPatternId); + filterManager.addFilters(newFilters); const indexPattern = await indexPatterns.get(indexPatternId); indexPattern.popularizeField(field.name, 1); }; diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/discover.js b/src/legacy/core_plugins/kibana/public/discover/angular/discover.js index ed5049aa912e0..8ee23bfb005a2 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/discover.js @@ -31,7 +31,6 @@ import './doc_table'; import { getSort } from './doc_table/lib/get_sort'; import { getSortForSearchSource } from './doc_table/lib/get_sort_for_search_source'; import * as columnActions from './doc_table/actions/columns'; -import * as filterActions from './doc_table/actions/filter'; import indexTemplate from './discover.html'; import { showOpenSearchPanel } from '../top_nav/show_open_search_panel'; @@ -41,7 +40,6 @@ import { getPainlessError } from './get_painless_error'; import { angular, buildVislibDimensions, - getFilterGenerator, getRequestInspectorStats, getResponseInspectorStats, getServices, @@ -57,7 +55,7 @@ import { subscribeWithScope, tabifyAggResponse, vislibSeriesResponseHandlerProvider, - VisProvider, + Vis, SavedObjectSaveModal, } from '../kibana_services'; @@ -76,7 +74,7 @@ const { import { getRootBreadcrumbs, getSavedSearchBreadcrumbs } from '../breadcrumbs'; import { extractTimeFilter, changeTimeFilter } from '../../../../data/public'; import { start as data } from '../../../../data/public/legacy'; - +import { generateFilters } from '../../../../../../plugins/data/public'; const { savedQueryService } = data.search.services; @@ -190,13 +188,11 @@ function discoverController( localStorage, uiCapabilities ) { - const Vis = Private(VisProvider); const responseHandler = vislibSeriesResponseHandlerProvider().handler; const getUnhashableStates = Private(getUnhashableStatesProvider); const shareContextMenuExtensions = Private(ShareContextMenuExtensionsRegistryProvider); const queryFilter = Private(FilterBarQueryFilterProvider); - const filterGen = getFilterGenerator(queryFilter); const inspectorAdapters = { requests: new RequestAdapter() @@ -901,7 +897,8 @@ function discoverController( // TODO: On array fields, negating does not negate the combination, rather all terms $scope.filterQuery = function (field, values, operation) { $scope.indexPattern.popularizeField(field, 1); - filterActions.addFilter(field, values, operation, $scope.indexPattern.id, $scope.state, filterGen); + const newFilters = generateFilters(queryFilter, field, values, operation, $scope.indexPattern.id); + return queryFilter.addFilters(newFilters); }; $scope.addColumn = function addColumn(columnName) { diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/__tests__/actions/filter.js b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/__tests__/actions/filter.js deleted file mode 100644 index 1f5db791469b9..0000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/__tests__/actions/filter.js +++ /dev/null @@ -1,66 +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 { addFilter } from '../../actions/filter'; -import StubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import NoDigestPromises from 'test_utils/no_digest_promises'; -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import sinon from 'sinon'; - -function getFilterGeneratorStub() { - return { - add: sinon.stub() - }; -} - -describe('doc table filter actions', function () { - NoDigestPromises.activateForSuite(); - - let filterGen; - let indexPattern; - - beforeEach(ngMock.module( - 'kibana', - function ($provide) { - $provide.service('indexPatterns', require('fixtures/mock_index_patterns')); - } - )); - - beforeEach(ngMock.inject(function (Private) { - indexPattern = Private(StubbedLogstashIndexPatternProvider); - filterGen = getFilterGeneratorStub(); - })); - - describe('add', function () { - - it('should defer to the FilterManager when dealing with a lucene query', function () { - const state = { - query: { query: 'foo', language: 'lucene' } - }; - const args = ['foo', ['bar'], '+', indexPattern, ]; - addFilter('foo', ['bar'], '+', indexPattern, state, filterGen); - expect(filterGen.add.calledOnce).to.be(true); - expect(filterGen.add.calledWith(...args)).to.be(true); - }); - - }); - - -}); diff --git a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search.test.tsx b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search.test.tsx index badfbb4b14a4c..5054f7b4bdad1 100644 --- a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search.test.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search.test.tsx @@ -121,7 +121,7 @@ describe('DiscoverFieldSearch', () => { // @ts-ignore (aggregtableButtonGroup.props() as EuiButtonGroupProps).onChange('aggregatable-true', null); }); - missingSwitch.simulate('change', { target: { value: false } }); + missingSwitch.simulate('click'); expect(onChange).toBeCalledTimes(2); }); diff --git a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search.tsx b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search.tsx index 3d93487d9e6cc..d5f6b63d12199 100644 --- a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search.tsx @@ -29,6 +29,7 @@ import { EuiPopoverTitle, EuiSelect, EuiSwitch, + EuiSwitchEvent, EuiForm, EuiFormRow, EuiButtonGroup, @@ -154,7 +155,7 @@ export function DiscoverFieldSearch({ onChange, value, types }: Props) { setActiveFiltersCount(activeFiltersCount + diff); }; - const handleMissingChange = (e: React.ChangeEvent) => { + const handleMissingChange = (e: EuiSwitchEvent) => { const missingValue = e.target.checked; handleValueChange('missing', missingValue); }; diff --git a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts index 732fb6d2e4e70..c575465a377e2 100644 --- a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts +++ b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts @@ -25,10 +25,12 @@ import { npStart } from 'ui/new_platform'; import { esFilters, TimeRange, + FilterManager, onlyDisabledFiltersChanged, + generateFilters, getTime, + Query, } from '../../../../../../plugins/data/public'; -import { Query } from '../../../../data/public'; import { APPLY_FILTER_TRIGGER, Container, @@ -43,7 +45,6 @@ import { getSortForSearchSource } from '../angular/doc_table/lib/get_sort_for_se import { Adapters, angular, - getFilterGenerator, getRequestInspectorStats, getResponseInspectorStats, getServices, @@ -72,18 +73,6 @@ interface SearchScope extends ng.IScope { isLoading?: boolean; } -export interface FilterManager { - generate: ( - field: { - name: string; - scripted: boolean; - }, - values: string | string[], - operation: string, - index: number - ) => esFilters.Filter[]; -} - interface SearchEmbeddableConfig { $rootScope: ng.IRootScopeService; $compile: ng.ICompileService; @@ -107,7 +96,7 @@ export class SearchEmbeddable extends Embeddable private autoRefreshFetchSubscription?: Subscription; private subscription?: Subscription; public readonly type = SEARCH_EMBEDDABLE_TYPE; - private filterGen: FilterManager; + private filterManager: FilterManager; private abortController?: AbortController; private prevTimeRange?: TimeRange; @@ -134,7 +123,7 @@ export class SearchEmbeddable extends Embeddable parent ); - this.filterGen = getFilterGenerator(queryFilter); + this.filterManager = queryFilter as FilterManager; this.savedSearch = savedSearch; this.$rootScope = $rootScope; this.$compile = $compile; @@ -251,7 +240,7 @@ export class SearchEmbeddable extends Embeddable }; searchScope.filter = async (field, value, operator) => { - let filters = this.filterGen.generate(field, value, operator, indexPattern.id); + let filters = generateFilters(this.filterManager, field, value, operator, indexPattern.id); filters = filters.map(filter => ({ ...filter, $state: { store: esFilters.FilterStateStore.APP_STATE }, diff --git a/src/legacy/core_plugins/kibana/public/discover/embeddable/types.ts b/src/legacy/core_plugins/kibana/public/discover/embeddable/types.ts index 5473ec0e7b8b4..2d940ad8cba98 100644 --- a/src/legacy/core_plugins/kibana/public/discover/embeddable/types.ts +++ b/src/legacy/core_plugins/kibana/public/discover/embeddable/types.ts @@ -17,13 +17,11 @@ * under the License. */ -import { TimeRange } from 'src/plugins/data/public'; -import { Query } from 'src/legacy/core_plugins/data/public'; import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from 'src/plugins/embeddable/public'; import { StaticIndexPattern } from '../kibana_services'; import { SavedSearch } from '../types'; import { SortOrder } from '../angular/doc_table/components/table_header/helpers'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters, TimeRange, Query } from '../../../../../../plugins/data/public'; export interface SearchInput extends EmbeddableInput { timeRange: TimeRange; diff --git a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts index b78d05e68acad..d0eb115e32676 100644 --- a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts @@ -84,8 +84,6 @@ export { angular }; export { buildVislibDimensions } from 'ui/visualize/loader/pipeline_helpers/build_pipeline'; // @ts-ignore export { callAfterBindingsWorkaround } from 'ui/compat'; -// @ts-ignore -export { getFilterGenerator } from 'ui/filter_manager'; export { getRequestInspectorStats, getResponseInspectorStats, @@ -114,7 +112,7 @@ export { tabifyAggResponse } from 'ui/agg_response/tabify'; export { vislibSeriesResponseHandlerProvider } from 'ui/vis/response_handlers/vislib'; // EXPORT types -export { VisProvider } from 'ui/vis'; +export { Vis } from 'ui/vis'; export { StaticIndexPattern, IndexPatterns, IndexPattern, FieldType } from 'ui/index_patterns'; export { SearchSource } from 'ui/courier'; export { ElasticSearchHit } from 'ui/registry/doc_views_types'; diff --git a/src/legacy/core_plugins/kibana/public/kibana.js b/src/legacy/core_plugins/kibana/public/kibana.js index fe741a357cbfe..c5b9d86b57aae 100644 --- a/src/legacy/core_plugins/kibana/public/kibana.js +++ b/src/legacy/core_plugins/kibana/public/kibana.js @@ -37,7 +37,6 @@ import 'uiExports/navbarExtensions'; import 'uiExports/contextMenuActions'; import 'uiExports/managementSections'; import 'uiExports/indexManagement'; -import 'uiExports/devTools'; import 'uiExports/docViews'; import 'uiExports/embeddableFactories'; import 'uiExports/embeddableActions'; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/__jest__/extract_export_details.test.ts b/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/__jest__/extract_export_details.test.ts index a6ed2e36839f4..4ecc3583e76ce 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/__jest__/extract_export_details.test.ts +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/__jest__/extract_export_details.test.ts @@ -62,7 +62,10 @@ describe('extractExportDetails', () => { [ [ objLine('1', 'index-pattern'), - detailsLine(1, [{ id: '2', type: 'index-pattern' }, { id: '3', type: 'index-pattern' }]), + detailsLine(1, [ + { id: '2', type: 'index-pattern' }, + { id: '3', type: 'index-pattern' }, + ]), ].join(''), ], { @@ -75,7 +78,10 @@ describe('extractExportDetails', () => { expect(result).toEqual({ exportedCount: 1, missingRefCount: 2, - missingReferences: [{ id: '2', type: 'index-pattern' }, { id: '3', type: 'index-pattern' }], + missingReferences: [ + { id: '2', type: 'index-pattern' }, + { id: '3', type: 'index-pattern' }, + ], }); }); diff --git a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts index 60cf7c7ec1928..0b75c6ffa1ffb 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts @@ -33,11 +33,12 @@ import { npStart } from 'ui/new_platform'; import { IExpressionLoaderParams } from '../../../../expressions/public/np_ready/public/types'; import { start as expressions } from '../../../../expressions/public/legacy'; import { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; -import { Query } from '../../../../data/public'; import { TimeRange, + Query, onlyDisabledFiltersChanged, esFilters, + mapAndFlattenFilters, } from '../../../../../../plugins/data/public'; import { EmbeddableInput, @@ -47,7 +48,6 @@ import { APPLY_FILTER_TRIGGER, } from '../../../../../../plugins/embeddable/public'; import { dispatchRenderComplete } from '../../../../../../plugins/kibana_utils/public'; -import { mapAndFlattenFilters } from '../../../../../../plugins/data/public'; const getKeys = (o: T): Array => Object.keys(o) as Array; diff --git a/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/_saved_vis.js b/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/_saved_vis.js index f8adaed0bf584..aec80b8d13551 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/_saved_vis.js +++ b/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/_saved_vis.js @@ -25,7 +25,7 @@ * NOTE: It's a type of SavedObject, but specific to visualizations. */ -import { VisProvider } from 'ui/vis'; +import { Vis } from 'ui/vis'; import { uiModules } from 'ui/modules'; import { updateOldState } from 'ui/vis/vis_update_state'; import { VisualizeConstants } from '../visualize_constants'; @@ -39,7 +39,6 @@ import { uiModules .get('app/visualize') .factory('SavedVis', function (Promise, savedSearches, Private) { - const Vis = Private(VisProvider); const SavedObject = Private(SavedObjectProvider); createLegacyClass(SavedVis).inherits(SavedObject); function SavedVis(opts) { diff --git a/src/legacy/core_plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx b/src/legacy/core_plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx index d4d1c159906aa..4d3b72bae6411 100644 --- a/src/legacy/core_plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx +++ b/src/legacy/core_plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx @@ -45,6 +45,7 @@ export function TopNavMenuItem(props: TopNavMenuData) { isDisabled={isDisabled()} onClick={handleClick} data-test-subj={props.testId} + className={props.className} > {capitalize(props.label || props.id!)} diff --git a/src/legacy/core_plugins/region_map/public/__tests__/region_map_visualization.js b/src/legacy/core_plugins/region_map/public/__tests__/region_map_visualization.js index 9bfa413257967..b57fbd637f0b7 100644 --- a/src/legacy/core_plugins/region_map/public/__tests__/region_map_visualization.js +++ b/src/legacy/core_plugins/region_map/public/__tests__/region_map_visualization.js @@ -22,7 +22,7 @@ import ngMock from 'ng_mock'; import _ from 'lodash'; import ChoroplethLayer from '../choropleth_layer'; import LogstashIndexPatternStubProvider from 'fixtures/stubbed_logstash_index_pattern'; -import * as visModule from 'ui/vis'; +import { Vis } from 'ui/vis'; import { ImageComparator } from 'test_utils/image_comparator'; import worldJson from './world.json'; import EMS_CATALOGUE from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_manifest.json'; @@ -50,7 +50,6 @@ const PIXEL_DIFF = 96; describe('RegionMapsVisualizationTests', function () { let domNode; let RegionMapsVisualization; - let Vis; let indexPattern; let vis; let dependencies; @@ -113,7 +112,6 @@ describe('RegionMapsVisualizationTests', function () { visualizationsSetup.types.registerVisualization(() => createRegionMapTypeDefinition(dependencies)); } - Vis = Private(visModule.VisProvider); RegionMapsVisualization = createRegionMapVisualization(dependencies); indexPattern = Private(LogstashIndexPatternStubProvider); diff --git a/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx b/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx index 9749c7fa8e2f9..8306b3274a914 100644 --- a/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx +++ b/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx @@ -82,7 +82,10 @@ function RegionMapOptions(props: RegionMapOptionsProps) { const setField = useCallback( (paramName: 'selectedJoinField', value: FileLayerField['name']) => { if (stateParams.selectedLayer) { - setValue(paramName, stateParams.selectedLayer.fields.find(f => f.name === value)); + setValue( + paramName, + stateParams.selectedLayer.fields.find(f => f.name === value) + ); } }, [setValue, stateParams.selectedLayer] diff --git a/src/legacy/core_plugins/telemetry/index.ts b/src/legacy/core_plugins/telemetry/index.ts index 149fa99938563..9993f2dbf0b86 100644 --- a/src/legacy/core_plugins/telemetry/index.ts +++ b/src/legacy/core_plugins/telemetry/index.ts @@ -50,12 +50,9 @@ const telemetry = (kibana: any) => { allowChangingOptInStatus: Joi.boolean().default(true), optIn: Joi.when('allowChangingOptInStatus', { is: false, - then: Joi.valid(true).required(), - otherwise: Joi.boolean() - .allow(null) - .default(null), + then: Joi.valid(true).default(true), + otherwise: Joi.boolean().default(true), }), - // `config` is used internally and not intended to be set config: Joi.string().default(Joi.ref('$defaultConfigPath')), banner: Joi.boolean().default(true), @@ -68,6 +65,15 @@ const telemetry = (kibana: any) => { `https://telemetry.elastic.co/xpack/${ENDPOINT_VERSION}/send` ), }), + optInStatusUrl: Joi.when('$dev', { + is: true, + then: Joi.string().default( + `https://telemetry-staging.elastic.co/opt_in_status/${ENDPOINT_VERSION}/send` + ), + otherwise: Joi.string().default( + `https://telemetry.elastic.co/opt_in_status/${ENDPOINT_VERSION}/send` + ), + }), sendUsageFrom: Joi.string() .allow(['server', 'browser']) .default('browser'), @@ -103,6 +109,7 @@ const telemetry = (kibana: any) => { config.get('telemetry.allowChangingOptInStatus') !== false && getXpackConfigWithDeprecated(config, 'telemetry.banner'), telemetryOptedIn: config.get('telemetry.optIn'), + telemetryOptInStatusUrl: config.get('telemetry.optInStatusUrl'), allowChangingOptInStatus: config.get('telemetry.allowChangingOptInStatus'), telemetrySendUsageFrom: config.get('telemetry.sendUsageFrom'), }; @@ -142,7 +149,6 @@ const telemetry = (kibana: any) => { } as any) as CoreSetup; telemetryPlugin(initializerContext).setup(coreSetup); - // register collectors server.usage.collectorSet.register(createTelemetryPluginUsageCollector(server)); server.usage.collectorSet.register(createLocalizationUsageCollector(server)); diff --git a/src/legacy/core_plugins/telemetry/public/components/__snapshots__/telemetry_form.test.js.snap b/src/legacy/core_plugins/telemetry/public/components/__snapshots__/telemetry_form.test.js.snap index b96313fd700ac..a7f8d72e016f8 100644 --- a/src/legacy/core_plugins/telemetry/public/components/__snapshots__/telemetry_form.test.js.snap +++ b/src/legacy/core_plugins/telemetry/public/components/__snapshots__/telemetry_form.test.js.snap @@ -34,7 +34,8 @@ exports[`TelemetryForm renders as expected when allows to change optIn status 1` save={[Function]} setting={ Object { - "defVal": false, + "ariaName": "Provide usage statistics", + "defVal": true, "description":

Help us improve the Elastic Stack by providing usage statistics for basic features. We will not share this data outside of Elastic. diff --git a/src/legacy/core_plugins/telemetry/public/components/telemetry_form.js b/src/legacy/core_plugins/telemetry/public/components/telemetry_form.js index 80eb2da59c47e..6c6ace71af4d0 100644 --- a/src/legacy/core_plugins/telemetry/public/components/telemetry_form.js +++ b/src/legacy/core_plugins/telemetry/public/components/telemetry_form.js @@ -33,6 +33,7 @@ import { getConfigTelemetryDesc, PRIVACY_STATEMENT_URL } from '../../common/cons import { OptInExampleFlyout } from './opt_in_details_component'; import { Field } from 'ui/management'; import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; const SEARCH_TERMS = ['telemetry', 'usage', 'data', 'usage data']; @@ -116,7 +117,8 @@ export class TelemetryForm extends Component { type: 'boolean', value: telemetryOptInProvider.getOptIn() || false, description: this.renderDescription(), - defVal: false, + defVal: true, + ariaName: i18n.translate('telemetry.provideUsageStatisticsLabel', { defaultMessage: 'Provide usage statistics' }) }} save={this.toggleOptIn} clear={this.toggleOptIn} diff --git a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/click_banner.test.js b/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/click_banner.test.js index 6e9a9fc8443ba..54557f100f4aa 100644 --- a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/click_banner.test.js +++ b/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/click_banner.test.js @@ -54,7 +54,7 @@ const getTelemetryOptInProvider = ({ simulateFailure = false, simulateError = fa addBasePath: (url) => url }; - const provider = new TelemetryOptInProvider(injector, chrome); + const provider = new TelemetryOptInProvider(injector, chrome, false); if (simulateError) { provider.setOptIn = () => Promise.reject('unhandled error'); diff --git a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/handle_old_settings.test.js b/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/handle_old_settings.test.js index f26ca0ca0e3c5..d78a4a3e92362 100644 --- a/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/handle_old_settings.test.js +++ b/src/legacy/core_plugins/telemetry/public/hacks/welcome_banner/handle_old_settings.test.js @@ -49,7 +49,7 @@ const getTelemetryOptInProvider = (enabled, { simulateFailure = false } = {}) => } }; - return new TelemetryOptInProvider($injector, chrome); + return new TelemetryOptInProvider($injector, chrome, false); }; describe('handle_old_settings', () => { diff --git a/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.test.js b/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.test.js index 26f14fc87d937..b0ebb9e7382f6 100644 --- a/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.test.js +++ b/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.test.js @@ -48,7 +48,7 @@ describe('TelemetryOptInProvider', () => { } }; - const provider = new TelemetryOptInProvider(mockInjector, mockChrome); + const provider = new TelemetryOptInProvider(mockInjector, mockChrome, false); return { provider, mockHttp, diff --git a/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.ts b/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.ts index f7b09b1befafa..9b32f88df1218 100644 --- a/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.ts +++ b/src/legacy/core_plugins/telemetry/public/services/telemetry_opt_in.ts @@ -26,7 +26,36 @@ import { i18n } from '@kbn/i18n'; let bannerId: string | null = null; let currentOptInStatus = false; -export function TelemetryOptInProvider($injector: any, chrome: any) { +async function sendOptInStatus($injector: any, chrome: any, enabled: boolean) { + const telemetryOptInStatusUrl = npStart.core.injectedMetadata.getInjectedVar( + 'telemetryOptInStatusUrl' + ) as string; + const $http = $injector.get('$http'); + + try { + const optInStatus = await $http.post( + chrome.addBasePath('/api/telemetry/v2/clusters/_opt_in_stats'), + { + enabled, + unencrypted: false, + } + ); + + if (optInStatus.data && optInStatus.data.length) { + return await fetch(telemetryOptInStatusUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(optInStatus.data), + }); + } + } catch (err) { + // Sending the ping is best-effort. Telemetry tries to send the ping once and discards it immediately if sending fails. + // swallow any errors + } +} +export function TelemetryOptInProvider($injector: any, chrome: any, sendOptInStatusChange = true) { currentOptInStatus = npStart.core.injectedMetadata.getInjectedVar('telemetryOptedIn') as boolean; const allowChangingOptInStatus = npStart.core.injectedMetadata.getInjectedVar( 'allowChangingOptInStatus' @@ -49,6 +78,9 @@ export function TelemetryOptInProvider($injector: any, chrome: any) { try { await $http.post(chrome.addBasePath('/api/telemetry/v2/optIn'), { enabled }); + if (sendOptInStatusChange) { + await sendOptInStatus($injector, chrome, enabled); + } currentOptInStatus = enabled; } catch (error) { toastNotifications.addError(error, { diff --git a/src/legacy/core_plugins/telemetry/server/collection_manager.ts b/src/legacy/core_plugins/telemetry/server/collection_manager.ts index 19bc735b9a965..799d9f4ee9c8b 100644 --- a/src/legacy/core_plugins/telemetry/server/collection_manager.ts +++ b/src/legacy/core_plugins/telemetry/server/collection_manager.ts @@ -18,83 +18,186 @@ */ import { encryptTelemetry } from './collectors'; +import { CallCluster } from '../../elasticsearch'; export type EncryptedStatsGetterConfig = { unencrypted: false } & { server: any; - start: any; - end: any; - isDev: boolean; + start: string; + end: string; }; export type UnencryptedStatsGetterConfig = { unencrypted: true } & { req: any; - start: any; - end: any; - isDev: boolean; + start: string; + end: string; }; +export interface ClusterDetails { + clusterUuid: string; +} + export interface StatsCollectionConfig { - callCluster: any; + callCluster: CallCluster; server: any; - start: any; - end: any; + start: string; + end: string; } export type StatsGetterConfig = UnencryptedStatsGetterConfig | EncryptedStatsGetterConfig; +export type ClusterDetailsGetter = (config: StatsCollectionConfig) => Promise; +export type StatsGetter = ( + clustersDetails: ClusterDetails[], + config: StatsCollectionConfig +) => Promise; -export type StatsGetter = (config: StatsGetterConfig) => Promise; - -export const getStatsCollectionConfig = ( - config: StatsGetterConfig, - esClustser: string -): StatsCollectionConfig => { - const { start, end } = config; - const server = config.unencrypted ? config.req.server : config.server; - const { callWithRequest, callWithInternalUser } = server.plugins.elasticsearch.getCluster( - esClustser - ); - const callCluster = config.unencrypted - ? (...args: any[]) => callWithRequest(config.req, ...args) - : callWithInternalUser; - - return { server, callCluster, start, end }; -}; +interface CollectionConfig { + title: string; + priority: number; + esCluster: string; + statsGetter: StatsGetter; + clusterDetailsGetter: ClusterDetailsGetter; +} +interface Collection { + statsGetter: StatsGetter; + clusterDetailsGetter: ClusterDetailsGetter; + esCluster: string; + title: string; +} export class TelemetryCollectionManager { - private getterMethod?: StatsGetter; - private collectionTitle?: string; - private getterMethodPriority = -1; - - public setStatsGetter = (statsGetter: StatsGetter, title: string, priority = 0) => { - if (priority > this.getterMethodPriority) { - this.getterMethod = statsGetter; - this.collectionTitle = title; - this.getterMethodPriority = priority; + private usageGetterMethodPriority = -1; + private collections: Collection[] = []; + + public setCollection = (collectionConfig: CollectionConfig) => { + const { title, priority, esCluster, statsGetter, clusterDetailsGetter } = collectionConfig; + + if (typeof priority !== 'number') { + throw new Error('priority must be set.'); + } + if (priority === this.usageGetterMethodPriority) { + throw new Error(`A Usage Getter with the same priority is already set.`); } - }; - private getStats = async (config: StatsGetterConfig) => { - if (!this.getterMethod) { - throw Error('Stats getter method not set.'); + if (priority > this.usageGetterMethodPriority) { + if (!statsGetter) { + throw Error('Stats getter method not set.'); + } + if (!esCluster) { + throw Error('esCluster name must be set for the getCluster method.'); + } + if (!clusterDetailsGetter) { + throw Error('Cluser UUIds method is not set.'); + } + + this.collections.unshift({ + statsGetter, + clusterDetailsGetter, + esCluster, + title, + }); + this.usageGetterMethodPriority = priority; } - const usageData = await this.getterMethod(config); + }; - if (config.unencrypted) return usageData; - return encryptTelemetry(usageData, config.isDev); + private getStatsCollectionConfig = async ( + collection: Collection, + config: StatsGetterConfig + ): Promise => { + const { start, end } = config; + const server = config.unencrypted ? config.req.server : config.server; + const { callWithRequest, callWithInternalUser } = server.plugins.elasticsearch.getCluster( + collection.esCluster + ); + const callCluster = config.unencrypted + ? (...args: any[]) => callWithRequest(config.req, ...args) + : callWithInternalUser; + + return { server, callCluster, start, end }; }; - public getCollectionTitle = () => { - return this.collectionTitle; + + private getOptInStatsForCollection = async ( + collection: Collection, + optInStatus: boolean, + statsCollectionConfig: StatsCollectionConfig + ) => { + const clustersDetails = await collection.clusterDetailsGetter(statsCollectionConfig); + return clustersDetails.map(({ clusterUuid }) => ({ + cluster_uuid: clusterUuid, + opt_in_status: optInStatus, + })); }; - public getStatsGetter = () => { - if (!this.getterMethod) { - throw Error('Stats getter method not set.'); + private getUsageForCollection = async ( + collection: Collection, + statsCollectionConfig: StatsCollectionConfig + ) => { + const clustersDetails = await collection.clusterDetailsGetter(statsCollectionConfig); + + if (clustersDetails.length === 0) { + // don't bother doing a further lookup, try next collection. + return; } - return { - getStats: this.getStats, - priority: this.getterMethodPriority, - title: this.collectionTitle, - }; + + return await collection.statsGetter(clustersDetails, statsCollectionConfig); + }; + + public getOptInStats = async (optInStatus: boolean, config: StatsGetterConfig) => { + for (const collection of this.collections) { + const statsCollectionConfig = await this.getStatsCollectionConfig(collection, config); + try { + const optInStats = await this.getOptInStatsForCollection( + collection, + optInStatus, + statsCollectionConfig + ); + if (optInStats && optInStats.length) { + statsCollectionConfig.server.log( + ['debug', 'telemetry', 'collection'], + `Got Opt In stats using ${collection.title} collection.` + ); + if (config.unencrypted) { + return optInStats; + } + const isDev = statsCollectionConfig.server.config().get('env.dev'); + return encryptTelemetry(optInStats, isDev); + } + } catch (err) { + statsCollectionConfig.server.log( + ['debu', 'telemetry', 'collection'], + `Failed to collect any opt in stats with registered collections.` + ); + // swallow error to try next collection; + } + } + + return []; + }; + public getStats = async (config: StatsGetterConfig) => { + for (const collection of this.collections) { + const statsCollectionConfig = await this.getStatsCollectionConfig(collection, config); + try { + const usageData = await this.getUsageForCollection(collection, statsCollectionConfig); + if (usageData && usageData.length) { + statsCollectionConfig.server.log( + ['debug', 'telemetry', 'collection'], + `Got Usage using ${collection.title} collection.` + ); + if (config.unencrypted) { + return usageData; + } + const isDev = statsCollectionConfig.server.config().get('env.dev'); + return encryptTelemetry(usageData, isDev); + } + } catch (err) { + statsCollectionConfig.server.log( + ['debu', 'telemetry', 'collection'], + `Failed to collect any usage with registered collections.` + ); + // swallow error to try next collection; + } + } + + return []; }; } diff --git a/src/legacy/core_plugins/telemetry/server/collectors/telemetry_plugin/telemetry_plugin_collector.ts b/src/legacy/core_plugins/telemetry/server/collectors/telemetry_plugin/telemetry_plugin_collector.ts index e092ceb5e8593..a172ba7dc6955 100644 --- a/src/legacy/core_plugins/telemetry/server/collectors/telemetry_plugin/telemetry_plugin_collector.ts +++ b/src/legacy/core_plugins/telemetry/server/collectors/telemetry_plugin/telemetry_plugin_collector.ts @@ -19,7 +19,7 @@ import { TELEMETRY_STATS_TYPE } from '../../../common/constants'; import { getTelemetrySavedObject, TelemetrySavedObject } from '../../telemetry_repository'; -import { getTelemetryOptIn, getTelemetryUsageFetcher } from '../../telemetry_config'; +import { getTelemetryOptIn, getTelemetrySendUsageFrom } from '../../telemetry_config'; export interface TelemetryUsageStats { opt_in_status?: boolean | null; usage_fetcher?: 'browser' | 'server'; @@ -53,7 +53,7 @@ export function createCollectorFetch(server: any) { configTelemetryOptIn, }), last_reported: telemetrySavedObject ? telemetrySavedObject.lastReported : undefined, - usage_fetcher: getTelemetryUsageFetcher({ + usage_fetcher: getTelemetrySendUsageFrom({ telemetrySavedObject, configTelemetrySendUsageFrom, }), diff --git a/src/legacy/core_plugins/telemetry/server/collectors/usage/ensure_deep_object.ts b/src/legacy/core_plugins/telemetry/server/collectors/usage/ensure_deep_object.ts index 6594c7f8e7a6f..3b7a9355da746 100644 --- a/src/legacy/core_plugins/telemetry/server/collectors/usage/ensure_deep_object.ts +++ b/src/legacy/core_plugins/telemetry/server/collectors/usage/ensure_deep_object.ts @@ -42,19 +42,16 @@ export function ensureDeepObject(obj: any): any { return obj.map(item => ensureDeepObject(item)); } - return Object.keys(obj).reduce( - (fullObject, propertyKey) => { - const propertyValue = obj[propertyKey]; - if (!propertyKey.includes(separator)) { - fullObject[propertyKey] = ensureDeepObject(propertyValue); - } else { - walk(fullObject, propertyKey.split(separator), propertyValue); - } + return Object.keys(obj).reduce((fullObject, propertyKey) => { + const propertyValue = obj[propertyKey]; + if (!propertyKey.includes(separator)) { + fullObject[propertyKey] = ensureDeepObject(propertyValue); + } else { + walk(fullObject, propertyKey.split(separator), propertyValue); + } - return fullObject; - }, - {} as any - ); + return fullObject; + }, {} as any); } function walk(obj: any, keys: string[], value: any) { diff --git a/src/legacy/core_plugins/telemetry/server/fetcher.ts b/src/legacy/core_plugins/telemetry/server/fetcher.ts index 43883395eac99..9edd8457f2b89 100644 --- a/src/legacy/core_plugins/telemetry/server/fetcher.ts +++ b/src/legacy/core_plugins/telemetry/server/fetcher.ts @@ -21,7 +21,7 @@ import moment from 'moment'; // @ts-ignore import fetch from 'node-fetch'; import { telemetryCollectionManager } from './collection_manager'; -import { getTelemetryOptIn, getTelemetryUsageFetcher } from './telemetry_config'; +import { getTelemetryOptIn, getTelemetrySendUsageFrom } from './telemetry_config'; import { getTelemetrySavedObject, updateTelemetrySavedObject } from './telemetry_repository'; import { REPORT_INTERVAL_MS } from '../common/constants'; import { getXpackConfigWithDeprecated } from '../common/get_xpack_config_with_deprecated'; @@ -61,7 +61,7 @@ export class FetcherTask { allowChangingOptInStatus, configTelemetryOptIn, }), - telemetrySendUsageFrom: getTelemetryUsageFetcher({ + telemetrySendUsageFrom: getTelemetrySendUsageFrom({ telemetrySavedObject, configTelemetrySendUsageFrom, }), @@ -87,18 +87,13 @@ export class FetcherTask { }; private fetchTelemetry = async () => { - const { getStats, title } = telemetryCollectionManager.getStatsGetter(); - this.server.log(['debug', 'telemetry', 'fetcher'], `Fetching usage using ${title} getter.`); - const config = this.server.config(); - - return await getStats({ + return await telemetryCollectionManager.getStats({ unencrypted: false, server: this.server, start: moment() .subtract(20, 'minutes') .toISOString(), end: moment().toISOString(), - isDev: config.get('env.dev'), }); }; diff --git a/src/legacy/core_plugins/telemetry/server/plugin.ts b/src/legacy/core_plugins/telemetry/server/plugin.ts index a5f0f1234799a..f2628090c08af 100644 --- a/src/legacy/core_plugins/telemetry/server/plugin.ts +++ b/src/legacy/core_plugins/telemetry/server/plugin.ts @@ -19,8 +19,7 @@ import { CoreSetup, PluginInitializerContext } from 'src/core/server'; import { registerRoutes } from './routes'; -import { telemetryCollectionManager } from './collection_manager'; -import { getStats } from './telemetry_collection'; +import { registerCollection } from './telemetry_collection'; export class TelemetryPlugin { private readonly currentKibanaVersion: string; @@ -31,7 +30,7 @@ export class TelemetryPlugin { public setup(core: CoreSetup) { const currentKibanaVersion = this.currentKibanaVersion; - telemetryCollectionManager.setStatsGetter(getStats, 'local'); + registerCollection(); registerRoutes({ core, currentKibanaVersion }); } } diff --git a/src/legacy/core_plugins/telemetry/server/routes/index.ts b/src/legacy/core_plugins/telemetry/server/routes/index.ts index 93654f6470555..66a7b2c97f3ae 100644 --- a/src/legacy/core_plugins/telemetry/server/routes/index.ts +++ b/src/legacy/core_plugins/telemetry/server/routes/index.ts @@ -18,8 +18,9 @@ */ import { CoreSetup } from 'src/core/server'; -import { registerTelemetryConfigRoutes } from './telemetry_config'; -import { registerTelemetryDataRoutes } from './telemetry_stats'; +import { registerTelemetryOptInRoutes } from './telemetry_opt_in'; +import { registerTelemetryUsageStatsRoutes } from './telemetry_usage_stats'; +import { registerTelemetryOptInStatsRoutes } from './telemetry_opt_in_stats'; interface RegisterRoutesParams { core: CoreSetup; @@ -27,6 +28,7 @@ interface RegisterRoutesParams { } export function registerRoutes({ core, currentKibanaVersion }: RegisterRoutesParams) { - registerTelemetryConfigRoutes({ core, currentKibanaVersion }); - registerTelemetryDataRoutes(core); + registerTelemetryOptInRoutes({ core, currentKibanaVersion }); + registerTelemetryUsageStatsRoutes(core); + registerTelemetryOptInStatsRoutes(core); } diff --git a/src/legacy/core_plugins/telemetry/server/routes/telemetry_config.ts b/src/legacy/core_plugins/telemetry/server/routes/telemetry_opt_in.ts similarity index 74% rename from src/legacy/core_plugins/telemetry/server/routes/telemetry_config.ts rename to src/legacy/core_plugins/telemetry/server/routes/telemetry_opt_in.ts index 440f83277340a..596c5c17c353e 100644 --- a/src/legacy/core_plugins/telemetry/server/routes/telemetry_config.ts +++ b/src/legacy/core_plugins/telemetry/server/routes/telemetry_opt_in.ts @@ -18,9 +18,12 @@ */ import Joi from 'joi'; +import moment from 'moment'; import { boomify } from 'boom'; import { CoreSetup } from 'src/core/server'; import { getTelemetryAllowChangingOptInStatus } from '../telemetry_config'; +import { sendTelemetryOptInStatus } from './telemetry_opt_in_stats'; + import { TelemetrySavedObjectAttributes, updateTelemetrySavedObject, @@ -31,7 +34,7 @@ interface RegisterOptInRoutesParams { currentKibanaVersion: string; } -export function registerTelemetryConfigRoutes({ +export function registerTelemetryOptInRoutes({ core, currentKibanaVersion, }: RegisterOptInRoutesParams) { @@ -49,8 +52,9 @@ export function registerTelemetryConfigRoutes({ }, handler: async (req: any, h: any) => { try { + const newOptInStatus = req.payload.enabled; const attributes: TelemetrySavedObjectAttributes = { - enabled: req.payload.enabled, + enabled: newOptInStatus, lastVersionChecked: currentKibanaVersion, }; const config = req.server.config(); @@ -58,6 +62,7 @@ export function registerTelemetryConfigRoutes({ const configTelemetryAllowChangingOptInStatus = config.get( 'telemetry.allowChangingOptInStatus' ); + const allowChangingOptInStatus = getTelemetryAllowChangingOptInStatus({ telemetrySavedObject: savedObjectsClient, configTelemetryAllowChangingOptInStatus, @@ -65,11 +70,28 @@ export function registerTelemetryConfigRoutes({ if (!allowChangingOptInStatus) { return h.response({ error: 'Not allowed to change Opt-in Status.' }).code(400); } + + const sendUsageFrom = config.get('telemetry.sendUsageFrom'); + if (sendUsageFrom === 'server') { + const optInStatusUrl = config.get('telemetry.optInStatusUrl'); + await sendTelemetryOptInStatus( + { optInStatusUrl, newOptInStatus }, + { + start: moment() + .subtract(20, 'minutes') + .toISOString(), + end: moment().toISOString(), + server: req.server, + unencrypted: false, + } + ); + } + await updateTelemetrySavedObject(savedObjectsClient, attributes); + return h.response({}).code(200); } catch (err) { return boomify(err); } - return h.response({}).code(200); }, }); } diff --git a/src/legacy/core_plugins/telemetry/server/routes/telemetry_opt_in_stats.ts b/src/legacy/core_plugins/telemetry/server/routes/telemetry_opt_in_stats.ts new file mode 100644 index 0000000000000..d3bf6dbb77d7a --- /dev/null +++ b/src/legacy/core_plugins/telemetry/server/routes/telemetry_opt_in_stats.ts @@ -0,0 +1,87 @@ +/* + * 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. + */ + +// @ts-ignore +import fetch from 'node-fetch'; +import Joi from 'joi'; +import moment from 'moment'; +import { CoreSetup } from 'src/core/server'; +import { telemetryCollectionManager, StatsGetterConfig } from '../collection_manager'; + +interface SendTelemetryOptInStatusConfig { + optInStatusUrl: string; + newOptInStatus: boolean; +} + +export async function sendTelemetryOptInStatus( + config: SendTelemetryOptInStatusConfig, + statsGetterConfig: StatsGetterConfig +) { + const { optInStatusUrl, newOptInStatus } = config; + const optInStatus = await telemetryCollectionManager.getOptInStats( + newOptInStatus, + statsGetterConfig + ); + + await fetch(optInStatusUrl, { + method: 'post', + body: optInStatus, + }); +} + +export function registerTelemetryOptInStatsRoutes(core: CoreSetup) { + const { server } = core.http as any; + + server.route({ + method: 'POST', + path: '/api/telemetry/v2/clusters/_opt_in_stats', + options: { + validate: { + payload: Joi.object({ + enabled: Joi.bool().required(), + unencrypted: Joi.bool().default(true), + }), + }, + }, + handler: async (req: any, h: any) => { + try { + const newOptInStatus = req.payload.enabled; + const unencrypted = req.payload.unencrypted; + const statsGetterConfig = { + start: moment() + .subtract(20, 'minutes') + .toISOString(), + end: moment().toISOString(), + server: req.server, + req, + unencrypted, + }; + + const optInStatus = await telemetryCollectionManager.getOptInStats( + newOptInStatus, + statsGetterConfig + ); + + return h.response(optInStatus).code(200); + } catch (err) { + return h.response([]).code(200); + } + }, + }); +} diff --git a/src/legacy/core_plugins/telemetry/server/routes/telemetry_stats.ts b/src/legacy/core_plugins/telemetry/server/routes/telemetry_usage_stats.ts similarity index 86% rename from src/legacy/core_plugins/telemetry/server/routes/telemetry_stats.ts rename to src/legacy/core_plugins/telemetry/server/routes/telemetry_usage_stats.ts index e87c041a263a5..c14314ca4da24 100644 --- a/src/legacy/core_plugins/telemetry/server/routes/telemetry_stats.ts +++ b/src/legacy/core_plugins/telemetry/server/routes/telemetry_usage_stats.ts @@ -22,7 +22,7 @@ import { boomify } from 'boom'; import { CoreSetup } from 'src/core/server'; import { telemetryCollectionManager } from '../collection_manager'; -export function registerTelemetryDataRoutes(core: CoreSetup) { +export function registerTelemetryUsageStatsRoutes(core: CoreSetup) { const { server } = core.http as any; server.route({ @@ -44,21 +44,17 @@ export function registerTelemetryDataRoutes(core: CoreSetup) { const start = req.payload.timeRange.min; const end = req.payload.timeRange.max; const unencrypted = req.payload.unencrypted; - const isDev = config.get('env.dev'); try { - const { getStats, title } = telemetryCollectionManager.getStatsGetter(); - server.log(['debug', 'telemetry', 'fetcher'], `Fetching usage using ${title} getter.`); - - return await getStats({ + return await telemetryCollectionManager.getStats({ unencrypted, server, req, start, end, - isDev, }); } catch (err) { + const isDev = config.get('env.dev'); if (isDev) { // don't ignore errors when running in dev mode return boomify(err, { statusCode: err.status || 500 }); diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_collection/__tests__/get_cluster_stats.js b/src/legacy/core_plugins/telemetry/server/telemetry_collection/__tests__/get_cluster_stats.js index 9ca609cd88778..d60b330db7b5b 100644 --- a/src/legacy/core_plugins/telemetry/server/telemetry_collection/__tests__/get_cluster_stats.js +++ b/src/legacy/core_plugins/telemetry/server/telemetry_collection/__tests__/get_cluster_stats.js @@ -35,9 +35,9 @@ export function mockGetClusterStats(callCluster, clusterStats, req) { .returns(clusterStats); } -describe('get_cluster_stats', () => { +describe.skip('get_cluster_stats', () => { - it('uses callCluster to get cluster.stats API', () => { + it('uses callCluster to get cluster.stats API', async () => { const callCluster = sinon.stub(); const response = Promise.resolve({}); diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_collection/__tests__/get_local_stats.js b/src/legacy/core_plugins/telemetry/server/telemetry_collection/__tests__/get_local_stats.js index 261012e594b1c..4cbdf18df4a74 100644 --- a/src/legacy/core_plugins/telemetry/server/telemetry_collection/__tests__/get_local_stats.js +++ b/src/legacy/core_plugins/telemetry/server/telemetry_collection/__tests__/get_local_stats.js @@ -152,7 +152,7 @@ describe('get_local_stats', () => { }); }); - describe('getLocalStats', () => { + describe.skip('getLocalStats', () => { it('returns expected object without xpack data when X-Pack fails to respond', async () => { const callClusterUsageFailed = sinon.stub(); diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_cluster_stats.js b/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_cluster_stats.ts similarity index 65% rename from src/legacy/core_plugins/telemetry/server/telemetry_collection/get_cluster_stats.js rename to src/legacy/core_plugins/telemetry/server/telemetry_collection/get_cluster_stats.ts index a840c39812e2c..4abd95f0cf66d 100644 --- a/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_cluster_stats.js +++ b/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_cluster_stats.ts @@ -17,18 +17,24 @@ * under the License. */ +import { CallCluster } from 'src/legacy/core_plugins/elasticsearch'; import { TIMEOUT } from './constants'; - +import { ClusterDetailsGetter } from '../collection_manager'; /** * Get the cluster stats from the connected cluster. * * This is the equivalent to GET /_cluster/stats?timeout=30s. - * - * @param {function} callCluster The callWithInternalUser handler (exposed for testing) - * @return {Promise} The response from Elasticsearch equivalent to GET /_cluster/stats. */ -export function getClusterStats(callCluster) { - return callCluster('cluster.stats', { - timeout: TIMEOUT +export async function getClusterStats(callCluster: CallCluster) { + return await callCluster('cluster.stats', { + timeout: TIMEOUT, }); } + +/** + * Get the cluster uuids from the connected cluster. + */ +export const getClusterUuids: ClusterDetailsGetter = async ({ callCluster }) => { + const result = await getClusterStats(callCluster); + return [{ clusterUuid: result.cluster_uuid }]; +}; diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_stats.js b/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_stats.ts similarity index 72% rename from src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_stats.js rename to src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_stats.ts index 6125dadc3646f..e11c6b1277d5b 100644 --- a/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_stats.js +++ b/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_stats.ts @@ -18,9 +18,12 @@ */ import { get, omit } from 'lodash'; +// @ts-ignore import { getClusterInfo } from './get_cluster_info'; import { getClusterStats } from './get_cluster_stats'; +// @ts-ignore import { getKibana, handleKibanaStats } from './get_kibana'; +import { StatsGetter } from '../collection_manager'; /** * Handle the separate local calls by combining them into a single object response that looks like the @@ -30,9 +33,9 @@ import { getKibana, handleKibanaStats } from './get_kibana'; * @param {Object} clusterStats Cluster stats (GET /_cluster/stats) * @return {Object} A combined object containing the different responses. */ -export function handleLocalStats(server, clusterInfo, clusterStats, kibana) { +export function handleLocalStats(server: any, clusterInfo: any, clusterStats: any, kibana: any) { return { - timestamp: (new Date()).toISOString(), + timestamp: new Date().toISOString(), cluster_uuid: get(clusterInfo, 'cluster_uuid'), cluster_name: get(clusterInfo, 'cluster_name'), version: get(clusterInfo, 'version.number'), @@ -40,7 +43,7 @@ export function handleLocalStats(server, clusterInfo, clusterStats, kibana) { collection: 'local', stack_stats: { kibana: handleKibanaStats(server, kibana), - } + }, }; } @@ -51,12 +54,16 @@ export function handleLocalStats(server, clusterInfo, clusterStats, kibana) { * @param {function} callCluster The callWithInternalUser handler (exposed for testing) * @return {Promise} The object containing the current Elasticsearch cluster's telemetry. */ -export async function getLocalStats({ server, callCluster }) { - const [ clusterInfo, clusterStats, kibana ] = await Promise.all([ - getClusterInfo(callCluster), // cluster info - getClusterStats(callCluster), // cluster stats (not to be confused with cluster _state_) - getKibana(server, callCluster), - ]); - - return handleLocalStats(server, clusterInfo, clusterStats, kibana); -} +export const getLocalStats: StatsGetter = async (clustersDetails, config) => { + const { server, callCluster } = config; + return await Promise.all( + clustersDetails.map(async clustersDetail => { + const [clusterInfo, clusterStats, kibana] = await Promise.all([ + getClusterInfo(callCluster), // cluster info + getClusterStats(callCluster), // cluster stats (not to be confused with cluster _state_) + getKibana(server, callCluster), + ]); + return handleLocalStats(server, clusterInfo, clusterStats, kibana); + }) + ); +}; diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_collection/index.ts b/src/legacy/core_plugins/telemetry/server/telemetry_collection/index.ts index f54aaf0ce1bc0..7f228dbc5e6f6 100644 --- a/src/legacy/core_plugins/telemetry/server/telemetry_collection/index.ts +++ b/src/legacy/core_plugins/telemetry/server/telemetry_collection/index.ts @@ -19,4 +19,5 @@ // @ts-ignore export { getLocalStats } from './get_local_stats'; -export { getStats } from './get_stats'; +export { getClusterUuids } from './get_cluster_stats'; +export { registerCollection } from './register_collection'; diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_collection/register_collection.ts b/src/legacy/core_plugins/telemetry/server/telemetry_collection/register_collection.ts new file mode 100644 index 0000000000000..faf8e9de79194 --- /dev/null +++ b/src/legacy/core_plugins/telemetry/server/telemetry_collection/register_collection.ts @@ -0,0 +1,51 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * 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 { telemetryCollectionManager } from '../collection_manager'; +import { getLocalStats } from './get_local_stats'; +import { getClusterUuids } from './get_cluster_stats'; + +export function registerCollection() { + telemetryCollectionManager.setCollection({ + esCluster: 'data', + title: 'local', + priority: 0, + statsGetter: getLocalStats, + clusterDetailsGetter: getClusterUuids, + }); +} diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_opt_in.ts b/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_opt_in.ts index 057a8b0c47958..d83ffdf69b576 100644 --- a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_opt_in.ts +++ b/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_opt_in.ts @@ -44,7 +44,7 @@ export const getTelemetryOptIn: GetTelemetryOptIn = ({ } if (telemetrySavedObject === null || typeof telemetrySavedObject.enabled !== 'boolean') { - return null; + return configTelemetryOptIn; } const savedOptIn = telemetrySavedObject.enabled; diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_usage_fetcher.test.ts b/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_send_usage_from.test.ts similarity index 92% rename from src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_usage_fetcher.test.ts rename to src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_send_usage_from.test.ts index f2f99104433a3..69868a97a931d 100644 --- a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_usage_fetcher.test.ts +++ b/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_send_usage_from.test.ts @@ -17,10 +17,10 @@ * under the License. */ -import { getTelemetryUsageFetcher } from './get_telemetry_usage_fetcher'; +import { getTelemetrySendUsageFrom } from './get_telemetry_send_usage_from'; import { TelemetrySavedObject } from '../telemetry_repository/get_telemetry_saved_object'; -describe('getTelemetryUsageFetcher', () => { +describe('getTelemetrySendUsageFrom', () => { it('returns kibana.yml config when saved object not found', () => { const params: CallGetTelemetryUsageFetcherParams = { savedObjectNotFound: true, @@ -65,7 +65,7 @@ interface CallGetTelemetryUsageFetcherParams { function callGetTelemetryUsageFetcher(params: CallGetTelemetryUsageFetcherParams) { const telemetrySavedObject = getMockTelemetrySavedObject(params); const configTelemetrySendUsageFrom = params.configSendUsageFrom; - return getTelemetryUsageFetcher({ configTelemetrySendUsageFrom, telemetrySavedObject }); + return getTelemetrySendUsageFrom({ configTelemetrySendUsageFrom, telemetrySavedObject }); } function getMockTelemetrySavedObject( diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_usage_fetcher.ts b/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_send_usage_from.ts similarity index 96% rename from src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_usage_fetcher.ts rename to src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_send_usage_from.ts index 98f2d6b0c7bbf..9e4ae14b6097c 100644 --- a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_usage_fetcher.ts +++ b/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_send_usage_from.ts @@ -23,7 +23,7 @@ interface GetTelemetryUsageFetcherConfig { telemetrySavedObject: TelemetrySavedObject; } -export function getTelemetryUsageFetcher({ +export function getTelemetrySendUsageFrom({ telemetrySavedObject, configTelemetrySendUsageFrom, }: GetTelemetryUsageFetcherConfig) { diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/index.ts b/src/legacy/core_plugins/telemetry/server/telemetry_config/index.ts index 25b588b99a3b8..ab30dac1c3666 100644 --- a/src/legacy/core_plugins/telemetry/server/telemetry_config/index.ts +++ b/src/legacy/core_plugins/telemetry/server/telemetry_config/index.ts @@ -19,5 +19,5 @@ export { replaceTelemetryInjectedVars } from './replace_injected_vars'; export { getTelemetryOptIn } from './get_telemetry_opt_in'; -export { getTelemetryUsageFetcher } from './get_telemetry_usage_fetcher'; +export { getTelemetrySendUsageFrom } from './get_telemetry_send_usage_from'; export { getTelemetryAllowChangingOptInStatus } from './get_telemetry_allow_changing_opt_in_status'; diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/replace_injected_vars.ts b/src/legacy/core_plugins/telemetry/server/telemetry_config/replace_injected_vars.ts index c9b4f4ebcd650..90d1f9cfdac65 100644 --- a/src/legacy/core_plugins/telemetry/server/telemetry_config/replace_injected_vars.ts +++ b/src/legacy/core_plugins/telemetry/server/telemetry_config/replace_injected_vars.ts @@ -19,7 +19,7 @@ import { getTelemetrySavedObject } from '../telemetry_repository'; import { getTelemetryOptIn } from './get_telemetry_opt_in'; -import { getTelemetryUsageFetcher } from './get_telemetry_usage_fetcher'; +import { getTelemetrySendUsageFrom } from './get_telemetry_send_usage_from'; import { getTelemetryAllowChangingOptInStatus } from './get_telemetry_allow_changing_opt_in_status'; export async function replaceTelemetryInjectedVars(request: any) { @@ -51,7 +51,7 @@ export async function replaceTelemetryInjectedVars(request: any) { currentKibanaVersion, }); - const telemetrySendUsageFrom = getTelemetryUsageFetcher({ + const telemetrySendUsageFrom = getTelemetrySendUsageFrom({ configTelemetrySendUsageFrom, telemetrySavedObject, }); diff --git a/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js b/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js index 751f92fe88215..0e3c4fdd9d355 100644 --- a/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js +++ b/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js @@ -20,7 +20,7 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import LogstashIndexPatternStubProvider from 'fixtures/stubbed_logstash_index_pattern'; -import * as visModule from 'ui/vis'; +import { Vis } from 'ui/vis'; import { ImageComparator } from 'test_utils/image_comparator'; import dummyESResponse from './dummy_es_response.json'; import initial from './initial.png'; @@ -65,7 +65,6 @@ let visRegComplete = false; describe('CoordinateMapsVisualizationTest', function () { let domNode; let CoordinateMapsVisualization; - let Vis; let indexPattern; let vis; let dependencies; @@ -91,7 +90,6 @@ describe('CoordinateMapsVisualizationTest', function () { } - Vis = Private(visModule.VisProvider); CoordinateMapsVisualization = createTileMapVisualization(dependencies); indexPattern = Private(LogstashIndexPatternStubProvider); diff --git a/src/legacy/core_plugins/tile_map/public/components/wms_options.tsx b/src/legacy/core_plugins/tile_map/public/components/wms_options.tsx index ef6b2eaea1e52..c5ccc3acba610 100644 --- a/src/legacy/core_plugins/tile_map/public/components/wms_options.tsx +++ b/src/legacy/core_plugins/tile_map/public/components/wms_options.tsx @@ -23,23 +23,26 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { TmsLayer } from 'ui/vis/map/service_settings'; +import { Vis } from 'ui/vis'; +import { RegionMapVisParams } from '../../../region_map/public/types'; import { SelectOption, SwitchOption } from '../../../kbn_vislib_vis_types/public/components'; -import { RegionMapOptionsProps } from '../../../region_map/public/components/region_map_options'; import { WmsInternalOptions } from './wms_internal_options'; -import { TileMapOptionsProps } from './tile_map_options'; -import { TileMapVisParams } from '../types'; +import { WMSOptions, TileMapVisParams } from '../types'; + +interface Props { + stateParams: TileMapVisParams | RegionMapVisParams; + setValue: (title: 'wms', options: WMSOptions) => void; + vis: Vis; +} const mapLayerForOption = ({ id }: TmsLayer) => ({ text: id, value: id }); -function WmsOptions({ stateParams, setValue, vis }: TileMapOptionsProps | RegionMapOptionsProps) { +function WmsOptions({ stateParams, setValue, vis }: Props) { const { wms } = stateParams; const { tmsLayers } = vis.type.editorConfig.collections; const tmsLayerOptions = useMemo(() => tmsLayers.map(mapLayerForOption), [tmsLayers]); - const setWmsOption = ( - paramName: T, - value: TileMapVisParams['wms'][T] - ) => + const setWmsOption = (paramName: T, value: WMSOptions[T]) => setValue('wms', { ...wms, [paramName]: value, diff --git a/src/legacy/core_plugins/timelion/public/vis/timelion_request_handler.ts b/src/legacy/core_plugins/timelion/public/vis/timelion_request_handler.ts index 6239e4027c392..35dea4a0deb9b 100644 --- a/src/legacy/core_plugins/timelion/public/vis/timelion_request_handler.ts +++ b/src/legacy/core_plugins/timelion/public/vis/timelion_request_handler.ts @@ -22,8 +22,7 @@ import { buildEsQuery, getEsQueryConfig } from '@kbn/es-query'; // @ts-ignore import { timezoneProvider } from 'ui/vis/lib/timezone'; import { KIBANA_CONTEXT_NAME } from 'src/plugins/expressions/public'; -import { Query } from 'src/legacy/core_plugins/data/public'; -import { TimeRange, esFilters } from 'src/plugins/data/public'; +import { Query, TimeRange, esFilters } from 'src/plugins/data/public'; import { VisParams } from 'ui/vis'; import { i18n } from '@kbn/i18n'; import { TimelionVisualizationDependencies } from '../plugin'; diff --git a/src/legacy/core_plugins/vis_type_metric/public/__tests__/metric_vis.js b/src/legacy/core_plugins/vis_type_metric/public/__tests__/metric_vis.js index 7f626df6a4ea3..384beb3764e2e 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/__tests__/metric_vis.js +++ b/src/legacy/core_plugins/vis_type_metric/public/__tests__/metric_vis.js @@ -21,7 +21,7 @@ import $ from 'jquery'; import ngMock from 'ng_mock'; import expect from '@kbn/expect'; -import { VisProvider } from 'ui/vis'; +import { Vis } from 'ui/vis'; import LogstashIndexPatternStubProvider from 'fixtures/stubbed_logstash_index_pattern'; import { createMetricVisTypeDefinition } from '../metric_vis_type'; @@ -34,7 +34,6 @@ describe('metric_vis - createMetricVisTypeDefinition', () => { beforeEach( ngMock.inject(Private => { setup = () => { - const Vis = Private(VisProvider); const metricVisType = createMetricVisTypeDefinition(); const indexPattern = Private(LogstashIndexPatternStubProvider); diff --git a/src/legacy/core_plugins/vis_type_table/public/__tests__/table_vis_controller.js b/src/legacy/core_plugins/vis_type_table/public/__tests__/table_vis_controller.js index abebf8190dc9f..4153ce2da36a7 100644 --- a/src/legacy/core_plugins/vis_type_table/public/__tests__/table_vis_controller.js +++ b/src/legacy/core_plugins/vis_type_table/public/__tests__/table_vis_controller.js @@ -21,7 +21,7 @@ import $ from 'jquery'; import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import { legacyResponseHandlerProvider } from 'ui/vis/response_handlers/legacy'; -import { VisProvider } from 'ui/vis'; +import { Vis } from 'ui/vis'; import { VisFactoryProvider } from 'ui/vis/vis_factory'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; import { AppStateProvider } from 'ui/state_management/app_state'; @@ -36,7 +36,6 @@ describe('Table Vis - Controller', async function () { let Private; let $scope; let $el; - let Vis; let fixtures; let AppState; let tableAggResponse; @@ -63,7 +62,6 @@ describe('Table Vis - Controller', async function () { $compile = $injector.get('$compile'); fixtures = require('fixtures/fake_hierarchical_data'); AppState = Private(AppStateProvider); - Vis = Private(VisProvider); tableAggResponse = legacyResponseHandlerProvider().handler; }) ); diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js index d22ff92c4d3f6..13e8a4fd9535a 100644 --- a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js +++ b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js @@ -25,7 +25,7 @@ import fixtures from 'fixtures/fake_hierarchical_data'; import sinon from 'sinon'; import { legacyResponseHandlerProvider } from 'ui/vis/response_handlers/legacy'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { VisProvider } from 'ui/vis'; +import { Vis } from 'ui/vis'; import { tabifyAggResponse } from 'ui/agg_response/tabify'; import { round } from 'lodash'; @@ -36,7 +36,6 @@ import { setup as visualizationsSetup } from '../../../../visualizations/public/ describe('Table Vis - AggTable Directive', function () { let $rootScope; let $compile; - let Vis; let indexPattern; let settings; let tableAggResponse; @@ -113,7 +112,6 @@ describe('Table Vis - AggTable Directive', function () { ngMock.inject(function ($injector, Private, config) { tableAggResponse = legacyResponseHandlerProvider().handler; indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - Vis = Private(VisProvider); settings = config; $rootScope = $injector.get('$rootScope'); diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js index e8359e37d6186..f4e3a8e36605c 100644 --- a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js +++ b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js @@ -23,13 +23,12 @@ import expect from '@kbn/expect'; import fixtures from 'fixtures/fake_hierarchical_data'; import { legacyResponseHandlerProvider } from 'ui/vis/response_handlers/legacy'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { VisProvider } from 'ui/vis'; +import { Vis } from 'ui/vis'; import { tabifyAggResponse } from 'ui/agg_response/tabify'; describe('Table Vis - AggTableGroup Directive', function () { let $rootScope; let $compile; - let Vis; let indexPattern; let tableAggResponse; const tabifiedData = {}; @@ -69,7 +68,6 @@ describe('Table Vis - AggTableGroup Directive', function () { tableAggResponse = legacyResponseHandlerProvider().handler; indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - Vis = Private(VisProvider); $rootScope = $injector.get('$rootScope'); $compile = $injector.get('$compile'); diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud_visualization.js b/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud_visualization.js index 69d08d8cb3f74..0cb903faac47c 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud_visualization.js +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud_visualization.js @@ -20,7 +20,7 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import LogstashIndexPatternStubProvider from 'fixtures/stubbed_logstash_index_pattern'; -import * as visModule from 'ui/vis'; +import { Vis } from 'ui/vis'; import { ImageComparator } from 'test_utils/image_comparator'; import { TagCloudVisualization } from '../tag_cloud_visualization'; import basicdrawPng from './basicdraw.png'; @@ -33,7 +33,6 @@ const PIXEL_DIFF = 64; describe('TagCloudVisualizationTest', function () { let domNode; - let Vis; let indexPattern; let vis; let imageComparator; @@ -57,7 +56,6 @@ describe('TagCloudVisualizationTest', function () { beforeEach(ngMock.module('kibana')); beforeEach(ngMock.inject((Private) => { - Vis = Private(visModule.VisProvider); indexPattern = Private(LogstashIndexPatternStubProvider); })); 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 842d3aa6c4ad7..2b42c22ad7c43 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 @@ -44,8 +44,7 @@ const APP_NAME = 'VisEditor'; export class VisEditor extends Component { constructor(props) { super(props); - const { vis } = props; - this.appState = vis.API.getAppState(); + this.appState = props.appState; this.localStorage = new Storage(window.localStorage); this.state = { model: props.visParams, @@ -183,7 +182,6 @@ export class VisEditor extends Component { dirty={this.state.dirty} autoApply={this.state.autoApply} model={model} - appState={this.appState} savedObj={this.props.savedObj} timeRange={this.props.timeRange} uiState={this.uiState} @@ -239,4 +237,5 @@ VisEditor.propTypes = { isEditorMode: PropTypes.bool, savedObj: PropTypes.object, timeRange: PropTypes.object, + appState: PropTypes.object, }; 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 46725a2c5d01f..191f35d2e03ea 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 @@ -23,7 +23,7 @@ import ngMock from 'ng_mock'; import $ from 'jquery'; import { createVegaVisualization } from '../vega_visualization'; import LogstashIndexPatternStubProvider from 'fixtures/stubbed_logstash_index_pattern'; -import * as visModule from 'ui/vis'; +import { Vis } from 'ui/vis'; import { ImageComparator } from 'test_utils/image_comparator'; import vegaliteGraph from '!!raw-loader!./vegalite_graph.hjson'; @@ -50,7 +50,6 @@ const PIXEL_DIFF = 30; describe('VegaVisualizations', () => { let domNode; let VegaVisualization; - let Vis; let indexPattern; let vis; let imageComparator; @@ -73,7 +72,6 @@ describe('VegaVisualizations', () => { ); } - Vis = Private(visModule.VisProvider); VegaVisualization = createVegaVisualization(vegaVisualizationDependencies); indexPattern = Private(LogstashIndexPatternStubProvider); diff --git a/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts b/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts index b4c32f37eb90c..accc52c1e5a14 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts +++ b/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts @@ -18,11 +18,9 @@ */ import { timefilter } from 'ui/timefilter'; -import { TimeRange } from 'src/plugins/data/public'; -import { Query } from 'src/legacy/core_plugins/data/public'; -import { buildEsQuery, getEsQueryConfig } from '@kbn/es-query'; -import { esFilters } from '../../../../plugins/data/public'; +import { buildEsQuery, getEsQueryConfig } from '@kbn/es-query'; +import { esFilters, TimeRange, Query } from '../../../../plugins/data/public'; // @ts-ignore import { VegaParser } from './data_model/vega_parser'; diff --git a/src/legacy/core_plugins/visualizations/public/index.ts b/src/legacy/core_plugins/visualizations/public/index.ts index ad86c9ddb14c5..ca79f547890f9 100644 --- a/src/legacy/core_plugins/visualizations/public/index.ts +++ b/src/legacy/core_plugins/visualizations/public/index.ts @@ -39,7 +39,6 @@ export { DefaultEditorSize } from 'ui/vis/editor_size'; import * as types from 'ui/vis/vis'; export type Vis = types.Vis; export type VisParams = types.VisParams; -export type VisProvider = types.VisProvider; export type VisState = types.VisState; export { VisualizationController } from 'ui/vis/vis_types/vis_type'; export { Status } from 'ui/vis/update_status'; diff --git a/src/legacy/plugin_discovery/types.ts b/src/legacy/plugin_discovery/types.ts index c14daa37f5706..dfd36f2aa7b2b 100644 --- a/src/legacy/plugin_discovery/types.ts +++ b/src/legacy/plugin_discovery/types.ts @@ -62,7 +62,6 @@ export interface LegacyPluginOptions { }>; apps: any; hacks: string[]; - devTools: string[]; styleSheetPaths: string; injectDefaultVars: (server: Server) => Record; noParse: string[]; diff --git a/src/legacy/ui/public/agg_response/tabify/__tests__/_get_columns.js b/src/legacy/ui/public/agg_response/tabify/__tests__/_get_columns.js index 27b390ed7e471..39303e94adc6f 100644 --- a/src/legacy/ui/public/agg_response/tabify/__tests__/_get_columns.js +++ b/src/legacy/ui/public/agg_response/tabify/__tests__/_get_columns.js @@ -20,15 +20,13 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import { tabifyGetColumns } from '../_get_columns'; -import { VisProvider } from '../../../vis'; +import { Vis } from '../../../vis'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; describe('get columns', function () { - let Vis; let indexPattern; beforeEach(ngMock.module('kibana')); beforeEach(ngMock.inject(function (Private) { - Vis = Private(VisProvider); indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); })); diff --git a/src/legacy/ui/public/agg_response/tabify/__tests__/_integration.js b/src/legacy/ui/public/agg_response/tabify/__tests__/_integration.js index fd8df904b600f..dabd66a22fcd2 100644 --- a/src/legacy/ui/public/agg_response/tabify/__tests__/_integration.js +++ b/src/legacy/ui/public/agg_response/tabify/__tests__/_integration.js @@ -22,16 +22,14 @@ import fixtures from 'fixtures/fake_hierarchical_data'; import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import { tabifyAggResponse } from '../tabify'; -import { VisProvider } from '../../../vis'; +import { Vis } from '../../../vis'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; describe('tabifyAggResponse Integration', function () { - let Vis; let indexPattern; beforeEach(ngMock.module('kibana')); beforeEach(ngMock.inject(function (Private) { - Vis = Private(VisProvider); indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); })); diff --git a/src/legacy/ui/public/agg_response/tabify/__tests__/_response_writer.js b/src/legacy/ui/public/agg_response/tabify/__tests__/_response_writer.js index 09668c638d695..001132f97c95a 100644 --- a/src/legacy/ui/public/agg_response/tabify/__tests__/_response_writer.js +++ b/src/legacy/ui/public/agg_response/tabify/__tests__/_response_writer.js @@ -20,11 +20,10 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import { TabbedAggResponseWriter } from '../_response_writer'; -import { VisProvider } from '../../../vis'; +import { Vis } from '../../../vis'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; describe('TabbedAggResponseWriter class', function () { - let Vis; let Private; let indexPattern; @@ -32,7 +31,6 @@ describe('TabbedAggResponseWriter class', function () { beforeEach(ngMock.inject(function ($injector) { Private = $injector.get('Private'); - Vis = Private(VisProvider); indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); })); diff --git a/src/legacy/ui/public/agg_types/__tests__/buckets/_terms_other_bucket_helper.js b/src/legacy/ui/public/agg_types/__tests__/buckets/_terms_other_bucket_helper.js index dcadeacb3d1fe..759a6b80d8faf 100644 --- a/src/legacy/ui/public/agg_types/__tests__/buckets/_terms_other_bucket_helper.js +++ b/src/legacy/ui/public/agg_types/__tests__/buckets/_terms_other_bucket_helper.js @@ -20,7 +20,7 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import { buildOtherBucketAgg, mergeOtherBucketAggResponse, updateMissingBucket } from '../../buckets/_terms_other_bucket_helper'; -import { VisProvider } from '../../../vis'; +import { Vis } from '../../../vis'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; const visConfigSingleTerm = { @@ -158,7 +158,6 @@ describe('Terms Agg Other bucket helper', () => { function init(aggConfig) { ngMock.module('kibana'); ngMock.inject((Private) => { - const Vis = Private(VisProvider); const indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); vis = new Vis(indexPattern, aggConfig); diff --git a/src/legacy/ui/public/agg_types/agg_configs.ts b/src/legacy/ui/public/agg_types/agg_configs.ts index 675d37d05c33c..7c0245f30a1fd 100644 --- a/src/legacy/ui/public/agg_types/agg_configs.ts +++ b/src/legacy/ui/public/agg_types/agg_configs.ts @@ -253,13 +253,10 @@ export class AggConfigs { // collect all the aggregations const aggregations = this.aggs .filter(agg => agg.enabled && agg.type) - .reduce( - (requestValuesAggs, agg: AggConfig) => { - const aggs = agg.getRequestAggs(); - return aggs ? requestValuesAggs.concat(aggs) : requestValuesAggs; - }, - [] as AggConfig[] - ); + .reduce((requestValuesAggs, agg: AggConfig) => { + const aggs = agg.getRequestAggs(); + return aggs ? requestValuesAggs.concat(aggs) : requestValuesAggs; + }, [] as AggConfig[]); // move metrics to the end return _.sortBy(aggregations, (agg: AggConfig) => agg.type.type === AggGroupNames.Metrics ? 1 : 0 @@ -282,13 +279,10 @@ export class AggConfigs { * @return {array[AggConfig]} */ getResponseAggs(): AggConfig[] { - return this.getRequestAggs().reduce( - function(responseValuesAggs, agg: AggConfig) { - const aggs = agg.getResponseAggs(); - return aggs ? responseValuesAggs.concat(aggs) : responseValuesAggs; - }, - [] as AggConfig[] - ); + return this.getRequestAggs().reduce(function(responseValuesAggs, agg: AggConfig) { + const aggs = agg.getResponseAggs(); + return aggs ? responseValuesAggs.concat(aggs) : responseValuesAggs; + }, [] as AggConfig[]); } /** diff --git a/src/legacy/ui/public/agg_types/buckets/filters.ts b/src/legacy/ui/public/agg_types/buckets/filters.ts index 44a97abb7a1d7..a8d509d507c6b 100644 --- a/src/legacy/ui/public/agg_types/buckets/filters.ts +++ b/src/legacy/ui/public/agg_types/buckets/filters.ts @@ -27,10 +27,9 @@ import { buildEsQuery } from '@kbn/es-query'; import { FiltersParamEditor, FilterValue } from '../../vis/editors/default/controls/filters'; import { createFilterFilters } from './create_filter/filters'; import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type'; -import { setup as data } from '../../../../core_plugins/data/public/legacy'; import { Storage } from '../../../../../plugins/kibana_utils/public'; +import { getQueryLog } from '../../../../../plugins/data/public'; -const { getQueryLog } = data.query.helpers; const config = chrome.getUiSettingsClient(); const storage = new Storage(window.localStorage); diff --git a/src/legacy/ui/public/agg_types/buckets/geo_hash.test.ts b/src/legacy/ui/public/agg_types/buckets/geo_hash.test.ts index 5c599f16e09c2..effa49f0ade6b 100644 --- a/src/legacy/ui/public/agg_types/buckets/geo_hash.test.ts +++ b/src/legacy/ui/public/agg_types/buckets/geo_hash.test.ts @@ -156,8 +156,9 @@ describe('Geohash Agg', () => { describe('aggregation options', () => { it('should only create geohash_grid and geo_centroid aggregations when isFilteredByCollar is false', () => { const aggConfigs = getAggConfigs({ isFilteredByCollar: false }); - const requestAggs = geoHashBucketAgg.getRequestAggs(aggConfigs - .aggs[0] as IBucketGeoHashGridAggConfig) as IBucketGeoHashGridAggConfig[]; + const requestAggs = geoHashBucketAgg.getRequestAggs( + aggConfigs.aggs[0] as IBucketGeoHashGridAggConfig + ) as IBucketGeoHashGridAggConfig[]; expect(requestAggs.length).toEqual(2); expect(requestAggs[0].type.name).toEqual('geohash_grid'); @@ -166,8 +167,9 @@ describe('Geohash Agg', () => { it('should only create filter and geohash_grid aggregations when useGeocentroid is false', () => { const aggConfigs = getAggConfigs({ useGeocentroid: false }); - const requestAggs = geoHashBucketAgg.getRequestAggs(aggConfigs - .aggs[0] as IBucketGeoHashGridAggConfig) as IBucketGeoHashGridAggConfig[]; + const requestAggs = geoHashBucketAgg.getRequestAggs( + aggConfigs.aggs[0] as IBucketGeoHashGridAggConfig + ) as IBucketGeoHashGridAggConfig[]; expect(requestAggs.length).toEqual(2); expect(requestAggs[0].type.name).toEqual('filter'); @@ -179,36 +181,43 @@ describe('Geohash Agg', () => { let originalRequestAggs: IBucketGeoHashGridAggConfig[]; beforeEach(() => { - originalRequestAggs = geoHashBucketAgg.getRequestAggs(getAggConfigs() - .aggs[0] as IBucketGeoHashGridAggConfig) as IBucketGeoHashGridAggConfig[]; + originalRequestAggs = geoHashBucketAgg.getRequestAggs( + getAggConfigs().aggs[0] as IBucketGeoHashGridAggConfig + ) as IBucketGeoHashGridAggConfig[]; }); it('should change geo_bounding_box filter aggregation and vis session state when map movement is outside map collar', () => { - const [, geoBoxingBox] = geoHashBucketAgg.getRequestAggs(getAggConfigs({ - mapBounds: { - top_left: { lat: 10.0, lon: -10.0 }, - bottom_right: { lat: 9.0, lon: -9.0 }, - }, - }).aggs[0] as IBucketGeoHashGridAggConfig) as IBucketGeoHashGridAggConfig[]; + const [, geoBoxingBox] = geoHashBucketAgg.getRequestAggs( + getAggConfigs({ + mapBounds: { + top_left: { lat: 10.0, lon: -10.0 }, + bottom_right: { lat: 9.0, lon: -9.0 }, + }, + }).aggs[0] as IBucketGeoHashGridAggConfig + ) as IBucketGeoHashGridAggConfig[]; expect(originalRequestAggs[1].params).not.toEqual(geoBoxingBox.params); }); it('should not change geo_bounding_box filter aggregation and vis session state when map movement is within map collar', () => { - const [, geoBoxingBox] = geoHashBucketAgg.getRequestAggs(getAggConfigs({ - mapBounds: { - top_left: { lat: 1, lon: -1 }, - bottom_right: { lat: -1, lon: 1 }, - }, - }).aggs[0] as IBucketGeoHashGridAggConfig) as IBucketGeoHashGridAggConfig[]; + const [, geoBoxingBox] = geoHashBucketAgg.getRequestAggs( + getAggConfigs({ + mapBounds: { + top_left: { lat: 1, lon: -1 }, + bottom_right: { lat: -1, lon: 1 }, + }, + }).aggs[0] as IBucketGeoHashGridAggConfig + ) as IBucketGeoHashGridAggConfig[]; expect(originalRequestAggs[1].params).toEqual(geoBoxingBox.params); }); it('should change geo_bounding_box filter aggregation and vis session state when map zoom level changes', () => { - const [, geoBoxingBox] = geoHashBucketAgg.getRequestAggs(getAggConfigs({ - mapZoom: -1, - }).aggs[0] as IBucketGeoHashGridAggConfig) as IBucketGeoHashGridAggConfig[]; + const [, geoBoxingBox] = geoHashBucketAgg.getRequestAggs( + getAggConfigs({ + mapZoom: -1, + }).aggs[0] as IBucketGeoHashGridAggConfig + ) as IBucketGeoHashGridAggConfig[]; expect(originalRequestAggs[1].lastMapCollar).not.toEqual(geoBoxingBox.lastMapCollar); }); diff --git a/src/legacy/ui/public/agg_types/buckets/range.test.ts b/src/legacy/ui/public/agg_types/buckets/range.test.ts index f7cae60cce773..1b423e64c48ae 100644 --- a/src/legacy/ui/public/agg_types/buckets/range.test.ts +++ b/src/legacy/ui/public/agg_types/buckets/range.test.ts @@ -72,7 +72,10 @@ describe('Range Agg', () => { schema: 'segment', params: { field: 'bytes', - ranges: [{ from: 0, to: 1000 }, { from: 1000, to: 2000 }], + ranges: [ + { from: 0, to: 1000 }, + { from: 1000, to: 2000 }, + ], }, }, ], diff --git a/src/legacy/ui/public/agg_types/buckets/range.ts b/src/legacy/ui/public/agg_types/buckets/range.ts index 348fccdab3fe3..230675ab487ad 100644 --- a/src/legacy/ui/public/agg_types/buckets/range.ts +++ b/src/legacy/ui/public/agg_types/buckets/range.ts @@ -98,7 +98,10 @@ export const rangeBucketAgg = new BucketAggType({ }, { name: 'ranges', - default: [{ from: 0, to: 1000 }, { from: 1000, to: 2000 }], + default: [ + { from: 0, to: 1000 }, + { from: 1000, to: 2000 }, + ], editorComponent: RangesEditor, write(aggConfig: IBucketAggConfig, output: Record) { output.params.ranges = aggConfig.params.ranges; diff --git a/src/legacy/ui/public/agg_types/filter/prop_filter.ts b/src/legacy/ui/public/agg_types/filter/prop_filter.ts index 45f350ea6adc8..e6b5f3831e65d 100644 --- a/src/legacy/ui/public/agg_types/filter/prop_filter.ts +++ b/src/legacy/ui/public/agg_types/filter/prop_filter.ts @@ -59,24 +59,21 @@ function propFilter

(prop: P) { return list; } - const options = filters.reduce( - (acc, filter) => { - let type = 'include'; - let value = filter; + const options = filters.reduce((acc, filter) => { + let type = 'include'; + let value = filter; - if (filter.charAt(0) === '!') { - type = 'exclude'; - value = filter.substr(1); - } + if (filter.charAt(0) === '!') { + type = 'exclude'; + value = filter.substr(1); + } - if (!acc[type]) { - acc[type] = []; - } - acc[type].push(value); - return acc; - }, - {} as { [type: string]: string[] } - ); + if (!acc[type]) { + acc[type] = []; + } + acc[type].push(value); + return acc; + }, {} as { [type: string]: string[] }); return list.filter(item => { const value = item[prop]; diff --git a/src/legacy/ui/public/agg_types/metrics/metric_agg_type.ts b/src/legacy/ui/public/agg_types/metrics/metric_agg_type.ts index 66bc205cead13..c1f5528825bcc 100644 --- a/src/legacy/ui/public/agg_types/metrics/metric_agg_type.ts +++ b/src/legacy/ui/public/agg_types/metrics/metric_agg_type.ts @@ -58,8 +58,9 @@ export class MetricAggType< config.getValue || ((agg, bucket) => { // Metric types where an empty set equals `zero` - const isSettableToZero = [METRIC_TYPES.CARDINALITY, METRIC_TYPES.SUM].includes(agg.type - .name as METRIC_TYPES); + const isSettableToZero = [METRIC_TYPES.CARDINALITY, METRIC_TYPES.SUM].includes( + agg.type.name as METRIC_TYPES + ); // Return proper values when no buckets are present // `Count` handles empty sets properly diff --git a/src/legacy/ui/public/agg_types/metrics/percentile_ranks.test.ts b/src/legacy/ui/public/agg_types/metrics/percentile_ranks.test.ts index f3882ca57161f..7461b5cf07ee7 100644 --- a/src/legacy/ui/public/agg_types/metrics/percentile_ranks.test.ts +++ b/src/legacy/ui/public/agg_types/metrics/percentile_ranks.test.ts @@ -63,8 +63,9 @@ describe('AggTypesMetricsPercentileRanksProvider class', function() { }); it('uses the custom label if it is set', function() { - const responseAggs: any = percentileRanksMetricAgg.getResponseAggs(aggConfigs - .aggs[0] as IPercentileRanksAggConfig); + const responseAggs: any = percentileRanksMetricAgg.getResponseAggs( + aggConfigs.aggs[0] as IPercentileRanksAggConfig + ); const percentileRankLabelFor5kBytes = responseAggs[0].makeLabel(); const percentileRankLabelFor10kBytes = responseAggs[1].makeLabel(); diff --git a/src/legacy/ui/public/agg_types/metrics/percentiles.test.ts b/src/legacy/ui/public/agg_types/metrics/percentiles.test.ts index 1503f43b22dc3..c9f4bcc3862a0 100644 --- a/src/legacy/ui/public/agg_types/metrics/percentiles.test.ts +++ b/src/legacy/ui/public/agg_types/metrics/percentiles.test.ts @@ -63,8 +63,9 @@ describe('AggTypesMetricsPercentilesProvider class', () => { }); it('uses the custom label if it is set', () => { - const responseAggs: any = percentilesMetricAgg.getResponseAggs(aggConfigs - .aggs[0] as IPercentileAggConfig); + const responseAggs: any = percentilesMetricAgg.getResponseAggs( + aggConfigs.aggs[0] as IPercentileAggConfig + ); const ninetyFifthPercentileLabel = responseAggs[0].makeLabel(); diff --git a/src/legacy/ui/public/agg_types/metrics/std_deviation.test.ts b/src/legacy/ui/public/agg_types/metrics/std_deviation.test.ts index ae09b5cd78977..3125026a52185 100644 --- a/src/legacy/ui/public/agg_types/metrics/std_deviation.test.ts +++ b/src/legacy/ui/public/agg_types/metrics/std_deviation.test.ts @@ -58,8 +58,9 @@ describe('AggTypeMetricStandardDeviationProvider class', () => { it('uses the custom label if it is set', () => { const aggConfigs = getAggConfigs('custom label'); - const responseAggs: any = stdDeviationMetricAgg.getResponseAggs(aggConfigs - .aggs[0] as IStdDevAggConfig); + const responseAggs: any = stdDeviationMetricAgg.getResponseAggs( + aggConfigs.aggs[0] as IStdDevAggConfig + ); const lowerStdDevLabel = responseAggs[0].makeLabel(); const upperStdDevLabel = responseAggs[1].makeLabel(); @@ -71,8 +72,9 @@ describe('AggTypeMetricStandardDeviationProvider class', () => { it('uses the default labels if custom label is not set', () => { const aggConfigs = getAggConfigs(); - const responseAggs: any = stdDeviationMetricAgg.getResponseAggs(aggConfigs - .aggs[0] as IStdDevAggConfig); + const responseAggs: any = stdDeviationMetricAgg.getResponseAggs( + aggConfigs.aggs[0] as IStdDevAggConfig + ); const lowerStdDevLabel = responseAggs[0].makeLabel(); const upperStdDevLabel = responseAggs[1].makeLabel(); diff --git a/src/legacy/ui/public/filter_manager/__tests__/filter_generator.js b/src/legacy/ui/public/filter_manager/__tests__/filter_generator.js deleted file mode 100644 index 5b6455bf20847..0000000000000 --- a/src/legacy/ui/public/filter_manager/__tests__/filter_generator.js +++ /dev/null @@ -1,166 +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 _ from 'lodash'; -import sinon from 'sinon'; -import MockState from 'fixtures/mock_state'; -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import { getFilterGenerator } from '..'; -import { FilterBarQueryFilterProvider } from '../../filter_manager/query_filter'; -import { uniqFilters, esFilters } from '../../../../../plugins/data/public'; - -let queryFilter; -let filterGen; -let appState; - -function checkAddFilters(length, comps, idx) { - idx = idx || 0; - const filters = queryFilter.addFilters.getCall(idx).args[0]; - - expect(filters.length).to.be(length); - if (!Array.isArray(comps)) return; - comps.forEach(function (comp, i) { - expect(filters[i]).to.eql(comp); - }); -} - -describe('Filter Manager', function () { - beforeEach(ngMock.module( - 'kibana', - 'kibana/global_state', - function ($provide) { - $provide.service('indexPatterns', require('fixtures/mock_index_patterns')); - - appState = new MockState({ filters: [] }); - $provide.service('getAppState', function () { - return function () { return appState; }; - }); - } - )); - - beforeEach(ngMock.inject(function (_$rootScope_, Private) { - - // mock required queryFilter methods, used in the manager - queryFilter = Private(FilterBarQueryFilterProvider); - filterGen = getFilterGenerator(queryFilter); - sinon.stub(queryFilter, 'getAppFilters').callsFake(() => appState.filters); - sinon.stub(queryFilter, 'addFilters').callsFake((filters) => { - if (!Array.isArray(filters)) filters = [filters]; - appState.filters = uniqFilters(appState.filters.concat(filters)); - }); - })); - - it('should have an `add` function', function () { - expect(filterGen.add).to.be.a(Function); - }); - - it('should add a filter', function () { - filterGen.add('myField', 1, '+', 'myIndex'); - expect(queryFilter.addFilters.callCount).to.be(1); - checkAddFilters(1, [{ - meta: { index: 'myIndex', negate: false }, - query: { match_phrase: { myField: 1 } } - }]); - }); - - it('should add multiple filters if passed an array of values', function () { - filterGen.add('myField', [1, 2, 3], '+', 'myIndex'); - expect(queryFilter.addFilters.callCount).to.be(1); - checkAddFilters(3, [{ - meta: { index: 'myIndex', negate: false }, - query: { match_phrase: { myField: 1 } } - }, { - meta: { index: 'myIndex', negate: false }, - query: { match_phrase: { myField: 2 } } - }, { - meta: { index: 'myIndex', negate: false }, - query: { match_phrase: { myField: 3 } } - }]); - }); - - it('should add an exists filter if _exists_ is used as the field', function () { - filterGen.add('_exists_', 'myField', '+', 'myIndex'); - checkAddFilters(1, [{ - meta: { index: 'myIndex', negate: false }, - exists: { field: 'myField' } - }]); - }); - - it('should negate existing filter instead of added a conflicting filter', function () { - filterGen.add('myField', 1, '+', 'myIndex'); - checkAddFilters(1, [{ - meta: { index: 'myIndex', negate: false }, - query: { match_phrase: { myField: 1 } } - }], 0); - expect(appState.filters).to.have.length(1); - - // NOTE: negating exists filters also forces disabled to false - filterGen.add('myField', 1, '-', 'myIndex'); - checkAddFilters(1, [{ - meta: { index: 'myIndex', negate: true, disabled: false }, - query: { match_phrase: { myField: 1 } } - }], 1); - expect(appState.filters).to.have.length(1); - - filterGen.add('_exists_', 'myField', '+', 'myIndex'); - checkAddFilters(1, [{ - meta: { index: 'myIndex', negate: false }, - exists: { field: 'myField' } - }], 2); - expect(appState.filters).to.have.length(2); - - filterGen.add('_exists_', 'myField', '-', 'myIndex'); - checkAddFilters(1, [{ - meta: { index: 'myIndex', negate: true, disabled: false }, - exists: { field: 'myField' } - }], 3); - expect(appState.filters).to.have.length(2); - - const scriptedField = { name: 'scriptedField', scripted: true, script: 1, lang: 'painless' }; - filterGen.add(scriptedField, 1, '+', 'myIndex'); - checkAddFilters(1, [{ - meta: { index: 'myIndex', negate: false, field: 'scriptedField' }, - script: esFilters.getPhraseScript(scriptedField, 1) - }], 4); - expect(appState.filters).to.have.length(3); - - filterGen.add(scriptedField, 1, '-', 'myIndex'); - checkAddFilters(1, [{ - meta: { index: 'myIndex', negate: true, disabled: false, field: 'scriptedField' }, - script: esFilters.getPhraseScript(scriptedField, 1) - }], 5); - expect(appState.filters).to.have.length(3); - }); - - it('should enable matching filters being changed', function () { - _.each([true, false], function (negate) { - appState.filters = [{ - query: { match_phrase: { myField: 1 } }, - meta: { disabled: true, negate: negate } - }]; - expect(appState.filters.length).to.be(1); - expect(appState.filters[0].meta.disabled).to.be(true); - - filterGen.add('myField', 1, '+', 'myIndex'); - expect(appState.filters.length).to.be(1); - expect(appState.filters[0].meta.disabled).to.be(false); - }); - }); -}); diff --git a/src/legacy/ui/public/filter_manager/filter_generator.js b/src/legacy/ui/public/filter_manager/filter_generator.js deleted file mode 100644 index e11e0ff6653a7..0000000000000 --- a/src/legacy/ui/public/filter_manager/filter_generator.js +++ /dev/null @@ -1,98 +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 _ from 'lodash'; -import { esFilters } from '../../../../plugins/data/public'; - -// Adds a filter to a passed state -export function getFilterGenerator(queryFilter) { - const filterGen = {}; - - filterGen.generate = (field, values, operation, index) => { - values = Array.isArray(values) ? values : [values]; - const fieldName = _.isObject(field) ? field.name : field; - const filters = _.flatten([queryFilter.getAppFilters()]); - const newFilters = []; - - const negate = (operation === '-'); - - // TODO: On array fields, negating does not negate the combination, rather all terms - _.each(values, function (value) { - let filter; - const existing = _.find(filters, function (filter) { - if (!filter) return; - - if (fieldName === '_exists_' && filter.exists) { - return filter.exists.field === value; - } - - if (esFilters.isPhraseFilter(filter)) { - return esFilters.getPhraseFilterField(filter) === fieldName && esFilters.getPhraseFilterValue(filter) === value; - } - - if (filter.script) { - return filter.meta.field === fieldName && filter.script.script.params.value === value; - } - }); - - if (existing) { - existing.meta.disabled = false; - if (existing.meta.negate !== negate) { - existing.meta.negate = !existing.meta.negate; - } - newFilters.push(existing); - return; - } - - switch (fieldName) { - case '_exists_': - filter = { - meta: { negate, index }, - exists: { - field: value - } - }; - break; - default: - if (field.scripted) { - filter = { - meta: { negate, index, field: fieldName }, - script: esFilters.getPhraseScript(field, value) - }; - } else { - filter = { meta: { negate, index }, query: { match_phrase: {} } }; - filter.query.match_phrase[fieldName] = value; - } - - break; - } - - newFilters.push(filter); - }); - - return newFilters; - }; - - filterGen.add = function (field, values, operation, index) { - const newFilters = this.generate(field, values, operation, index); - return queryFilter.addFilters(newFilters); - }; - - return filterGen; -} diff --git a/src/legacy/ui/public/filter_manager/index.js b/src/legacy/ui/public/filter_manager/index.js index 6adc4e0965ccd..ce99d4cac3017 100644 --- a/src/legacy/ui/public/filter_manager/index.js +++ b/src/legacy/ui/public/filter_manager/index.js @@ -17,4 +17,3 @@ * under the License. */ -export { getFilterGenerator } from './filter_generator'; diff --git a/src/legacy/ui/public/legacy_compat/angular_config.tsx b/src/legacy/ui/public/legacy_compat/angular_config.tsx index 58b8422cb2f8a..788718e848430 100644 --- a/src/legacy/ui/public/legacy_compat/angular_config.tsx +++ b/src/legacy/ui/public/legacy_compat/angular_config.tsx @@ -40,6 +40,7 @@ import { fatalError } from 'ui/notify'; import { capabilities } from 'ui/capabilities'; // @ts-ignore import { modifyUrl } from 'ui/url'; +import { toMountPoint } from '../../../../plugins/kibana_react/public'; // @ts-ignore import { UrlOverflowService } from '../error_url_overflow'; import { npStart } from '../new_platform'; @@ -329,7 +330,7 @@ const $setupUrlOverflowHandling = (newPlatform: CoreStart) => ( title: i18n.translate('common.ui.chrome.bigUrlWarningNotificationTitle', { defaultMessage: 'The URL is big and Kibana might stop working', }), - text: ( + text: toMountPoint( {}, + }, inspector: { registerView: () => undefined, __LEGACY: { @@ -97,6 +100,9 @@ export const npStart = { registerRenderer: sinon.fake(), registerType: sinon.fake(), }, + devTools: { + getSortedDevTools: () => [], + }, data: { autocomplete: { getProvider: sinon.fake(), diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts index 0c7b28e7da3df..9ee5d8580a90b 100644 --- a/src/legacy/ui/public/new_platform/new_platform.ts +++ b/src/legacy/ui/public/new_platform/new_platform.ts @@ -28,6 +28,7 @@ import { Start as InspectorStart, } from '../../../../plugins/inspector/public'; import { EuiUtilsStart } from '../../../../plugins/eui_utils/public'; +import { DevToolsSetup, DevToolsStart } from '../../../../plugins/dev_tools/public'; import { FeatureCatalogueSetup, FeatureCatalogueStart, @@ -40,6 +41,7 @@ export interface PluginsSetup { feature_catalogue: FeatureCatalogueSetup; inspector: InspectorSetup; uiActions: IUiActionsSetup; + devTools: DevToolsSetup; } export interface PluginsStart { @@ -50,6 +52,7 @@ export interface PluginsStart { feature_catalogue: FeatureCatalogueStart; inspector: InspectorStart; uiActions: IUiActionsStart; + devTools: DevToolsStart; } export const npSetup = { diff --git a/src/legacy/ui/public/utils/migrate_legacy_query.ts b/src/legacy/ui/public/utils/migrate_legacy_query.ts index 06e819d6a64a2..8d9b50d5a66b2 100644 --- a/src/legacy/ui/public/utils/migrate_legacy_query.ts +++ b/src/legacy/ui/public/utils/migrate_legacy_query.ts @@ -18,7 +18,7 @@ */ import { has } from 'lodash'; -import { Query } from 'plugins/data'; +import { Query } from 'src/plugins/data/public'; /** * Creates a standardized query object from old queries that were either strings or pure ES query DSL diff --git a/src/legacy/ui/public/vis/__tests__/_agg_config.js b/src/legacy/ui/public/vis/__tests__/_agg_config.js index 46d7ed4601f29..2e2e0c31bdb8a 100644 --- a/src/legacy/ui/public/vis/__tests__/_agg_config.js +++ b/src/legacy/ui/public/vis/__tests__/_agg_config.js @@ -20,7 +20,7 @@ import sinon from 'sinon'; import expect from '@kbn/expect'; import ngMock from 'ng_mock'; -import { VisProvider } from '..'; +import { Vis } from '..'; import { AggType } from '../../agg_types/agg_type'; import { AggConfig } from '../../agg_types/agg_config'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; @@ -28,12 +28,10 @@ import { fieldFormats } from '../../registry/field_formats'; describe('AggConfig', function () { - let Vis; let indexPattern; beforeEach(ngMock.module('kibana')); beforeEach(ngMock.inject(function (Private) { - Vis = Private(VisProvider); indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); })); diff --git a/src/legacy/ui/public/vis/__tests__/_agg_configs.js b/src/legacy/ui/public/vis/__tests__/_agg_configs.js index 96e4aa943ed1d..3240bd56f1502 100644 --- a/src/legacy/ui/public/vis/__tests__/_agg_configs.js +++ b/src/legacy/ui/public/vis/__tests__/_agg_configs.js @@ -22,7 +22,7 @@ import sinon from 'sinon'; import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import { AggConfig } from '../../agg_types/agg_config'; -import { VisProvider } from '..'; +import { Vis } from '..'; import { AggConfigs } from '../../agg_types/agg_configs'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; import { Schemas } from '../editors/default/schemas'; @@ -30,13 +30,11 @@ import { AggGroupNames } from '../editors/default/agg_groups'; describe('AggConfigs', function () { - let Vis; let indexPattern; beforeEach(ngMock.module('kibana')); beforeEach(ngMock.inject(function (Private) { // load main deps - Vis = Private(VisProvider); indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); })); diff --git a/src/legacy/ui/public/vis/__tests__/_vis.js b/src/legacy/ui/public/vis/__tests__/_vis.js index 5a2f93ab35b50..1d5e2de6dafe3 100644 --- a/src/legacy/ui/public/vis/__tests__/_vis.js +++ b/src/legacy/ui/public/vis/__tests__/_vis.js @@ -20,13 +20,12 @@ import _ from 'lodash'; import ngMock from 'ng_mock'; import expect from '@kbn/expect'; -import { VisProvider } from '..'; +import { Vis } from '..'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; import { start as visualizations } from '../../../../core_plugins/visualizations/public/np_ready/public/legacy'; describe('Vis Class', function () { let indexPattern; - let Vis; let visTypes; let vis; @@ -43,7 +42,6 @@ describe('Vis Class', function () { beforeEach(ngMock.module('kibana')); beforeEach(ngMock.inject(function (Private) { - Vis = Private(VisProvider); indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); visTypes = visualizations.types; })); diff --git a/src/legacy/ui/public/vis/editors/default/components/agg.test.tsx b/src/legacy/ui/public/vis/editors/default/components/agg.test.tsx index 7806b1c0f78fb..661ece5944fa3 100644 --- a/src/legacy/ui/public/vis/editors/default/components/agg.test.tsx +++ b/src/legacy/ui/public/vis/editors/default/components/agg.test.tsx @@ -248,10 +248,9 @@ describe('DefaultEditorAgg component', () => { expect(compHistogram.find(DefaultEditorAggParams).props()).toHaveProperty('disabledParams', [ 'min_doc_count', ]); - expect(compDateHistogram.find(DefaultEditorAggParams).props()).toHaveProperty( - 'disabledParams', - ['min_doc_count'] - ); + expect( + compDateHistogram.find(DefaultEditorAggParams).props() + ).toHaveProperty('disabledParams', ['min_doc_count']); }); it('should set error when agg is not histogram or date_histogram', () => { diff --git a/src/legacy/ui/public/vis/editors/default/components/agg_group_state.tsx b/src/legacy/ui/public/vis/editors/default/components/agg_group_state.tsx index 8e8926f027cad..980889743c20d 100644 --- a/src/legacy/ui/public/vis/editors/default/components/agg_group_state.tsx +++ b/src/legacy/ui/public/vis/editors/default/components/agg_group_state.tsx @@ -53,13 +53,10 @@ function aggGroupReducer(state: AggsState, action: AggsAction): AggsState { } function initAggsState(group: AggConfig[]): AggsState { - return group.reduce( - (state, agg) => { - state[agg.id] = { touched: false, valid: true }; - return state; - }, - {} as AggsState - ); + return group.reduce((state, agg) => { + state[agg.id] = { touched: false, valid: true }; + return state; + }, {} as AggsState); } export { aggGroupReducer, initAggsState }; diff --git a/src/legacy/ui/public/vis/editors/default/components/agg_params_helper.test.ts b/src/legacy/ui/public/vis/editors/default/components/agg_params_helper.test.ts index 5fb241714b2e8..eb6bef4887642 100644 --- a/src/legacy/ui/public/vis/editors/default/components/agg_params_helper.test.ts +++ b/src/legacy/ui/public/vis/editors/default/components/agg_params_helper.test.ts @@ -121,7 +121,10 @@ describe('DefaultEditorAggParams helpers', () => { }, schema: {}, getIndexPattern: jest.fn(() => ({ - fields: [{ name: '@timestamp', type: 'date' }, { name: 'geo_desc', type: 'string' }], + fields: [ + { name: '@timestamp', type: 'date' }, + { name: 'geo_desc', type: 'string' }, + ], })), params: { orderBy: 'orderBy', diff --git a/src/legacy/ui/public/vis/editors/default/controls/auto_precision.tsx b/src/legacy/ui/public/vis/editors/default/controls/auto_precision.tsx index 3b6aebe8c2b0c..53f74465e90a5 100644 --- a/src/legacy/ui/public/vis/editors/default/controls/auto_precision.tsx +++ b/src/legacy/ui/public/vis/editors/default/controls/auto_precision.tsx @@ -23,7 +23,7 @@ import { EuiSwitch, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { AggParamEditorProps } from '..'; -function AutoPrecisionParamEditor({ value, setValue }: AggParamEditorProps) { +function AutoPrecisionParamEditor({ value = false, setValue }: AggParamEditorProps) { const label = i18n.translate('common.ui.aggTypes.changePrecisionLabel', { defaultMessage: 'Change precision on map zoom', }); diff --git a/src/legacy/ui/public/vis/editors/default/controls/components/number_list/utils.test.ts b/src/legacy/ui/public/vis/editors/default/controls/components/number_list/utils.test.ts index 3928c0a62eeaa..c6772cc108762 100644 --- a/src/legacy/ui/public/vis/editors/default/controls/components/number_list/utils.test.ts +++ b/src/legacy/ui/public/vis/editors/default/controls/components/number_list/utils.test.ts @@ -35,7 +35,10 @@ describe('NumberList utils', () => { let range: Range; beforeEach(() => { - modelList = [{ value: 1, id: '1', isInvalid: false }, { value: 2, id: '2', isInvalid: false }]; + modelList = [ + { value: 1, id: '1', isInvalid: false }, + { value: 2, id: '2', isInvalid: false }, + ]; range = { min: 1, max: 10, diff --git a/src/legacy/ui/public/vis/editors/default/controls/filter.tsx b/src/legacy/ui/public/vis/editors/default/controls/filter.tsx index 4ebe7b0d835d7..e847a95ead478 100644 --- a/src/legacy/ui/public/vis/editors/default/controls/filter.tsx +++ b/src/legacy/ui/public/vis/editors/default/controls/filter.tsx @@ -20,7 +20,8 @@ import React, { useState } from 'react'; import { EuiForm, EuiButtonIcon, EuiFieldText, EuiFormRow, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { Query, QueryBarInput } from 'plugins/data'; +import { QueryBarInput } from 'plugins/data'; +import { Query } from 'src/plugins/data/public'; import { AggConfig } from '../../..'; import { npStart } from '../../../../new_platform'; import { Storage } from '../../../../../../../plugins/kibana_utils/public'; diff --git a/src/legacy/ui/public/vis/editors/default/controls/filters.tsx b/src/legacy/ui/public/vis/editors/default/controls/filters.tsx index fe72e8dddd68d..aa654d26a23fd 100644 --- a/src/legacy/ui/public/vis/editors/default/controls/filters.tsx +++ b/src/legacy/ui/public/vis/editors/default/controls/filters.tsx @@ -21,7 +21,7 @@ import React, { useState, useEffect } from 'react'; import { omit, isEqual } from 'lodash'; import { htmlIdGenerator, EuiButton, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { Query } from 'plugins/data'; +import { Query } from 'src/plugins/data/public'; import chrome from '../../../../chrome'; import { FilterRow } from './filter'; import { AggParamEditorProps } from '..'; diff --git a/src/legacy/ui/public/vis/editors/default/controls/switch.tsx b/src/legacy/ui/public/vis/editors/default/controls/switch.tsx index a5fc9682bd954..de675386d9100 100644 --- a/src/legacy/ui/public/vis/editors/default/controls/switch.tsx +++ b/src/legacy/ui/public/vis/editors/default/controls/switch.tsx @@ -30,7 +30,7 @@ interface SwitchParamEditorProps extends AggParamEditorProps { } function SwitchParamEditor({ - value, + value = false, setValue, dataTestSubj, displayToolTip, diff --git a/src/legacy/ui/public/vis/editors/default/controls/use_geocentroid.tsx b/src/legacy/ui/public/vis/editors/default/controls/use_geocentroid.tsx index 6da32690912e7..932a4d19b495c 100644 --- a/src/legacy/ui/public/vis/editors/default/controls/use_geocentroid.tsx +++ b/src/legacy/ui/public/vis/editors/default/controls/use_geocentroid.tsx @@ -23,7 +23,7 @@ import { EuiSwitch, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { AggParamEditorProps } from '..'; -function UseGeocentroidParamEditor({ value, setValue }: AggParamEditorProps) { +function UseGeocentroidParamEditor({ value = false, setValue }: AggParamEditorProps) { const label = i18n.translate('common.ui.aggTypes.placeMarkersOffGridLabel', { defaultMessage: 'Place markers off grid (use geocentroid)', }); diff --git a/src/legacy/ui/public/vis/editors/default/utils.tsx b/src/legacy/ui/public/vis/editors/default/utils.tsx index efc424488ec41..4f82298aaca41 100644 --- a/src/legacy/ui/public/vis/editors/default/utils.tsx +++ b/src/legacy/ui/public/vis/editors/default/utils.tsx @@ -44,24 +44,21 @@ function groupAndSortBy< TGroupBy extends string = 'type', TLabelName extends string = 'title' >(objects: T[], groupBy: TGroupBy, labelName: TLabelName): ComboBoxGroupedOptions { - const groupedOptions = objects.reduce( - (array, obj) => { - const group = array.find(element => element.label === obj[groupBy]); - const option = { - label: obj[labelName], - target: obj, - }; + const groupedOptions = objects.reduce((array, obj) => { + const group = array.find(element => element.label === obj[groupBy]); + const option = { + label: obj[labelName], + target: obj, + }; - if (group && group.options) { - group.options.push(option); - } else { - array.push({ label: obj[groupBy], options: [option] }); - } + if (group && group.options) { + group.options.push(option); + } else { + array.push({ label: obj[groupBy], options: [option] }); + } - return array; - }, - [] as Array> - ); + return array; + }, [] as Array>); groupedOptions.sort(sortByLabel); diff --git a/src/legacy/ui/public/vis/index.d.ts b/src/legacy/ui/public/vis/index.d.ts index 5a2a7edb6329a..791ce2563e0f1 100644 --- a/src/legacy/ui/public/vis/index.d.ts +++ b/src/legacy/ui/public/vis/index.d.ts @@ -18,5 +18,5 @@ */ export { AggConfig } from '../agg_types/agg_config'; -export { Vis, VisProvider, VisParams, VisState } from './vis'; +export { Vis, VisParams, VisState } from './vis'; export { VisualizationController, VisType } from './vis_types/vis_type'; diff --git a/src/legacy/ui/public/vis/index.js b/src/legacy/ui/public/vis/index.js index 711b465a1e8a5..05cd030f7d100 100644 --- a/src/legacy/ui/public/vis/index.js +++ b/src/legacy/ui/public/vis/index.js @@ -17,4 +17,4 @@ * under the License. */ -export { VisProvider } from './vis'; +export { Vis } from './vis'; diff --git a/src/legacy/ui/public/vis/map/map_messages.js b/src/legacy/ui/public/vis/map/map_messages.js index 7571547ecf0d4..a9a636ab9e4cf 100644 --- a/src/legacy/ui/public/vis/map/map_messages.js +++ b/src/legacy/ui/public/vis/map/map_messages.js @@ -24,6 +24,7 @@ import { EuiSpacer, EuiButtonEmpty } from '@elastic/eui'; +import { toMountPoint } from '../../../../../plugins/kibana_react/public'; export const createZoomWarningMsg = (function () { let disableZoomMsg = false; @@ -107,7 +108,7 @@ export const createZoomWarningMsg = (function () { const zoomToast = ({ title: 'No additional zoom levels', - text: , + text: toMountPoint(), 'data-test-subj': 'maxZoomWarning', }); diff --git a/src/legacy/ui/public/vis/vis.d.ts b/src/legacy/ui/public/vis/vis.d.ts index be73cccf06a59..e16562641801e 100644 --- a/src/legacy/ui/public/vis/vis.d.ts +++ b/src/legacy/ui/public/vis/vis.d.ts @@ -30,8 +30,6 @@ export interface Vis { [key: string]: any; } -export type VisProvider = (...dependencies: any[]) => Vis; - export interface VisParams { [key: string]: any; } diff --git a/src/legacy/ui/public/vis/vis.js b/src/legacy/ui/public/vis/vis.js index c1fff1556e3ad..304289a5cfa07 100644 --- a/src/legacy/ui/public/vis/vis.js +++ b/src/legacy/ui/public/vis/vis.js @@ -38,181 +38,178 @@ import { start as visualizations } from '../../../core_plugins/visualizations/pu import '../directives/bind'; -export function VisProvider(Private, getAppState) { - const visTypes = visualizations.types; - - class Vis extends EventEmitter { - constructor(indexPattern, visState) { - super(); - visState = visState || {}; - - if (_.isString(visState)) { - visState = { - type: visState - }; - } - this.indexPattern = indexPattern; - this._setUiState(new PersistedState()); - this.setCurrentState(visState); - this.setState(this.getCurrentState(), false); - - // Session state is for storing information that is transitory, and will not be saved with the visualization. - // For instance, map bounds, which depends on the view port, browser window size, etc. - this.sessionState = {}; - - this.API = { - SearchSource: SearchSource, - events: { - filter: data => this.eventsSubject.next({ name: 'filterBucket', data }), - brush: data => this.eventsSubject.next({ name: 'brush', data }), - }, - getAppState, +const visTypes = visualizations.types; + +class Vis extends EventEmitter { + constructor(indexPattern, visState) { + super(); + visState = visState || {}; + + if (_.isString(visState)) { + visState = { + type: visState }; } + this.indexPattern = indexPattern; + this._setUiState(new PersistedState()); + this.setCurrentState(visState); + this.setState(this.getCurrentState(), false); + + // Session state is for storing information that is transitory, and will not be saved with the visualization. + // For instance, map bounds, which depends on the view port, browser window size, etc. + this.sessionState = {}; + + this.API = { + SearchSource: SearchSource, + events: { + filter: data => this.eventsSubject.next({ name: 'filterBucket', data }), + brush: data => this.eventsSubject.next({ name: 'brush', data }), + }, + }; + } - setCurrentState(state) { - this.title = state.title || ''; - const type = state.type || this.type; - if (_.isString(type)) { - this.type = visTypes.get(type); - if (!this.type) { - throw new Error(`Invalid type "${type}"`); - } - } else { - this.type = type; + setCurrentState(state) { + this.title = state.title || ''; + const type = state.type || this.type; + if (_.isString(type)) { + this.type = visTypes.get(type); + if (!this.type) { + throw new Error(`Invalid type "${type}"`); } + } else { + this.type = type; + } - this.params = _.defaults({}, - _.cloneDeep(state.params || {}), - _.cloneDeep(this.type.visConfig.defaults || {}) - ); + this.params = _.defaults({}, + _.cloneDeep(state.params || {}), + _.cloneDeep(this.type.visConfig.defaults || {}) + ); - updateVisualizationConfig(state.params, this.params); + updateVisualizationConfig(state.params, this.params); - if (state.aggs || !this.aggs) { - this.aggs = new AggConfigs(this.indexPattern, state.aggs ? state.aggs.aggs || state.aggs : [], this.type.schemas.all); - } + if (state.aggs || !this.aggs) { + this.aggs = new AggConfigs(this.indexPattern, state.aggs ? state.aggs.aggs || state.aggs : [], this.type.schemas.all); } + } - setState(state, updateCurrentState = true) { - this._state = _.cloneDeep(state); - if (updateCurrentState) { - this.setCurrentState(this._state); - } + setState(state, updateCurrentState = true) { + this._state = _.cloneDeep(state); + if (updateCurrentState) { + this.setCurrentState(this._state); } + } - updateState() { - this.setState(this.getCurrentState(true)); - this.emit('update'); - } + updateState() { + this.setState(this.getCurrentState(true)); + this.emit('update'); + } - forceReload() { - this.emit('reload'); - } + forceReload() { + this.emit('reload'); + } - getCurrentState(includeDisabled) { - return { - title: this.title, - type: this.type.name, - params: _.cloneDeep(this.params), - aggs: this.aggs.aggs - .map(agg => agg.toJSON()) - .filter(agg => includeDisabled || agg.enabled) - .filter(Boolean) - }; - } + getCurrentState(includeDisabled) { + return { + title: this.title, + type: this.type.name, + params: _.cloneDeep(this.params), + aggs: this.aggs.aggs + .map(agg => agg.toJSON()) + .filter(agg => includeDisabled || agg.enabled) + .filter(Boolean) + }; + } - getSerializableState(state) { - return { - title: state.title, - type: state.type, - params: _.cloneDeep(state.params), - aggs: state.aggs.aggs - .map(agg => agg.toJSON()) - .filter(agg => agg.enabled) - .filter(Boolean) - }; - } + getSerializableState(state) { + return { + title: state.title, + type: state.type, + params: _.cloneDeep(state.params), + aggs: state.aggs.aggs + .map(agg => agg.toJSON()) + .filter(agg => agg.enabled) + .filter(Boolean) + }; + } - copyCurrentState(includeDisabled = false) { - const state = this.getCurrentState(includeDisabled); - state.aggs = new AggConfigs(this.indexPattern, state.aggs.aggs || state.aggs, this.type.schemas.all); - return state; - } + copyCurrentState(includeDisabled = false) { + const state = this.getCurrentState(includeDisabled); + state.aggs = new AggConfigs(this.indexPattern, state.aggs.aggs || state.aggs, this.type.schemas.all); + return state; + } - getStateInternal(includeDisabled) { - return { - title: this._state.title, - type: this._state.type, - params: this._state.params, - aggs: this._state.aggs - .filter(agg => includeDisabled || agg.enabled) - }; - } + getStateInternal(includeDisabled) { + return { + title: this._state.title, + type: this._state.type, + params: this._state.params, + aggs: this._state.aggs + .filter(agg => includeDisabled || agg.enabled) + }; + } - getEnabledState() { - return this.getStateInternal(false); - } + getEnabledState() { + return this.getStateInternal(false); + } - getAggConfig() { - return this.aggs.clone({ enabledOnly: true }); - } + getAggConfig() { + return this.aggs.clone({ enabledOnly: true }); + } - getState() { - return this.getStateInternal(true); - } + getState() { + return this.getStateInternal(true); + } - isHierarchical() { - if (_.isFunction(this.type.hierarchicalData)) { - return !!this.type.hierarchicalData(this); - } else { - return !!this.type.hierarchicalData; - } + isHierarchical() { + if (_.isFunction(this.type.hierarchicalData)) { + return !!this.type.hierarchicalData(this); + } else { + return !!this.type.hierarchicalData; } + } - hasSchemaAgg(schemaName, aggTypeName) { - const aggs = this.aggs.bySchemaName(schemaName) || []; - return aggs.some(function (agg) { - if (!agg.type || !agg.type.name) return false; - return agg.type.name === aggTypeName; - }); - } + hasSchemaAgg(schemaName, aggTypeName) { + const aggs = this.aggs.bySchemaName(schemaName) || []; + return aggs.some(function (agg) { + if (!agg.type || !agg.type.name) return false; + return agg.type.name === aggTypeName; + }); + } - hasUiState() { - return !!this.__uiState; - } + hasUiState() { + return !!this.__uiState; + } - /*** - * this should not be used outside of visualize - * @param uiState - * @private - */ - _setUiState(uiState) { - if (uiState instanceof PersistedState) { - this.__uiState = uiState; - } + /*** + * this should not be used outside of visualize + * @param uiState + * @private + */ + _setUiState(uiState) { + if (uiState instanceof PersistedState) { + this.__uiState = uiState; } + } - getUiState() { - return this.__uiState; - } + getUiState() { + return this.__uiState; + } - /** - * Currently this is only used to extract map-specific information - * (e.g. mapZoom, mapCenter). - */ - uiStateVal(key, val) { - if (this.hasUiState()) { - if (_.isUndefined(val)) { - return this.__uiState.get(key); - } - return this.__uiState.set(key, val); + /** + * Currently this is only used to extract map-specific information + * (e.g. mapZoom, mapCenter). + */ + uiStateVal(key, val) { + if (this.hasUiState()) { + if (_.isUndefined(val)) { + return this.__uiState.get(key); } - return val; + return this.__uiState.set(key, val); } + return val; } +} - Vis.prototype.type = 'histogram'; +Vis.prototype.type = 'histogram'; - return Vis; -} +export { Vis }; diff --git a/src/legacy/ui/public/vis/vis_types/__tests__/vislib_vis_legend.js b/src/legacy/ui/public/vis/vis_types/__tests__/vislib_vis_legend.js index 18eef99f05a75..afb3fea15a430 100644 --- a/src/legacy/ui/public/vis/vis_types/__tests__/vislib_vis_legend.js +++ b/src/legacy/ui/public/vis/vis_types/__tests__/vislib_vis_legend.js @@ -21,7 +21,7 @@ import $ from 'jquery'; import _ from 'lodash'; import expect from '@kbn/expect'; import ngMock from 'ng_mock'; -import { VisProvider } from '../../vis'; +import { Vis } from '../../vis'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; describe('visualize_legend directive', function () { @@ -29,7 +29,6 @@ describe('visualize_legend directive', function () { let $compile; let $timeout; let $el; - let Vis; let indexPattern; let fixtures; @@ -39,7 +38,6 @@ describe('visualize_legend directive', function () { $compile = $injector.get('$compile'); $timeout = $injector.get('$timeout'); fixtures = require('fixtures/fake_hierarchical_data'); - Vis = Private(VisProvider); indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); })); diff --git a/src/legacy/ui/public/vislib/__tests__/visualizations/pie_chart.js b/src/legacy/ui/public/vislib/__tests__/visualizations/pie_chart.js index a4f62e6ceb431..5692ae65af281 100644 --- a/src/legacy/ui/public/vislib/__tests__/visualizations/pie_chart.js +++ b/src/legacy/ui/public/vislib/__tests__/visualizations/pie_chart.js @@ -24,7 +24,7 @@ import _ from 'lodash'; import fixtures from 'fixtures/fake_hierarchical_data'; import $ from 'jquery'; import FixturesVislibVisFixtureProvider from 'fixtures/vislib/_vis_fixture'; -import { VisProvider } from '../../../vis'; +import { Vis } from '../../../vis'; import '../../../persisted_state'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; import { vislibSlicesResponseHandlerProvider } from '../../../vis/response_handlers/vislib'; @@ -113,7 +113,6 @@ describe('No global chart settings', function () { addTooltip: true }; let chart1; - let Vis; let persistedState; let indexPattern; let responseHandler; @@ -123,7 +122,6 @@ describe('No global chart settings', function () { beforeEach(ngMock.module('kibana')); beforeEach(ngMock.inject(function (Private, $injector) { chart1 = Private(FixturesVislibVisFixtureProvider)(visLibParams1); - Vis = Private(VisProvider); persistedState = new ($injector.get('PersistedState'))(); indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); responseHandler = vislibSlicesResponseHandlerProvider().handler; @@ -203,7 +201,6 @@ describe('Vislib PieChart Class Test Suite', function () { addTooltip: true }; let vis; - let Vis; let persistedState; let indexPattern; let data; @@ -213,7 +210,6 @@ describe('Vislib PieChart Class Test Suite', function () { beforeEach(ngMock.module('kibana')); beforeEach(ngMock.inject(function (Private, $injector) { vis = Private(FixturesVislibVisFixtureProvider)(visLibParams); - Vis = Private(VisProvider); persistedState = new ($injector.get('PersistedState'))(); indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); responseHandler = vislibSlicesResponseHandlerProvider().handler; diff --git a/src/legacy/ui/public/visualize/loader/pipeline_helpers/build_pipeline.test.ts b/src/legacy/ui/public/visualize/loader/pipeline_helpers/build_pipeline.test.ts index 056f3e8cfc586..70e0c1f1382fa 100644 --- a/src/legacy/ui/public/visualize/loader/pipeline_helpers/build_pipeline.test.ts +++ b/src/legacy/ui/public/visualize/loader/pipeline_helpers/build_pipeline.test.ts @@ -172,7 +172,10 @@ describe('visualize loader pipeline helpers: build pipeline', () => { const visState = { ...visStateDef, params: { foo: 'bar' } }; const schemas = { ...schemasDef, - metric: [{ ...schemaConfig, accessor: 0 }, { ...schemaConfig, accessor: 1 }], + metric: [ + { ...schemaConfig, accessor: 0 }, + { ...schemaConfig, accessor: 1 }, + ], }; const actual = buildPipelineVisFunction.table(visState, schemas, uiState); expect(actual).toMatchSnapshot(); @@ -192,7 +195,10 @@ describe('visualize loader pipeline helpers: build pipeline', () => { const visState = { ...visStateDef, params: { foo: 'bar' } }; const schemas = { ...schemasDef, - metric: [{ ...schemaConfig, accessor: 0 }, { ...schemaConfig, accessor: 1 }], + metric: [ + { ...schemaConfig, accessor: 0 }, + { ...schemaConfig, accessor: 1 }, + ], split_row: [2, 4], bucket: [3], }; @@ -250,7 +256,10 @@ describe('visualize loader pipeline helpers: build pipeline', () => { const visState = { ...visStateDef, params: { metric: {} } }; const schemas = { ...schemasDef, - metric: [{ ...schemaConfig, accessor: 0 }, { ...schemaConfig, accessor: 1 }], + metric: [ + { ...schemaConfig, accessor: 0 }, + { ...schemaConfig, accessor: 1 }, + ], }; const actual = buildPipelineVisFunction.metric(visState, schemas, uiState); expect(actual).toMatchSnapshot(); @@ -260,7 +269,10 @@ describe('visualize loader pipeline helpers: build pipeline', () => { const visState = { ...visStateDef, params: { metric: {} } }; const schemas = { ...schemasDef, - metric: [{ ...schemaConfig, accessor: 0 }, { ...schemaConfig, accessor: 1 }], + metric: [ + { ...schemaConfig, accessor: 0 }, + { ...schemaConfig, accessor: 1 }, + ], group: [{ accessor: 2 }], }; const actual = buildPipelineVisFunction.metric(visState, schemas, uiState); diff --git a/src/legacy/ui/public/visualize/loader/utils/query_geohash_bounds.ts b/src/legacy/ui/public/visualize/loader/utils/query_geohash_bounds.ts index 912afab74bef4..36759551a1723 100644 --- a/src/legacy/ui/public/visualize/loader/utils/query_geohash_bounds.ts +++ b/src/legacy/ui/public/visualize/loader/utils/query_geohash_bounds.ts @@ -22,11 +22,10 @@ import { get } from 'lodash'; import { toastNotifications } from 'ui/notify'; import { AggConfig } from 'ui/vis'; -import { Query } from 'src/legacy/core_plugins/data/public'; import { timefilter } from 'ui/timefilter'; import { Vis } from '../../../vis'; +import { esFilters, Query } from '../../../../../../plugins/data/public'; import { SearchSource } from '../../../courier'; -import { esFilters } from '../../../../../../plugins/data/public'; interface QueryGeohashBoundsParams { filters?: esFilters.Filter[]; diff --git a/src/legacy/ui/public/visualize/loader/vis.js b/src/legacy/ui/public/visualize/loader/vis.js index 1942fd58afebb..29208563055d0 100644 --- a/src/legacy/ui/public/visualize/loader/vis.js +++ b/src/legacy/ui/public/visualize/loader/vis.js @@ -33,114 +33,109 @@ import { PersistedState } from '../../persisted_state'; import { start as visualizations } from '../../../../core_plugins/visualizations/public/np_ready/public/legacy'; -export function VisProvider(getAppState) { - const visTypes = visualizations.types; - - class Vis extends EventEmitter { - constructor(visState = { type: 'histogram' }) { - super(); - - this._setUiState(new PersistedState()); - this.setState(visState); - - // Session state is for storing information that is transitory, and will not be saved with the visualization. - // For instance, map bounds, which depends on the view port, browser window size, etc. - this.sessionState = {}; - - this.API = { - events: { - filter: data => { - if (!this.eventsSubject) return; - this.eventsSubject.next({ name: 'filterBucket', data }); - }, - brush: data => { - if (!this.eventsSubject) return; - this.eventsSubject.next({ name: 'brush', data }); - }, +const visTypes = visualizations.types; + +export class Vis extends EventEmitter { + constructor(visState = { type: 'histogram' }) { + super(); + + this._setUiState(new PersistedState()); + this.setState(visState); + + // Session state is for storing information that is transitory, and will not be saved with the visualization. + // For instance, map bounds, which depends on the view port, browser window size, etc. + this.sessionState = {}; + + this.API = { + events: { + filter: data => { + if (!this.eventsSubject) return; + this.eventsSubject.next({ name: 'filterBucket', data }); }, - getAppState, - }; - } + brush: data => { + if (!this.eventsSubject) return; + this.eventsSubject.next({ name: 'brush', data }); + }, + }, + }; + } - setState(state) { - this.title = state.title || ''; - const type = state.type || this.type; - if (_.isString(type)) { - this.type = visTypes.get(type); - if (!this.type) { - throw new Error(`Invalid type "${type}"`); - } - } else { - this.type = type; + setState(state) { + this.title = state.title || ''; + const type = state.type || this.type; + if (_.isString(type)) { + this.type = visTypes.get(type); + if (!this.type) { + throw new Error(`Invalid type "${type}"`); } - - this.params = _.defaultsDeep({}, - _.cloneDeep(state.params || {}), - _.cloneDeep(this.type.visConfig.defaults || {}) - ); + } else { + this.type = type; } - setCurrentState(state) { - this.setState(state); - } + this.params = _.defaultsDeep({}, + _.cloneDeep(state.params || {}), + _.cloneDeep(this.type.visConfig.defaults || {}) + ); + } - getState() { - return { - title: this.title, - type: this.type.name, - params: _.cloneDeep(this.params), - }; - } + setCurrentState(state) { + this.setState(state); + } - updateState() { - this.emit('update'); - } + getState() { + return { + title: this.title, + type: this.type.name, + params: _.cloneDeep(this.params), + }; + } - forceReload() { - this.emit('reload'); - } + updateState() { + this.emit('update'); + } - isHierarchical() { - if (_.isFunction(this.type.hierarchicalData)) { - return !!this.type.hierarchicalData(this); - } else { - return !!this.type.hierarchicalData; - } - } + forceReload() { + this.emit('reload'); + } - hasUiState() { - return !!this.__uiState; + isHierarchical() { + if (_.isFunction(this.type.hierarchicalData)) { + return !!this.type.hierarchicalData(this); + } else { + return !!this.type.hierarchicalData; } + } - /*** - * this should not be used outside of visualize - * @param uiState - * @private - */ - _setUiState(uiState) { - if (uiState instanceof PersistedState) { - this.__uiState = uiState; - } - } + hasUiState() { + return !!this.__uiState; + } - getUiState() { - return this.__uiState; + /*** + * this should not be used outside of visualize + * @param uiState + * @private + */ + _setUiState(uiState) { + if (uiState instanceof PersistedState) { + this.__uiState = uiState; } + } - /** - * Currently this is only used to extract map-specific information - * (e.g. mapZoom, mapCenter). - */ - uiStateVal(key, val) { - if (this.hasUiState()) { - if (_.isUndefined(val)) { - return this.__uiState.get(key); - } - return this.__uiState.set(key, val); + getUiState() { + return this.__uiState; + } + + /** + * Currently this is only used to extract map-specific information + * (e.g. mapZoom, mapCenter). + */ + uiStateVal(key, val) { + if (this.hasUiState()) { + if (_.isUndefined(val)) { + return this.__uiState.get(key); } - return val; + return this.__uiState.set(key, val); } + return val; } - - return Vis; } diff --git a/src/plugins/dashboard_embeddable_container/public/actions/replace_panel_flyout.tsx b/src/plugins/dashboard_embeddable_container/public/actions/replace_panel_flyout.tsx index 02e5f45fae3bd..36efd0bcba676 100644 --- a/src/plugins/dashboard_embeddable_container/public/actions/replace_panel_flyout.tsx +++ b/src/plugins/dashboard_embeddable_container/public/actions/replace_panel_flyout.tsx @@ -19,15 +19,9 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; -import { - EuiFlyout, - EuiFlyoutBody, - EuiFlyoutHeader, - EuiTitle, - EuiGlobalToastListToast as Toast, -} from '@elastic/eui'; +import { EuiFlyout, EuiFlyoutBody, EuiFlyoutHeader, EuiTitle } from '@elastic/eui'; import { DashboardPanelState } from '../embeddable'; -import { NotificationsStart } from '../../../../core/public'; +import { NotificationsStart, Toast } from '../../../../core/public'; import { IContainer, IEmbeddable, diff --git a/src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_container.tsx b/src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_container.tsx index 8b258f3558438..6cefd11c912f1 100644 --- a/src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_container.tsx @@ -20,7 +20,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { I18nProvider } from '@kbn/i18n/react'; -import { RefreshInterval, TimeRange, Query } from '../../../data/public'; +import { RefreshInterval, TimeRange, Query, esFilters } from '../../../data/public'; import { CoreStart } from '../../../../core/public'; import { IUiActionsStart } from '../ui_actions_plugin'; import { @@ -37,7 +37,6 @@ import { createPanelState } from './panel'; import { DashboardPanelState } from './types'; import { DashboardViewport } from './viewport/dashboard_viewport'; import { Start as InspectorStartContract } from '../../../inspector/public'; -import { esFilters } from '../../../../plugins/data/public'; import { KibanaContextProvider, KibanaReactContext, diff --git a/src/plugins/dashboard_embeddable_container/public/plugin.tsx b/src/plugins/dashboard_embeddable_container/public/plugin.tsx index eadf70a36416a..dbb5a06da9cd9 100644 --- a/src/plugins/dashboard_embeddable_container/public/plugin.tsx +++ b/src/plugins/dashboard_embeddable_container/public/plugin.tsx @@ -61,9 +61,10 @@ export class DashboardEmbeddableContainerPublicPlugin const { application, notifications, overlays } = core; const { embeddable, inspector, uiActions } = plugins; - const SavedObjectFinder: React.FC< - Exclude - > = props => ( + const SavedObjectFinder: React.FC> = props => ( string); params?: any; + value?: string | ((formatter?: FilterValueFormatter) => string); } export interface Filter { diff --git a/src/plugins/data/public/query/filter_manager/index.ts b/src/plugins/data/public/query/filter_manager/index.ts index 7955cdd825ee6..ce7a479151797 100644 --- a/src/plugins/data/public/query/filter_manager/index.ts +++ b/src/plugins/data/public/query/filter_manager/index.ts @@ -22,3 +22,4 @@ export { FilterManager } from './filter_manager'; export { uniqFilters } from './lib/uniq_filters'; export { mapAndFlattenFilters } from './lib/map_and_flatten_filters'; export { onlyDisabledFiltersChanged } from './lib/only_disabled'; +export { generateFilters } from './lib/generate_filters'; diff --git a/src/plugins/data/public/query/filter_manager/lib/generate_filter.test.ts b/src/plugins/data/public/query/filter_manager/lib/generate_filter.test.ts new file mode 100644 index 0000000000000..46cf0fd9c111e --- /dev/null +++ b/src/plugins/data/public/query/filter_manager/lib/generate_filter.test.ts @@ -0,0 +1,130 @@ +/* + * 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 { generateFilters } from './generate_filters'; +import { FilterManager } from '../filter_manager'; +import { esFilters } from '../../..'; + +const INDEX_NAME = 'my-index'; +const EXISTS_FIELD_NAME = '_exists_'; +const FIELD = { + name: 'my-field', +}; +const PHRASE_VALUE = 'my-value'; + +describe('Generate filters', () => { + let mockFilterManager: FilterManager; + let filtersArray: esFilters.Filter[]; + + beforeEach(() => { + filtersArray = []; + mockFilterManager = { + getAppFilters: () => { + return filtersArray; + }, + } as FilterManager; + }); + + it('should create exists filter', () => { + const filters = generateFilters( + mockFilterManager, + EXISTS_FIELD_NAME, + FIELD.name, + '', + INDEX_NAME + ); + expect(filters).toHaveLength(1); + expect(filters[0].meta.index === INDEX_NAME); + expect(filters[0].meta.negate).toBeFalsy(); + expect(esFilters.isExistsFilter(filters[0])).toBeTruthy(); + }); + + it('should create negated exists filter', () => { + const filters = generateFilters( + mockFilterManager, + EXISTS_FIELD_NAME, + FIELD.name, + '-', + INDEX_NAME + ); + expect(filters).toHaveLength(1); + expect(filters[0].meta.index === INDEX_NAME); + expect(filters[0].meta.negate).toBeTruthy(); + expect(esFilters.isExistsFilter(filters[0])).toBeTruthy(); + }); + + it('should update and re-enable EXISTING exists filter', () => { + const filter = esFilters.buildExistsFilter(FIELD, { id: INDEX_NAME }); + filter.meta.disabled = true; + filtersArray.push(filter); + + const filters = generateFilters(mockFilterManager, '_exists_', FIELD.name, '-', INDEX_NAME); + expect(filters).toHaveLength(1); + expect(filters[0].meta.index === INDEX_NAME); + expect(filters[0].meta.negate).toBeTruthy(); + expect(filters[0].meta.disabled).toBeFalsy(); + expect(esFilters.isExistsFilter(filters[0])).toBeTruthy(); + }); + + it('should create phrase filter', () => { + const filters = generateFilters(mockFilterManager, FIELD, PHRASE_VALUE, '', INDEX_NAME); + expect(filters).toHaveLength(1); + expect(filters[0].meta.index === INDEX_NAME); + expect(filters[0].meta.negate).toBeFalsy(); + expect(esFilters.isPhraseFilter(filters[0])).toBeTruthy(); + expect((filters[0] as esFilters.PhraseFilter).query.match_phrase).toEqual({ + [FIELD.name]: PHRASE_VALUE, + }); + }); + + it('should create negated phrase filter', () => { + const filters = generateFilters(mockFilterManager, FIELD, PHRASE_VALUE, '-', INDEX_NAME); + expect(filters).toHaveLength(1); + expect(filters[0].meta.index === INDEX_NAME); + expect(filters[0].meta.negate).toBeTruthy(); + expect(esFilters.isPhraseFilter(filters[0])).toBeTruthy(); + expect((filters[0] as esFilters.PhraseFilter).query.match_phrase).toEqual({ + [FIELD.name]: PHRASE_VALUE, + }); + }); + + it('should create multiple phrase filters', () => { + const ANOTHER_PHRASE = 'another-value'; + const filters = generateFilters( + mockFilterManager, + FIELD, + [PHRASE_VALUE, ANOTHER_PHRASE], + '', + INDEX_NAME + ); + expect(filters).toHaveLength(2); + expect(filters[0].meta.index === INDEX_NAME); + expect(filters[0].meta.negate).toBeFalsy(); + expect(filters[1].meta.index === INDEX_NAME); + expect(filters[1].meta.negate).toBeFalsy(); + expect(esFilters.isPhraseFilter(filters[0])).toBeTruthy(); + expect(esFilters.isPhraseFilter(filters[1])).toBeTruthy(); + expect((filters[0] as esFilters.PhraseFilter).query.match_phrase).toEqual({ + [FIELD.name]: PHRASE_VALUE, + }); + expect((filters[1] as esFilters.PhraseFilter).query.match_phrase).toEqual({ + [FIELD.name]: ANOTHER_PHRASE, + }); + }); +}); diff --git a/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts b/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts new file mode 100644 index 0000000000000..5c4cdc2717338 --- /dev/null +++ b/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts @@ -0,0 +1,112 @@ +/* + * 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 _ from 'lodash'; +import { FilterManager, esFilters, Field } from '../../..'; + +function getExistingFilter( + appFilters: esFilters.Filter[], + fieldName: string, + value: any +): esFilters.Filter | undefined { + // TODO: On array fields, negating does not negate the combination, rather all terms + return _.find(appFilters, function(filter) { + if (!filter) return; + + if (fieldName === '_exists_' && esFilters.isExistsFilter(filter)) { + return filter.exists!.field === value; + } + + if (esFilters.isPhraseFilter(filter)) { + return ( + esFilters.getPhraseFilterField(filter) === fieldName && + esFilters.getPhraseFilterValue(filter) === value + ); + } + + if (esFilters.isScriptedPhraseFilter(filter)) { + return filter.meta.field === fieldName && filter.meta.script!.script.params.value === value; + } + }); +} + +function updateExistingFilter(existingFilter: esFilters.Filter, negate: boolean) { + existingFilter.meta.disabled = false; + if (existingFilter.meta.negate !== negate) { + existingFilter.meta.negate = !existingFilter.meta.negate; + } +} + +/** + * Generate filter objects, as a result of triggering a filter action on a + * specific index pattern field. + * + * @param {FilterManager} filterManager - The active filter manager to lookup for existing filters + * @param {Field | string} field - The field for which filters should be generated + * @param {any} values - One or more values to filter for. + * @param {string} operation - "-" to create a negated filter + * @param {string} index - Index string to generate filters for + * + * @returns {object} An array of filters to be added back to filterManager + */ +export function generateFilters( + filterManager: FilterManager, + field: Field | string, + values: any, + operation: string, + index: string +): esFilters.Filter[] { + values = Array.isArray(values) ? values : [values]; + const fieldObj = _.isObject(field) + ? field + : { + name: field, + }; + const fieldName = fieldObj.name; + const newFilters: esFilters.Filter[] = []; + const appFilters = filterManager.getAppFilters(); + + const negate = operation === '-'; + let filter; + + _.each(values, function(value) { + const existing = getExistingFilter(appFilters, fieldName, value); + + if (existing) { + updateExistingFilter(existing, negate); + filter = existing; + } else { + const tmpIndexPattern = { id: index }; + switch (fieldName) { + case '_exists_': + filter = esFilters.buildExistsFilter(fieldObj, tmpIndexPattern); + break; + default: + filter = esFilters.buildPhraseFilter(fieldObj, value, tmpIndexPattern); + break; + } + + filter.meta.negate = negate; + } + + newFilters.push(filter); + }); + + return newFilters; +} diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.test.ts index 3afa3891a24bb..1847296016c73 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.test.ts @@ -30,7 +30,10 @@ describe('filter manager utilities', () => { }, geo_polygon: { point: { - points: [{ lat: 5, lon: 10 }, { lat: 15, lon: 20 }], + points: [ + { lat: 5, lon: 10 }, + { lat: 15, lon: 20 }, + ], }, }, } as esFilters.GeoPolygonFilter; diff --git a/src/plugins/data/public/query/index.tsx b/src/plugins/data/public/query/index.tsx index 9d7c2ffc56f70..224c2f3a04076 100644 --- a/src/plugins/data/public/query/index.tsx +++ b/src/plugins/data/public/query/index.tsx @@ -17,6 +17,8 @@ * under the License. */ +export * from './lib'; + export * from './query_service'; export * from './filter_manager'; diff --git a/src/plugins/data/public/query/lib/from_user.test.ts b/src/plugins/data/public/query/lib/from_user.test.ts new file mode 100644 index 0000000000000..b74a1a07dc356 --- /dev/null +++ b/src/plugins/data/public/query/lib/from_user.test.ts @@ -0,0 +1,45 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { fromUser } from '../'; + +describe('user input helpers', function() { + describe('user input parser', function() { + it('should return the input if passed an object', function() { + expect(fromUser({ foo: 'bar' })).toEqual({ foo: 'bar' }); + }); + + it('unless the object is empty, then convert it to an empty string', function() { + expect(fromUser({})).toEqual(''); + }); + + it('should pass through input strings that not start with {', function() { + expect(fromUser('foo')).toEqual('foo'); + expect(fromUser('400')).toEqual('400'); + expect(fromUser('true')).toEqual('true'); + }); + + it('should parse valid JSON and return the object instead of a string', function() { + expect(fromUser('{}')).toEqual({}); + + // invalid json remains a string + expect(fromUser('{a:b}')).toEqual('{a:b}'); + }); + }); +}); diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/from_user.ts b/src/plugins/data/public/query/lib/from_user.ts similarity index 96% rename from src/legacy/core_plugins/data/public/query/query_bar/lib/from_user.ts rename to src/plugins/data/public/query/lib/from_user.ts index 15eebaa0b9fd6..fbb1726fc99ea 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/lib/from_user.ts +++ b/src/plugins/data/public/query/lib/from_user.ts @@ -28,6 +28,10 @@ import _ from 'lodash'; export function fromUser(userInput: object | string) { const matchAll = ''; + if (_.isEmpty(userInput)) { + return ''; + } + if (_.isObject(userInput)) { return userInput; } diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/get_query_log.ts b/src/plugins/data/public/query/lib/get_query_log.ts similarity index 94% rename from src/legacy/core_plugins/data/public/query/query_bar/lib/get_query_log.ts rename to src/plugins/data/public/query/lib/get_query_log.ts index 66424d9a1d6a3..67073a9078046 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/lib/get_query_log.ts +++ b/src/plugins/data/public/query/lib/get_query_log.ts @@ -19,7 +19,7 @@ import { UiSettingsClientContract } from 'src/core/public'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; -import { PersistedLog } from '../../../../../../../plugins/data/public'; +import { PersistedLog } from '../persisted_log'; export function getQueryLog( uiSettings: UiSettingsClientContract, diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/index.ts b/src/plugins/data/public/query/lib/index.ts similarity index 93% rename from src/legacy/core_plugins/data/public/query/query_bar/lib/index.ts rename to src/plugins/data/public/query/lib/index.ts index 852f9fd269b32..19ac37fb59ae7 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/lib/index.ts +++ b/src/plugins/data/public/query/lib/index.ts @@ -20,3 +20,5 @@ export * from './match_pairs'; export * from './from_user'; export * from './to_user'; +export * from './to_user'; +export * from './get_query_log'; diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/match_pairs.ts b/src/plugins/data/public/query/lib/match_pairs.ts similarity index 100% rename from src/legacy/core_plugins/data/public/query/query_bar/lib/match_pairs.ts rename to src/plugins/data/public/query/lib/match_pairs.ts diff --git a/src/plugins/data/public/query/lib/to_user.test.ts b/src/plugins/data/public/query/lib/to_user.test.ts new file mode 100644 index 0000000000000..d13afa251ecb1 --- /dev/null +++ b/src/plugins/data/public/query/lib/to_user.test.ts @@ -0,0 +1,46 @@ +/* + * 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 { toUser } from '../'; + +describe('user input helpers', function() { + describe('model presentation formatter', function() { + it('should present objects as strings', function() { + expect(toUser({ foo: 'bar' })).toBe('{"foo":"bar"}'); + }); + + it('should present query_string queries as strings', function() { + expect(toUser({ query_string: { query: 'lucene query string' } })).toBe( + 'lucene query string' + ); + }); + + it('should present query_string queries without a query as an empty string', function() { + expect(toUser({ query_string: {} })).toBe(''); + }); + + it('should present string as strings', function() { + expect(toUser('foo')).toBe('foo'); + }); + + it('should present numbers as strings', function() { + expect(toUser(400)).toBe('400'); + }); + }); +}); diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/to_user.ts b/src/plugins/data/public/query/lib/to_user.ts similarity index 89% rename from src/legacy/core_plugins/data/public/query/query_bar/lib/to_user.ts rename to src/plugins/data/public/query/lib/to_user.ts index 1eb054ba40514..1fdb2d8ed03df 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/lib/to_user.ts +++ b/src/plugins/data/public/query/lib/to_user.ts @@ -17,14 +17,12 @@ * under the License. */ -import angular from 'angular'; - /** * Take text from the model and present it to the user as a string * @param text model value * @returns {string} */ -export function toUser(text: { [key: string]: any } | string): string { +export function toUser(text: { [key: string]: any } | string | number): string { if (text == null) { return ''; } @@ -35,7 +33,7 @@ export function toUser(text: { [key: string]: any } | string): string { if (text.query_string) { return toUser(text.query_string.query); } - return angular.toJson(text); + return JSON.stringify(text); } return '' + text; } diff --git a/src/plugins/data/public/search/create_app_mount_context_search.test.ts b/src/plugins/data/public/search/create_app_mount_context_search.test.ts index deab9af4e3a01..fa7cdbcda3082 100644 --- a/src/plugins/data/public/search/create_app_mount_context_search.test.ts +++ b/src/plugins/data/public/search/create_app_mount_context_search.test.ts @@ -62,8 +62,10 @@ describe('Create app mount search context', () => { }); }, }); - context - .search({ greeting: 'hi' } as any, {}, 'mysearch') - .subscribe(response => {}, () => {}, done); + context.search({ greeting: 'hi' } as any, {}, 'mysearch').subscribe( + response => {}, + () => {}, + done + ); }); }); diff --git a/src/plugins/data/public/search/es_search/es_search_strategy.ts b/src/plugins/data/public/search/es_search/es_search_strategy.ts index 643ded120799e..d29f3b6882b26 100644 --- a/src/plugins/data/public/search/es_search/es_search_strategy.ts +++ b/src/plugins/data/public/search/es_search/es_search_strategy.ts @@ -20,6 +20,7 @@ import { Observable } from 'rxjs'; import { ES_SEARCH_STRATEGY, IEsSearchResponse } from '../../../common/search'; import { SYNC_SEARCH_STRATEGY } from '../sync_search_strategy'; +import { getEsPreference } from './get_es_preference'; import { TSearchStrategyProvider, ISearchStrategy, ISearchGeneric, ISearchContext } from '..'; export const esSearchStrategyProvider: TSearchStrategyProvider = ( @@ -27,11 +28,17 @@ export const esSearchStrategyProvider: TSearchStrategyProvider => { return { - search: (request, options) => - search( + search: (request, options) => { + if (typeof request.params.preference === 'undefined') { + const setPreference = context.core.uiSettings.get('courier:setRequestPreference'); + const customPreference = context.core.uiSettings.get('courier:customRequestPreference'); + request.params.preference = getEsPreference(setPreference, customPreference); + } + return search( { ...request, serverStrategy: ES_SEARCH_STRATEGY }, options, SYNC_SEARCH_STRATEGY - ) as Observable, + ) as Observable; + }, }; }; diff --git a/src/plugins/data/public/search/es_search/get_es_preference.test.ts b/src/plugins/data/public/search/es_search/get_es_preference.test.ts new file mode 100644 index 0000000000000..27e6f9b48bbdd --- /dev/null +++ b/src/plugins/data/public/search/es_search/get_es_preference.test.ts @@ -0,0 +1,46 @@ +/* + * 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 { getEsPreference } from './get_es_preference'; + +jest.useFakeTimers(); + +describe('Get ES preference', () => { + test('returns the session ID if set to sessionId', () => { + const setPreference = 'sessionId'; + const customPreference = 'foobar'; + const sessionId = 'my_session_id'; + const preference = getEsPreference(setPreference, customPreference, sessionId); + expect(preference).toBe(sessionId); + }); + + test('returns the custom preference if set to custom', () => { + const setPreference = 'custom'; + const customPreference = 'foobar'; + const preference = getEsPreference(setPreference, customPreference); + expect(preference).toBe(customPreference); + }); + + test('returns undefined if set to none', () => { + const setPreference = 'none'; + const customPreference = 'foobar'; + const preference = getEsPreference(setPreference, customPreference); + expect(preference).toBe(undefined); + }); +}); diff --git a/src/legacy/ui/public/registry/dev_tools.js b/src/plugins/data/public/search/es_search/get_es_preference.ts similarity index 70% rename from src/legacy/ui/public/registry/dev_tools.js rename to src/plugins/data/public/search/es_search/get_es_preference.ts index 1741f39f86375..200e5bacb7f18 100644 --- a/src/legacy/ui/public/registry/dev_tools.js +++ b/src/plugins/data/public/search/es_search/get_es_preference.ts @@ -17,11 +17,13 @@ * under the License. */ -import { uiRegistry } from './_registry'; - -export const DevToolsRegistryProvider = uiRegistry({ - name: 'devTools', - index: ['name'], - order: ['order'] -}); +const defaultSessionId = `${Date.now()}`; +export function getEsPreference( + setRequestPreference: string, + customRequestPreference?: string, + sessionId: string = defaultSessionId +) { + if (setRequestPreference === 'sessionId') return `${sessionId}`; + return setRequestPreference === 'custom' ? customRequestPreference : undefined; +} diff --git a/src/plugins/dev_tools/README.md b/src/plugins/dev_tools/README.md new file mode 100644 index 0000000000000..1610411b9c98e --- /dev/null +++ b/src/plugins/dev_tools/README.md @@ -0,0 +1,29 @@ +# Dev tools plugin + +The ui/registry/dev_tools is removed in favor of the `dev_tools` plugin which exposes a register method in the setup contract. +Registering app works mostly the same as registering apps in core.application.register. +Routing will be handled by the id of the dev tool - your dev tool will be mounted when the URL matches `/app/kibana#/dev_tools/`. +This API doesn't support angular, for registering angular dev tools, bootstrap a local module on mount into the given HTML element. + +During the migration this plugin exposes the registered dev tools in the start contract. This is necessary to keep the dev tools app +which is still living in the legacy platform working and will be removed once everything is moved over to the new platform. It should +not be used by other plugins. + +## Example registration + +```ts +// For legacy plugins +import { npSetup } from 'ui/new_platform'; +npSetup.plugins.dev_tools.register(/* same details here */); + +// For new plugins: first add 'dev_tools' to the list of `optionalPlugins` +// in your kibana.json file. Then access the plugin directly in `setup`: + +class MyPlugin { + setup(core, plugins) { + if (plugins.dev_tools) { + plugins.dev_tools.register(/* same details here. */); + } + } +} +``` diff --git a/src/plugins/dev_tools/kibana.json b/src/plugins/dev_tools/kibana.json new file mode 100644 index 0000000000000..307035c7ec664 --- /dev/null +++ b/src/plugins/dev_tools/kibana.json @@ -0,0 +1,6 @@ +{ + "id": "devTools", + "version": "kibana", + "server": false, + "ui": true +} diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/actions/filter.js b/src/plugins/dev_tools/public/index.ts similarity index 77% rename from src/legacy/core_plugins/kibana/public/discover/angular/doc_table/actions/filter.js rename to src/plugins/dev_tools/public/index.ts index 1a2854ec15412..3a0d1455e2168 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/actions/filter.js +++ b/src/plugins/dev_tools/public/index.ts @@ -17,10 +17,11 @@ * under the License. */ -export function addFilter(field, values = [], operation, index, state, filterGen) { - if (!Array.isArray(values)) { - values = [values]; - } +import { PluginInitializerContext } from 'kibana/public'; +import { DevToolsPlugin } from './plugin'; - filterGen.add(field, values, operation, index); +export function plugin(initializerContext: PluginInitializerContext) { + return new DevToolsPlugin(); } + +export * from './plugin'; diff --git a/src/plugins/dev_tools/public/plugin.ts b/src/plugins/dev_tools/public/plugin.ts new file mode 100644 index 0000000000000..8098308c0882b --- /dev/null +++ b/src/plugins/dev_tools/public/plugin.ts @@ -0,0 +1,115 @@ +/* + * 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 { App, CoreSetup, Plugin } from 'kibana/public'; +import { sortBy } from 'lodash'; + +export interface DevToolsSetup { + /** + * Register a developer tool. It will be available + * in the dev tools app under a separate tab. + * + * Registering dev tools works almost similar to registering + * applications in the core application service, + * but they will be rendered with a frame containing tabs + * to switch between the tools. + * @param devTool The dev tools descriptor + */ + register: (devTool: DevTool) => void; +} + +export interface DevToolsStart { + /** + * Returns all registered dev tools in an ordered array. + * This function is only exposed because the dev tools app + * actually rendering the tool has to stay in the legacy platform + * for now. Once it is moved into this plugin, this function + * becomes an implementation detail. + * @deprecated + */ + getSortedDevTools: () => readonly DevTool[]; +} + +/** + * Descriptor for a dev tool. A dev tool works similar to an application + * registered in the core application service. + */ +export interface DevTool { + /** + * The id of the dev tools. This will become part of the URL path + * (`dev_tools/${devTool.id}`. It has to be unique among registered + * dev tools. + */ + id: string; + /** + * The human readable name of the dev tool. Should be internationalized. + * This will be used as a label in the tab above the actual tool. + */ + title: string; + mount: App['mount']; + /** + * Flag indicating to disable the tab of this dev tool. Navigating to a + * disabled dev tool will be treated as the navigation to an unknown route + * (redirect to the console). + */ + disabled?: boolean; + /** + * Optional tooltip content of the tab. + */ + tooltipContent?: string; + /** + * Flag indicating whether the dev tool will do routing within the `dev_tools/${devTool.id}/` + * prefix. If it is set to true, the dev tool is responsible to redirect + * the user when navigating to unknown URLs within the prefix. If set + * to false only the root URL of the dev tool will be recognized as valid. + */ + enableRouting: boolean; + /** + * Number used to order the tabs. + */ + order: number; +} + +export class DevToolsPlugin implements Plugin { + private readonly devTools = new Map(); + + private getSortedDevTools(): readonly DevTool[] { + return sortBy([...this.devTools.values()], 'order'); + } + + public setup(core: CoreSetup) { + return { + register: (devTool: DevTool) => { + if (this.devTools.has(devTool.id)) { + throw new Error( + `Dev tool with id [${devTool.id}] has already been registered. Use a unique id.` + ); + } + + this.devTools.set(devTool.id, devTool); + }, + }; + } + + public start() { + return { + getSortedDevTools: this.getSortedDevTools.bind(this), + }; + } +} diff --git a/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx b/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx index b11bd167e15f2..70d7c99d3fb9d 100644 --- a/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx +++ b/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx @@ -173,7 +173,7 @@ test('Can set title to an empty string', async () => { ); const inputField = findTestSubject(component, 'customizePanelHideTitle'); - inputField.simulate('change'); + inputField.simulate('click'); findTestSubject(component, 'saveNewTitleButton').simulate('click'); expect(inputField.props().value).toBeUndefined(); diff --git a/src/plugins/es_ui_shared/static/forms/components/fields/toggle_field.tsx b/src/plugins/es_ui_shared/static/forms/components/fields/toggle_field.tsx index 417f3436a2c63..0c075c497a4d0 100644 --- a/src/plugins/es_ui_shared/static/forms/components/fields/toggle_field.tsx +++ b/src/plugins/es_ui_shared/static/forms/components/fields/toggle_field.tsx @@ -18,7 +18,7 @@ */ import React from 'react'; -import { EuiFormRow, EuiSwitch } from '@elastic/eui'; +import { EuiFormRow, EuiSwitch, EuiSwitchEvent } from '@elastic/eui'; import { FieldHook } from '../../hook_form_lib'; import { getFieldValidityAndErrorMessage } from '../helpers'; @@ -33,6 +33,14 @@ interface Props { export const ToggleField = ({ field, euiFieldProps = {}, ...rest }: Props) => { const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field); + // Shim for sufficient overlap between EuiSwitchEvent and FieldHook[onChange] event + const onChange = (e: EuiSwitchEvent) => { + const event = ({ ...e, value: `${e.target.checked}` } as unknown) as React.ChangeEvent<{ + value: string; + }>; + field.onChange(event); + }; + return ( { diff --git a/src/plugins/es_ui_shared/static/forms/helpers/serializers.ts b/src/plugins/es_ui_shared/static/forms/helpers/serializers.ts index e288f61de8681..0bb89cc1af593 100644 --- a/src/plugins/es_ui_shared/static/forms/helpers/serializers.ts +++ b/src/plugins/es_ui_shared/static/forms/helpers/serializers.ts @@ -69,24 +69,21 @@ export const stripEmptyFields = ( ): { [key: string]: any } => { const { types = ['string', 'object'], recursive = false } = options || {}; - return Object.entries(object).reduce( - (acc, [key, value]) => { - const type = typeof value; - const shouldStrip = types.includes(type as 'string'); + return Object.entries(object).reduce((acc, [key, value]) => { + const type = typeof value; + const shouldStrip = types.includes(type as 'string'); - if (shouldStrip && type === 'string' && value.trim() === '') { + if (shouldStrip && type === 'string' && value.trim() === '') { + return acc; + } else if (type === 'object' && !Array.isArray(value) && value !== null) { + if (Object.keys(value).length === 0 && shouldStrip) { return acc; - } else if (type === 'object' && !Array.isArray(value) && value !== null) { - if (Object.keys(value).length === 0 && shouldStrip) { - return acc; - } else if (recursive) { - value = stripEmptyFields({ ...value }, options); - } + } else if (recursive) { + value = stripEmptyFields({ ...value }, options); } + } - acc[key] = value; - return acc; - }, - {} as { [key: string]: any } - ); + acc[key] = value; + return acc; + }, {} as { [key: string]: any }); }; diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts index 360182368ae63..3902b0615a33d 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts @@ -65,15 +65,12 @@ export function useForm( const stripEmptyFields = (fields: FieldsMap): FieldsMap => { if (formOptions.stripEmptyFields) { - return Object.entries(fields).reduce( - (acc, [key, field]) => { - if (typeof field.value !== 'string' || field.value.trim() !== '') { - acc[key] = field; - } - return acc; - }, - {} as FieldsMap - ); + return Object.entries(fields).reduce((acc, [key, field]) => { + if (typeof field.value !== 'string' || field.value.trim() !== '') { + acc[key] = field; + } + return acc; + }, {} as FieldsMap); } return fields; }; diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/lib/utils.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/lib/utils.ts index 66c3e8d983f98..62867a0c07a6b 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/lib/utils.ts +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/lib/utils.ts @@ -50,10 +50,7 @@ export const mapFormFields = ( formFields: Record, fn: (field: FieldHook) => any ) => - Object.entries(formFields).reduce( - (acc, [key, field]) => { - acc[key] = fn(field); - return acc; - }, - {} as Record - ); + Object.entries(formFields).reduce((acc, [key, field]) => { + acc[key] = fn(field); + return acc; + }, {} as Record); diff --git a/src/plugins/expressions/public/interpreter_provider.ts b/src/plugins/expressions/public/interpreter_provider.ts index cb84370ad69c5..15d6b1c025f54 100644 --- a/src/plugins/expressions/public/interpreter_provider.ts +++ b/src/plugins/expressions/public/interpreter_provider.ts @@ -163,9 +163,9 @@ export function interpreterProvider(config: InterpreterConfig): ExpressionInterp // Check for missing required arguments each(argDefs, argDef => { - const { aliases, default: argDefault, name: argName, required } = argDef as (ArgumentType< + const { aliases, default: argDefault, name: argName, required } = argDef as ArgumentType< any - > & { name: string }); + > & { name: string }; if ( typeof argDefault === 'undefined' && required && diff --git a/src/plugins/expressions/public/types/index.ts b/src/plugins/expressions/public/types/index.ts index 87ef810682f60..faceef4f90a6f 100644 --- a/src/plugins/expressions/public/types/index.ts +++ b/src/plugins/expressions/public/types/index.ts @@ -21,8 +21,6 @@ import { ExpressionInterpret } from '../interpreter_provider'; import { TimeRange } from '../../../data/public'; import { Adapters } from '../../../inspector/public'; import { Query } from '../../../data/public'; -import { ExpressionAST } from '../../../expressions/public'; -import { ExpressionArgAST } from '../../../../plugins/expressions/public'; import { esFilters } from '../../../../plugins/data/public'; export { ArgumentType } from './arguments'; diff --git a/src/plugins/kibana_react/public/context/context.tsx b/src/plugins/kibana_react/public/context/context.tsx index cbae5c4638ca2..cbf2ad07b463e 100644 --- a/src/plugins/kibana_react/public/context/context.tsx +++ b/src/plugins/kibana_react/public/context/context.tsx @@ -32,12 +32,11 @@ const defaultContextValue = { export const context = createContext>(defaultContextValue); -export const useKibana = (): KibanaReactContextValue< - KibanaServices & Extra -> => - useContext((context as unknown) as React.Context< - KibanaReactContextValue - >); +export const useKibana = (): KibanaReactContextValue => + useContext( + (context as unknown) as React.Context> + ); export const withKibana = }>( type: React.ComponentType diff --git a/src/plugins/kibana_react/public/index.ts b/src/plugins/kibana_react/public/index.ts index cf025ec2e88d4..2d82f646c827b 100644 --- a/src/plugins/kibana_react/public/index.ts +++ b/src/plugins/kibana_react/public/index.ts @@ -24,3 +24,4 @@ export * from './overlays'; export * from './ui_settings'; export * from './field_icon'; export * from './table_list_view'; +export { toMountPoint } from './util'; diff --git a/src/plugins/kibana_react/public/notifications/create_notifications.test.tsx b/src/plugins/kibana_react/public/notifications/create_notifications.test.tsx index 35c503d590b2c..4f64a2b95f512 100644 --- a/src/plugins/kibana_react/public/notifications/create_notifications.test.tsx +++ b/src/plugins/kibana_react/public/notifications/create_notifications.test.tsx @@ -52,9 +52,20 @@ test('can display string element as title', () => { wrapper.toasts.show({ title: 'foo' }); expect(notifications.toasts.add).toHaveBeenCalledTimes(1); - expect(notifications.toasts.add.mock.calls[0][0]).toMatchObject({ - title: 'foo', - }); + expect(notifications.toasts.add.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "color": undefined, + "iconType": undefined, + "onClose": undefined, + "text": MountPoint { + "reactNode": , + }, + "title": MountPoint { + "reactNode": "foo", + }, + "toastLifeTimeMs": undefined, + } + `); }); test('can display React element as title', () => { @@ -67,10 +78,12 @@ test('can display React element as title', () => { expect(notifications.toasts.add).toHaveBeenCalledTimes(1); expect((notifications.toasts.add.mock.calls[0][0] as any).title).toMatchInlineSnapshot(` -

- bar -
- `); + MountPoint { + "reactNode":
+ bar +
, + } + `); }); test('can display React element as toast body', () => { @@ -81,12 +94,14 @@ test('can display React element as toast body', () => { expect(notifications.toasts.add).toHaveBeenCalledTimes(1); expect((notifications.toasts.add.mock.calls[0][0] as any).text).toMatchInlineSnapshot(` - -
- baz -
-
- `); + MountPoint { + "reactNode": +
+ baz +
+
, + } + `); }); test('can set toast properties', () => { @@ -102,17 +117,21 @@ test('can set toast properties', () => { }); expect(notifications.toasts.add.mock.calls[0][0]).toMatchInlineSnapshot(` - Object { - "color": "danger", - "iconType": "foo", - "onClose": undefined, - "text": - 1 - , - "title": "2", - "toastLifeTimeMs": 3, - } - `); + Object { + "color": "danger", + "iconType": "foo", + "onClose": undefined, + "text": MountPoint { + "reactNode": + 1 + , + }, + "title": MountPoint { + "reactNode": "2", + }, + "toastLifeTimeMs": 3, + } + `); }); test('can display success, warning and danger toasts', () => { @@ -124,21 +143,48 @@ test('can display success, warning and danger toasts', () => { wrapper.toasts.danger({ title: '3' }); expect(notifications.toasts.add).toHaveBeenCalledTimes(3); - expect(notifications.toasts.add.mock.calls[0][0]).toMatchObject({ - title: '1', - color: 'success', - iconType: 'check', - }); - expect(notifications.toasts.add.mock.calls[1][0]).toMatchObject({ - title: '2', - color: 'warning', - iconType: 'help', - }); - expect(notifications.toasts.add.mock.calls[2][0]).toMatchObject({ - title: '3', - color: 'danger', - iconType: 'alert', - }); + expect(notifications.toasts.add.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "color": "success", + "iconType": "check", + "onClose": undefined, + "text": MountPoint { + "reactNode": , + }, + "title": MountPoint { + "reactNode": "1", + }, + "toastLifeTimeMs": undefined, + } + `); + expect(notifications.toasts.add.mock.calls[1][0]).toMatchInlineSnapshot(` + Object { + "color": "warning", + "iconType": "help", + "onClose": undefined, + "text": MountPoint { + "reactNode": , + }, + "title": MountPoint { + "reactNode": "2", + }, + "toastLifeTimeMs": undefined, + } + `); + expect(notifications.toasts.add.mock.calls[2][0]).toMatchInlineSnapshot(` + Object { + "color": "danger", + "iconType": "alert", + "onClose": undefined, + "text": MountPoint { + "reactNode": , + }, + "title": MountPoint { + "reactNode": "3", + }, + "toastLifeTimeMs": undefined, + } + `); }); test('if body is not set, renders it empty', () => { @@ -147,7 +193,9 @@ test('if body is not set, renders it empty', () => { wrapper.toasts.success({ title: '1' }); - expect((notifications.toasts.add.mock.calls[0][0] as any).text).toMatchInlineSnapshot( - `` - ); + expect((notifications.toasts.add.mock.calls[0][0] as any).text).toMatchInlineSnapshot(` + MountPoint { + "reactNode": , + } + `); }); diff --git a/src/plugins/kibana_react/public/notifications/create_notifications.tsx b/src/plugins/kibana_react/public/notifications/create_notifications.tsx index 28c1d5391d160..774f74863ee6f 100644 --- a/src/plugins/kibana_react/public/notifications/create_notifications.tsx +++ b/src/plugins/kibana_react/public/notifications/create_notifications.tsx @@ -20,6 +20,7 @@ import * as React from 'react'; import { KibanaServices } from '../context/types'; import { KibanaReactNotifications } from './types'; +import { toMountPoint } from '../util'; export const createNotifications = (services: KibanaServices): KibanaReactNotifications => { const show: KibanaReactNotifications['toasts']['show'] = ({ @@ -34,8 +35,8 @@ export const createNotifications = (services: KibanaServices): KibanaReactNotifi throw new TypeError('Could not show notification as notifications service is not available.'); } services.notifications!.toasts.add({ - title, - text: <>{body || null}, + title: toMountPoint(title), + text: toMountPoint(<>{body || null}), color, iconType, toastLifeTimeMs, diff --git a/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.tsx b/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.tsx index e1e7f1c536342..bab710cdca595 100644 --- a/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.tsx +++ b/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.tsx @@ -32,6 +32,7 @@ import { EuiOverlayMask, EuiSpacer, EuiSwitch, + EuiSwitchEvent, EuiTextArea, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -227,7 +228,7 @@ export class SavedObjectSaveModal extends React.Component { }); }; - private onCopyOnSaveChange = (event: React.ChangeEvent) => { + private onCopyOnSaveChange = (event: EuiSwitchEvent) => { this.setState({ copyOnSave: event.target.checked, }); diff --git a/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx index 7d95c00e76419..dde8efa7e1106 100644 --- a/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx +++ b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx @@ -38,6 +38,7 @@ import { EuiCallOut, } from '@elastic/eui'; import { ToastsStart, UiSettingsClientContract } from 'kibana/public'; +import { toMountPoint } from '../util'; export const EMPTY_FILTER = ''; @@ -166,7 +167,7 @@ class TableListView extends React.Component itemsById[id])); } catch (error) { this.props.toastNotifications.addDanger({ - title: ( + title: toMountPoint( { + const mount = (element: HTMLElement) => { + ReactDOM.render({node}, element); + return () => ReactDOM.unmountComponentAtNode(element); + }; + // only used for tests and snapshots serialization + if (process.env.NODE_ENV !== 'production') { + mount.__reactMount__ = node; } -} - -uiModules.get('kibana').run(hideEmptyDevTools); + return mount; +}; diff --git a/src/plugins/kibana_react/public/util/test_helpers/react_mount_serializer.ts b/src/plugins/kibana_react/public/util/test_helpers/react_mount_serializer.ts new file mode 100644 index 0000000000000..45ad4cb407175 --- /dev/null +++ b/src/plugins/kibana_react/public/util/test_helpers/react_mount_serializer.ts @@ -0,0 +1,30 @@ +/* + * 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 function test(value: any) { + return value && value.__reactMount__; +} + +export function print(value: any, serialize: any) { + // there is no proper way to correctly indent multiline values + // so the trick here is to use the Object representation and rewriting the root object name + return serialize({ + reactNode: value.__reactMount__, + }).replace('Object', 'MountPoint'); +} diff --git a/src/plugins/newsfeed/public/lib/api.test.ts b/src/plugins/newsfeed/public/lib/api.test.ts index b9707ff91b936..4383b9e0f7dab 100644 --- a/src/plugins/newsfeed/public/lib/api.test.ts +++ b/src/plugins/newsfeed/public/lib/api.test.ts @@ -631,10 +631,7 @@ describe('getApi', () => { .mockImplementationOnce(getHttpMockWithItems(successItems)); getApi(httpMock, configMock.newsfeed, '6.8.2') - .pipe( - take(4), - toArray() - ) + .pipe(take(4), toArray()) .subscribe(result => { expect(result).toMatchInlineSnapshot(` Array [ diff --git a/test/functional/apps/discover/_shared_links.js b/test/functional/apps/discover/_shared_links.js index f0d34207a87a2..0b2b4f14f126d 100644 --- a/test/functional/apps/discover/_shared_links.js +++ b/test/functional/apps/discover/_shared_links.js @@ -25,11 +25,12 @@ export default function ({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['common', 'discover', 'share', 'timePicker']); + const browser = getService('browser'); describe('shared links', function describeIndexTests() { let baseUrl; - before(async function () { + async function setup({ storeStateInSessionStorage }) { baseUrl = PageObjects.common.getHostPort(); log.debug('baseUrl = ' + baseUrl); // browsers don't show the ':port' if it's 80 or 443 so we have to @@ -47,9 +48,12 @@ export default function ({ getService, getPageObjects }) { log.debug('load kibana index with default index pattern'); await esArchiver.load('discover'); - await esArchiver.loadIfNeeded('logstash_functional'); + await kibanaServer.uiSettings.replace({ + 'state:storeInSessionStorage': storeStateInSessionStorage + }); + log.debug('discover'); await PageObjects.common.navigateToApp('discover'); @@ -60,47 +64,103 @@ export default function ({ getService, getPageObjects }) { await PageObjects.common.sleep(1000); await PageObjects.share.clickShareTopNavButton(); - }); - describe('permalink', function () { - it('should allow for copying the snapshot URL', async function () { - const expectedUrl = - baseUrl + - '/app/kibana?_t=1453775307251#' + - '/discover?_g=(refreshInterval:(pause:!t,value:0),time' + - ':(from:\'2015-09-19T06:31:44.000Z\',to:\'2015-09' + - '-23T18:31:44.000Z\'))&_a=(columns:!(_source),index:\'logstash-' + - '*\',interval:auto,query:(language:kuery,query:\'\')' + - ',sort:!(!(\'@timestamp\',desc)))'; - const actualUrl = await PageObjects.share.getSharedUrl(); - // strip the timestamp out of each URL - expect(actualUrl.replace(/_t=\d{13}/, '_t=TIMESTAMP')).to.be( - expectedUrl.replace(/_t=\d{13}/, '_t=TIMESTAMP') - ); + return async () => { + await kibanaServer.uiSettings.replace({ + 'state:storeInSessionStorage': undefined + }); + }; + } + + describe('shared links with state in query', async () => { + let teardown; + before(async function () { + teardown = await setup({ storeStateInSessionStorage: false }); + }); + + after(async function () { + await teardown(); }); - it('should allow for copying the snapshot URL as a short URL', async function () { - const re = new RegExp(baseUrl + '/goto/[0-9a-f]{32}$'); - await PageObjects.share.checkShortenUrl(); - await retry.try(async () => { + describe('permalink', function () { + it('should allow for copying the snapshot URL', async function () { + const expectedUrl = + baseUrl + + '/app/kibana?_t=1453775307251#' + + '/discover?_g=(refreshInterval:(pause:!t,value:0),time' + + ':(from:\'2015-09-19T06:31:44.000Z\',to:\'2015-09' + + '-23T18:31:44.000Z\'))&_a=(columns:!(_source),index:\'logstash-' + + '*\',interval:auto,query:(language:kuery,query:\'\')' + + ',sort:!(!(\'@timestamp\',desc)))'; + const actualUrl = await PageObjects.share.getSharedUrl(); + // strip the timestamp out of each URL + expect(actualUrl.replace(/_t=\d{13}/, '_t=TIMESTAMP')).to.be( + expectedUrl.replace(/_t=\d{13}/, '_t=TIMESTAMP') + ); + }); + + it('should allow for copying the snapshot URL as a short URL', async function () { + const re = new RegExp(baseUrl + '/goto/[0-9a-f]{32}$'); + await PageObjects.share.checkShortenUrl(); + await retry.try(async () => { + const actualUrl = await PageObjects.share.getSharedUrl(); + expect(actualUrl).to.match(re); + }); + }); + + it('should allow for copying the saved object URL', async function () { + const expectedUrl = + baseUrl + + '/app/kibana#' + + '/discover/ab12e3c0-f231-11e6-9486-733b1ac9221a' + + '?_g=(refreshInterval%3A(pause%3A!t%2Cvalue%3A0)' + + '%2Ctime%3A(from%3A\'2015-09-19T06%3A31%3A44.000Z\'%2C' + + 'to%3A\'2015-09-23T18%3A31%3A44.000Z\'))'; + await PageObjects.discover.loadSavedSearch('A Saved Search'); + await PageObjects.share.clickShareTopNavButton(); + await PageObjects.share.exportAsSavedObject(); const actualUrl = await PageObjects.share.getSharedUrl(); - expect(actualUrl).to.match(re); + expect(actualUrl).to.be(expectedUrl); }); }); + }); + + describe('shared links with state in sessionStorage', async () => { + let teardown; + before(async function () { + teardown = await setup({ storeStateInSessionStorage: true }); + }); - it('should allow for copying the saved object URL', async function () { - const expectedUrl = - baseUrl + - '/app/kibana#' + - '/discover/ab12e3c0-f231-11e6-9486-733b1ac9221a' + - '?_g=(refreshInterval%3A(pause%3A!t%2Cvalue%3A0)' + - '%2Ctime%3A(from%3A\'2015-09-19T06%3A31%3A44.000Z\'%2C' + - 'to%3A\'2015-09-23T18%3A31%3A44.000Z\'))'; - await PageObjects.discover.loadSavedSearch('A Saved Search'); - await PageObjects.share.clickShareTopNavButton(); - await PageObjects.share.exportAsSavedObject(); - const actualUrl = await PageObjects.share.getSharedUrl(); - expect(actualUrl).to.be(expectedUrl); + after(async function () { + await teardown(); + }); + + describe('permalink', function () { + it('should allow for copying the snapshot URL as a short URL and should open it', async function () { + const re = new RegExp(baseUrl + '/goto/[0-9a-f]{32}$'); + await PageObjects.share.checkShortenUrl(); + let actualUrl; + await retry.try(async () => { + actualUrl = await PageObjects.share.getSharedUrl(); + expect(actualUrl).to.match(re); + }); + + const actualTime = await PageObjects.timePicker.getTimeConfig(); + + await browser.clearSessionStorage(); + await browser.get(actualUrl, false); + await retry.waitFor( + 'shortUrl resolves and opens', + async () => { + const resolvedUrl = await browser.getCurrentUrl(); + expect(resolvedUrl).to.match(/discover/); + const resolvedTime = await PageObjects.timePicker.getTimeConfig(); + expect(resolvedTime.start).to.equal(actualTime.start); + expect(resolvedTime.end).to.equal(actualTime.end); + return true; + } + ); + }); }); }); }); diff --git a/test/functional/apps/visualize/input_control_vis/input_control_options.js b/test/functional/apps/visualize/input_control_vis/input_control_options.js index b659d29b158b7..4088ab6193a59 100644 --- a/test/functional/apps/visualize/input_control_vis/input_control_options.js +++ b/test/functional/apps/visualize/input_control_vis/input_control_options.js @@ -133,13 +133,13 @@ export default function ({ getService, getPageObjects }) { describe('updateFiltersOnChange is true', () => { before(async () => { await PageObjects.visualize.clickVisEditorTab('options'); - await PageObjects.visualize.checkCheckbox('inputControlEditorUpdateFiltersOnChangeCheckbox'); + await PageObjects.visualize.checkSwitch('inputControlEditorUpdateFiltersOnChangeCheckbox'); await PageObjects.visualize.clickGo(); }); after(async () => { await PageObjects.visualize.clickVisEditorTab('options'); - await PageObjects.visualize.uncheckCheckbox('inputControlEditorUpdateFiltersOnChangeCheckbox'); + await PageObjects.visualize.uncheckSwitch('inputControlEditorUpdateFiltersOnChangeCheckbox'); await PageObjects.visualize.clickGo(); }); diff --git a/test/functional/page_objects/dashboard_page.js b/test/functional/page_objects/dashboard_page.js index ca141114f976d..af3a15e9b3015 100644 --- a/test/functional/page_objects/dashboard_page.js +++ b/test/functional/page_objects/dashboard_page.js @@ -347,7 +347,7 @@ export function DashboardPageProvider({ getService, getPageObjects }) { async clickSave() { log.debug('DashboardPage.clickSave'); - await testSubjects.clickWhenNotDisabled('confirmSaveSavedObjectButton'); + await testSubjects.click('confirmSaveSavedObjectButton'); } async pressEnterKey() { @@ -543,9 +543,10 @@ export function DashboardPageProvider({ getService, getPageObjects }) { async setSaveAsNewCheckBox(checked) { log.debug('saveAsNewCheckbox: ' + checked); const saveAsNewCheckbox = await testSubjects.find('saveAsNewCheckbox'); - const isAlreadyChecked = (await saveAsNewCheckbox.getAttribute('checked') === 'true'); + const isAlreadyChecked = (await saveAsNewCheckbox.getAttribute('aria-checked') === 'true'); if (isAlreadyChecked !== checked) { log.debug('Flipping save as new checkbox'); + const saveAsNewCheckbox = await testSubjects.find('saveAsNewCheckbox'); await retry.try(() => saveAsNewCheckbox.click()); } } @@ -553,9 +554,10 @@ export function DashboardPageProvider({ getService, getPageObjects }) { async setStoreTimeWithDashboard(checked) { log.debug('Storing time with dashboard: ' + checked); const storeTimeCheckbox = await testSubjects.find('storeTimeWithDashboard'); - const isAlreadyChecked = (await storeTimeCheckbox.getAttribute('checked') === 'true'); + const isAlreadyChecked = (await storeTimeCheckbox.getAttribute('aria-checked') === 'true'); if (isAlreadyChecked !== checked) { log.debug('Flipping store time checkbox'); + const storeTimeCheckbox = await testSubjects.find('storeTimeWithDashboard'); await retry.try(() => storeTimeCheckbox.click()); } } diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index 570511bee4bc5..4b65de57f12d8 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -305,9 +305,9 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro public async getRhythmChartLegendValue(nth = 0) { await PageObjects.visualize.waitForVisualizationRenderingStabilized(); - const metricValue = (await find.allByCssSelector( - `.echLegendItem .echLegendItem__displayValue` - ))[nth]; + const metricValue = ( + await find.allByCssSelector(`.echLegendItem .echLegendItem__displayValue`) + )[nth]; await metricValue.moveMouseTo(); return await metricValue.getVisibleText(); } diff --git a/test/functional/page_objects/visualize_page.js b/test/functional/page_objects/visualize_page.js index f3a90f20b6686..81d26a4b69478 100644 --- a/test/functional/page_objects/visualize_page.js +++ b/test/functional/page_objects/visualize_page.js @@ -372,6 +372,28 @@ export function VisualizePageProvider({ getService, getPageObjects, updateBaseli } } + async isSwitchChecked(selector) { + const checkbox = await testSubjects.find(selector); + const isChecked = await checkbox.getAttribute('aria-checked'); + return isChecked === 'true'; + } + + async checkSwitch(selector) { + const isChecked = await this.isSwitchChecked(selector); + if (!isChecked) { + log.debug(`checking switch ${selector}`); + await testSubjects.click(selector); + } + } + + async uncheckSwitch(selector) { + const isChecked = await this.isSwitchChecked(selector); + if (isChecked) { + log.debug(`unchecking switch ${selector}`); + await testSubjects.click(selector); + } + } + async setSelectByOptionText(selectId, optionText) { const selectField = await find.byCssSelector(`#${selectId}`); const options = await find.allByCssSelector(`#${selectId} > option`); @@ -1009,7 +1031,7 @@ export function VisualizePageProvider({ getService, getPageObjects, updateBaseli async setIsFilteredByCollarCheckbox(value = true) { await retry.try(async () => { - const isChecked = await this.isChecked('isFilteredByCollarCheckbox'); + const isChecked = await this.isSwitchChecked('isFilteredByCollarCheckbox'); if (isChecked !== value) { await testSubjects.click('isFilteredByCollarCheckbox'); throw new Error('isFilteredByCollar not set correctly'); diff --git a/test/functional/services/browser.ts b/test/functional/services/browser.ts index 97e02958f3787..a8ce4270d4205 100644 --- a/test/functional/services/browser.ts +++ b/test/functional/services/browser.ts @@ -429,6 +429,15 @@ export async function BrowserProvider({ getService }: FtrProviderContext) { ); } + /** + * Clears session storage for the focused window/frame. + * + * @return {Promise} + */ + public async clearSessionStorage(): Promise { + await driver.executeScript('return window.sessionStorage.clear();'); + } + /** * Closes the currently focused window. In most environments, after the window has been * closed, it is necessary to explicitly switch to whatever window is now focused. diff --git a/test/functional/services/remote/poll_for_log_entry.ts b/test/functional/services/remote/poll_for_log_entry.ts index b6b68cc0d3cf9..71e2711906fce 100644 --- a/test/functional/services/remote/poll_for_log_entry.ts +++ b/test/functional/services/remote/poll_for_log_entry.ts @@ -95,10 +95,7 @@ export function pollForLogEntry$( [new logging.Entry('SEVERE', `ERROR FETCHING BROWSR LOGS: ${error.message}`)], // pause 10 seconds then resubscribe - Rx.of(1).pipe( - delay(10 * 1000), - mergeMapTo(resubscribe) - ) + Rx.of(1).pipe(delay(10 * 1000), mergeMapTo(resubscribe)) ); }) ) diff --git a/test/functional/services/saved_query_management_component.ts b/test/functional/services/saved_query_management_component.ts index f134fde028e09..d6de0be0c172e 100644 --- a/test/functional/services/saved_query_management_component.ts +++ b/test/functional/services/saved_query_management_component.ts @@ -118,15 +118,17 @@ export function SavedQueryManagementComponentProvider({ getService }: FtrProvide await testSubjects.setValue('saveQueryFormDescription', description); const currentIncludeFiltersValue = - (await testSubjects.getAttribute('saveQueryFormIncludeFiltersOption', 'checked')) === + (await testSubjects.getAttribute('saveQueryFormIncludeFiltersOption', 'aria-checked')) === 'true'; if (currentIncludeFiltersValue !== includeFilters) { await testSubjects.click('saveQueryFormIncludeFiltersOption'); } const currentIncludeTimeFilterValue = - (await testSubjects.getAttribute('saveQueryFormIncludeTimeFilterOption', 'checked')) === - 'true'; + (await testSubjects.getAttribute( + 'saveQueryFormIncludeTimeFilterOption', + 'aria-checked' + )) === 'true'; if (currentIncludeTimeFilterValue !== includeTimeFilter) { await testSubjects.click('saveQueryFormIncludeTimeFilterOption'); } diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json index 766e6168002c2..da1bb597f5730 100644 --- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json +++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json @@ -7,7 +7,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "14.8.0", + "@elastic/eui": "14.9.0", "react": "^16.8.0", "react-dom": "^16.8.0" } diff --git a/test/plugin_functional/plugins/core_plugin_a/package.json b/test/plugin_functional/plugins/core_plugin_a/package.json index 7dede34f3c0cf..060ae49f43e8a 100644 --- a/test/plugin_functional/plugins/core_plugin_a/package.json +++ b/test/plugin_functional/plugins/core_plugin_a/package.json @@ -12,6 +12,6 @@ "build": "rm -rf './target' && tsc" }, "devDependencies": { - "typescript": "3.5.3" + "typescript": "3.7.2" } } diff --git a/test/plugin_functional/plugins/core_plugin_b/package.json b/test/plugin_functional/plugins/core_plugin_b/package.json index 18ff8cf7cc5c9..3eb878b9ed5dc 100644 --- a/test/plugin_functional/plugins/core_plugin_b/package.json +++ b/test/plugin_functional/plugins/core_plugin_b/package.json @@ -12,6 +12,6 @@ "build": "rm -rf './target' && tsc" }, "devDependencies": { - "typescript": "3.5.3" + "typescript": "3.7.2" } } diff --git a/test/plugin_functional/plugins/core_plugin_chromeless/kibana.json b/test/plugin_functional/plugins/core_plugin_chromeless/kibana.json new file mode 100644 index 0000000000000..a8a5616627726 --- /dev/null +++ b/test/plugin_functional/plugins/core_plugin_chromeless/kibana.json @@ -0,0 +1,8 @@ +{ + "id": "core_plugin_chromeless", + "version": "0.0.1", + "kibanaVersion": "kibana", + "configPath": ["core_plugin_chromeless"], + "server": false, + "ui": true +} diff --git a/test/plugin_functional/plugins/core_plugin_chromeless/package.json b/test/plugin_functional/plugins/core_plugin_chromeless/package.json new file mode 100644 index 0000000000000..eff6c1e1f142a --- /dev/null +++ b/test/plugin_functional/plugins/core_plugin_chromeless/package.json @@ -0,0 +1,17 @@ +{ + "name": "core_plugin_chromeless", + "version": "1.0.0", + "main": "target/test/plugin_functional/plugins/core_plugin_chromeless", + "kibana": { + "version": "kibana", + "templateVersion": "1.0.0" + }, + "license": "Apache-2.0", + "scripts": { + "kbn": "node ../../../../scripts/kbn.js", + "build": "rm -rf './target' && tsc" + }, + "devDependencies": { + "typescript": "3.5.3" + } +} diff --git a/test/plugin_functional/plugins/core_plugin_chromeless/public/application.tsx b/test/plugin_functional/plugins/core_plugin_chromeless/public/application.tsx new file mode 100644 index 0000000000000..556a9ca140715 --- /dev/null +++ b/test/plugin_functional/plugins/core_plugin_chromeless/public/application.tsx @@ -0,0 +1,74 @@ +/* + * 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 React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { BrowserRouter as Router, Route } from 'react-router-dom'; +import { + EuiPage, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiPageContentHeader, + EuiPageContentHeaderSection, + EuiPageHeader, + EuiPageHeaderSection, + EuiTitle, +} from '@elastic/eui'; + +import { AppMountContext, AppMountParameters } from 'kibana/public'; + +const Home = () => ( + + + + +

Welcome to Chromeless!

+
+
+
+ + + + +

Chromeless home page section title

+
+
+
+ Where did all the chrome go? +
+
+); + +const ChromelessApp = ({ basename }: { basename: string; context: AppMountContext }) => ( + + + + + +); + +export const renderApp = ( + context: AppMountContext, + { appBasePath, element }: AppMountParameters +) => { + render(, element); + + return () => unmountComponentAtNode(element); +}; diff --git a/src/legacy/core_plugins/console/public/quarantined/hacks/register.js b/test/plugin_functional/plugins/core_plugin_chromeless/public/index.ts similarity index 70% rename from src/legacy/core_plugins/console/public/quarantined/hacks/register.js rename to test/plugin_functional/plugins/core_plugin_chromeless/public/index.ts index b5df1c1af99c5..6e9959ecbdf9e 100644 --- a/src/legacy/core_plugins/console/public/quarantined/hacks/register.js +++ b/test/plugin_functional/plugins/core_plugin_chromeless/public/index.ts @@ -17,14 +17,14 @@ * under the License. */ -import { DevToolsRegistryProvider } from 'ui/registry/dev_tools'; -import { i18n } from '@kbn/i18n'; +import { PluginInitializer } from 'kibana/public'; +import { + CorePluginChromelessPlugin, + CorePluginChromelessPluginSetup, + CorePluginChromelessPluginStart, +} from './plugin'; -DevToolsRegistryProvider.register(() => ({ - order: 1, - name: 'console', - display: i18n.translate('console.consoleDisplayName', { - defaultMessage: 'Console', - }), - url: '#/dev_tools/console', -})); +export const plugin: PluginInitializer< + CorePluginChromelessPluginSetup, + CorePluginChromelessPluginStart +> = () => new CorePluginChromelessPlugin(); diff --git a/src/legacy/core_plugins/kibana/public/dev_tools/directives/dev_tools_app.js b/test/plugin_functional/plugins/core_plugin_chromeless/public/plugin.tsx similarity index 51% rename from src/legacy/core_plugins/kibana/public/dev_tools/directives/dev_tools_app.js rename to test/plugin_functional/plugins/core_plugin_chromeless/public/plugin.tsx index c7cb877be676b..03870410fb334 100644 --- a/src/legacy/core_plugins/kibana/public/dev_tools/directives/dev_tools_app.js +++ b/test/plugin_functional/plugins/core_plugin_chromeless/public/plugin.tsx @@ -17,34 +17,31 @@ * under the License. */ -import { uiModules } from 'ui/modules'; -import { DevToolsRegistryProvider } from 'ui/registry/dev_tools'; -import template from '../partials/dev_tools_app.html'; +import { Plugin, CoreSetup } from 'kibana/public'; -uiModules - .get('apps/dev_tools') - .directive('kbnDevToolsApp', function (Private, $location) { - const devToolsRegistry = Private(DevToolsRegistryProvider); +export class CorePluginChromelessPlugin + implements Plugin { + public setup(core: CoreSetup, deps: {}) { + core.application.register({ + id: 'chromeless', + title: 'Chromeless', + chromeless: true, + async mount(context, params) { + const { renderApp } = await import('./application'); + return renderApp(context, params); + }, + }); return { - restrict: 'E', - replace: true, - template, - transclude: true, - scope: { - topNavConfig: '=' + getGreeting() { + return 'Hello from Plugin Chromeless!'; }, - bindToController: true, - controllerAs: 'kbnDevToolsApp', - controller() { - this.devTools = devToolsRegistry.inOrder; - this.currentPath = `#${$location.path()}`; - - this.onClick = (item, $event) => { - if (item.disabled) { - $event.preventDefault(); - } - }; - } }; - }); + } + + public start() {} + public stop() {} +} + +export type CorePluginChromelessPluginSetup = ReturnType; +export type CorePluginChromelessPluginStart = ReturnType; diff --git a/test/plugin_functional/plugins/core_plugin_chromeless/tsconfig.json b/test/plugin_functional/plugins/core_plugin_chromeless/tsconfig.json new file mode 100644 index 0000000000000..5fcaeafbb0d85 --- /dev/null +++ b/test/plugin_functional/plugins/core_plugin_chromeless/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../../../tsconfig.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true + }, + "include": [ + "index.ts", + "public/**/*.ts", + "public/**/*.tsx", + "../../../../typings/**/*", + ], + "exclude": [] +} diff --git a/test/plugin_functional/plugins/core_plugin_legacy/package.json b/test/plugin_functional/plugins/core_plugin_legacy/package.json index 2ae83b28f7e85..5f784c7b836a5 100644 --- a/test/plugin_functional/plugins/core_plugin_legacy/package.json +++ b/test/plugin_functional/plugins/core_plugin_legacy/package.json @@ -12,6 +12,6 @@ "build": "rm -rf './target' && tsc" }, "devDependencies": { - "typescript": "3.5.3" + "typescript": "3.7.2" } } diff --git a/test/plugin_functional/plugins/demo_search/public/demo_search_strategy.ts b/test/plugin_functional/plugins/demo_search/public/demo_search_strategy.ts index 377163251010c..298eaaaf420e0 100644 --- a/test/plugin_functional/plugins/demo_search/public/demo_search_strategy.ts +++ b/test/plugin_functional/plugins/demo_search/public/demo_search_strategy.ts @@ -53,9 +53,7 @@ import { DEMO_SEARCH_STRATEGY, IDemoResponse } from '../common'; * @param context - context supplied by other plugins. * @param search - a search function to access other strategies that have already been registered. */ -export const demoClientSearchStrategyProvider: TSearchStrategyProvider< - typeof DEMO_SEARCH_STRATEGY -> = ( +export const demoClientSearchStrategyProvider: TSearchStrategyProvider = ( context: ISearchContext, search: ISearchGeneric ): ISearchStrategy => { diff --git a/test/plugin_functional/plugins/demo_search/server/demo_search_strategy.ts b/test/plugin_functional/plugins/demo_search/server/demo_search_strategy.ts index acb75b15196d6..d3f2360add6c0 100644 --- a/test/plugin_functional/plugins/demo_search/server/demo_search_strategy.ts +++ b/test/plugin_functional/plugins/demo_search/server/demo_search_strategy.ts @@ -20,9 +20,7 @@ import { TSearchStrategyProvider } from 'src/plugins/data/server'; import { DEMO_SEARCH_STRATEGY } from '../common'; -export const demoSearchStrategyProvider: TSearchStrategyProvider< - typeof DEMO_SEARCH_STRATEGY -> = () => { +export const demoSearchStrategyProvider: TSearchStrategyProvider = () => { return { search: request => { return Promise.resolve({ diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json index 7c5b6f6be58af..4d0444265825a 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json @@ -7,7 +7,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "14.8.0", + "@elastic/eui": "14.9.0", "react": "^16.8.0" } } diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json index ef472b4026957..9df9352f76fc2 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json @@ -8,7 +8,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "14.8.0", + "@elastic/eui": "14.9.0", "react": "^16.8.0" }, "scripts": { @@ -17,6 +17,6 @@ }, "devDependencies": { "@kbn/plugin-helpers": "9.0.2", - "typescript": "3.5.3" + "typescript": "3.7.2" } } diff --git a/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json b/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json index 277bb09ac745c..054276b620907 100644 --- a/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json +++ b/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json @@ -8,7 +8,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "14.8.0", + "@elastic/eui": "14.9.0", "react": "^16.8.0" }, "scripts": { @@ -17,6 +17,6 @@ }, "devDependencies": { "@kbn/plugin-helpers": "9.0.2", - "typescript": "3.5.3" + "typescript": "3.7.2" } } diff --git a/test/plugin_functional/test_suites/core_plugins/applications.ts b/test/plugin_functional/test_suites/core_plugins/applications.ts index eec2ec019a515..c16847dab9dc2 100644 --- a/test/plugin_functional/test_suites/core_plugins/applications.ts +++ b/test/plugin_functional/test_suites/core_plugins/applications.ts @@ -91,6 +91,22 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider await testSubjects.existOrFail('fooAppPageA'); }); + it('chromeless applications are not visible in apps list', async () => { + expect(await appsMenu.linkExists('Chromeless')).to.be(false); + }); + + it('navigating to chromeless application hides chrome', async () => { + await PageObjects.common.navigateToApp('chromeless'); + await loadingScreenNotShown(); + expect(await testSubjects.exists('headerGlobalNav')).to.be(false); + }); + + it('navigating away from chromeless application shows chrome', async () => { + await PageObjects.common.navigateToApp('foo'); + await loadingScreenNotShown(); + expect(await testSubjects.exists('headerGlobalNav')).to.be(true); + }); + it('can navigate from NP apps to legacy apps', async () => { await appsMenu.clickLink('Management'); await loadingScreenShown(); diff --git a/x-pack/dev-tools/jest/create_jest_config.js b/x-pack/dev-tools/jest/create_jest_config.js index 0fba7d2cefbd5..9d601e680cf87 100644 --- a/x-pack/dev-tools/jest/create_jest_config.js +++ b/x-pack/dev-tools/jest/create_jest_config.js @@ -47,7 +47,10 @@ export function createJestConfig({ kibanaDirectory, xPackKibanaDirectory }) { // since ESM modules are not natively supported in Jest yet (https://github.com/facebook/jest/issues/4842) '[/\\\\]node_modules(?![\\/\\\\]@elastic[\\/\\\\]eui)(?![\\/\\\\]monaco-editor)[/\\\\].+\\.js$', ], - snapshotSerializers: [`${kibanaDirectory}/node_modules/enzyme-to-json/serializer`], + snapshotSerializers: [ + `${kibanaDirectory}/node_modules/enzyme-to-json/serializer`, + `${kibanaDirectory}/src/plugins/kibana_react/public/util/test_helpers/react_mount_serializer.ts` + ], reporters: [ 'default', [ diff --git a/x-pack/legacy/common/eui_draggable/index.d.ts b/x-pack/legacy/common/eui_draggable/index.d.ts index a85da7a69534c..322966b3c982e 100644 --- a/x-pack/legacy/common/eui_draggable/index.d.ts +++ b/x-pack/legacy/common/eui_draggable/index.d.ts @@ -8,7 +8,7 @@ import React from 'react'; import { EuiDraggable, EuiDragDropContext } from '@elastic/eui'; type PropsOf = T extends React.ComponentType ? ComponentProps : never; -type FirstArgumentOf = Func extends ((arg1: infer FirstArgument, ...rest: any[]) => any) +type FirstArgumentOf = Func extends (arg1: infer FirstArgument, ...rest: any[]) => any ? FirstArgument : never; export type DragHandleProps = FirstArgumentOf< diff --git a/x-pack/legacy/common/eui_styled_components/eui_styled_components.tsx b/x-pack/legacy/common/eui_styled_components/eui_styled_components.tsx index 8e5fba31ac5a4..8becf6892ff92 100644 --- a/x-pack/legacy/common/eui_styled_components/eui_styled_components.tsx +++ b/x-pack/legacy/common/eui_styled_components/eui_styled_components.tsx @@ -38,6 +38,6 @@ const { injectGlobal, keyframes, withTheme, -} = styledComponents as ThemedStyledComponentsModule; +} = (styledComponents as unknown) as ThemedStyledComponentsModule; export { css, euiStyled, EuiThemeProvider, injectGlobal, keyframes, withTheme }; diff --git a/x-pack/legacy/plugins/actions/server/lib/task_runner_factory.test.ts b/x-pack/legacy/plugins/actions/server/lib/task_runner_factory.test.ts index 3e71725713070..a5bf42bc2cc01 100644 --- a/x-pack/legacy/plugins/actions/server/lib/task_runner_factory.test.ts +++ b/x-pack/legacy/plugins/actions/server/lib/task_runner_factory.test.ts @@ -111,11 +111,9 @@ test('executes the task by calling the executor with proper parameters', async ( expect(runnerResult).toBeUndefined(); expect(spaceIdToNamespace).toHaveBeenCalledWith('test'); - expect(mockedEncryptedSavedObjectsPlugin.getDecryptedAsInternalUser).toHaveBeenCalledWith( - 'action_task_params', - '3', - { namespace: 'namespace-test' } - ); + expect( + mockedEncryptedSavedObjectsPlugin.getDecryptedAsInternalUser + ).toHaveBeenCalledWith('action_task_params', '3', { namespace: 'namespace-test' }); expect(mockedActionExecutor.execute).toHaveBeenCalledWith({ actionId: '2', params: { baz: true }, diff --git a/x-pack/legacy/plugins/apm/common/apm_saved_object_constants.ts b/x-pack/legacy/plugins/apm/common/apm_saved_object_constants.ts new file mode 100644 index 0000000000000..ac43b700117c6 --- /dev/null +++ b/x-pack/legacy/plugins/apm/common/apm_saved_object_constants.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. + */ + +// APM Services telemetry +export const APM_SERVICES_TELEMETRY_SAVED_OBJECT_TYPE = + 'apm-services-telemetry'; +export const APM_SERVICES_TELEMETRY_SAVED_OBJECT_ID = 'apm-services-telemetry'; + +// APM indices +export const APM_INDICES_SAVED_OBJECT_TYPE = 'apm-indices'; +export const APM_INDICES_SAVED_OBJECT_ID = 'apm-indices'; diff --git a/x-pack/legacy/plugins/apm/common/projections/util/merge_projection/index.ts b/x-pack/legacy/plugins/apm/common/projections/util/merge_projection/index.ts index 9a8f11c6493c5..522f6d39ac71a 100644 --- a/x-pack/legacy/plugins/apm/common/projections/util/merge_projection/index.ts +++ b/x-pack/legacy/plugins/apm/common/projections/util/merge_projection/index.ts @@ -21,14 +21,14 @@ type SourceProjection = Omit, 'body'> & { }; type DeepMerge = U extends PlainObject - ? (T extends PlainObject - ? (Omit & - { - [key in keyof U]: T extends { [k in key]: any } - ? DeepMerge - : U[key]; - }) - : U) + ? T extends PlainObject + ? Omit & + { + [key in keyof U]: T extends { [k in key]: any } + ? DeepMerge + : U[key]; + } + : U : U; export function mergeProjection< diff --git a/x-pack/legacy/plugins/grokdebugger/public/sections/grokdebugger/directives/grokdebugger/index.js b/x-pack/legacy/plugins/apm/common/transaction_types.ts similarity index 61% rename from x-pack/legacy/plugins/grokdebugger/public/sections/grokdebugger/directives/grokdebugger/index.js rename to x-pack/legacy/plugins/apm/common/transaction_types.ts index b33973ed71736..4dd59af63047d 100644 --- a/x-pack/legacy/plugins/grokdebugger/public/sections/grokdebugger/directives/grokdebugger/index.js +++ b/x-pack/legacy/plugins/apm/common/transaction_types.ts @@ -4,4 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import './grokdebugger'; +export const TRANSACTION_PAGE_LOAD = 'page-load'; +export const TRANSACTION_ROUTE_CHANGE = 'route-change'; +export const TRANSACTION_REQUEST = 'request'; diff --git a/x-pack/legacy/plugins/apm/cypress/package.json b/x-pack/legacy/plugins/apm/cypress/package.json index 98dcd495b8594..ef8955fcbd1b0 100644 --- a/x-pack/legacy/plugins/apm/cypress/package.json +++ b/x-pack/legacy/plugins/apm/cypress/package.json @@ -11,11 +11,11 @@ "@cypress/snapshot": "^2.1.3", "@cypress/webpack-preprocessor": "^4.1.0", "@types/js-yaml": "^3.12.1", - "cypress": "^3.4.1", + "cypress": "^3.5.0", "js-yaml": "^3.13.1", "p-limit": "^2.2.1", "ts-loader": "^6.1.0", - "typescript": "^3.6.3", + "typescript": "3.7.2", "webpack": "^4.40.2" } } diff --git a/x-pack/legacy/plugins/apm/index.ts b/x-pack/legacy/plugins/apm/index.ts index 4655e5e6f92ea..bfbfb4bb99c6a 100644 --- a/x-pack/legacy/plugins/apm/index.ts +++ b/x-pack/legacy/plugins/apm/index.ts @@ -45,7 +45,10 @@ export const apm: LegacyPluginInitializer = kibana => { }, hacks: ['plugins/apm/hacks/toggle_app_link_in_nav'], savedObjectSchemas: { - 'apm-telemetry': { + 'apm-services-telemetry': { + isNamespaceAgnostic: true + }, + 'apm-indices': { isNamespaceAgnostic: true } }, diff --git a/x-pack/legacy/plugins/apm/mappings.json b/x-pack/legacy/plugins/apm/mappings.json index 0b31798242fad..02296606b1c01 100644 --- a/x-pack/legacy/plugins/apm/mappings.json +++ b/x-pack/legacy/plugins/apm/mappings.json @@ -1,5 +1,5 @@ { - "apm-telemetry": { + "apm-services-telemetry": { "properties": { "has_any_services": { "type": "boolean" diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx index 53f1893a168ac..69f0cf61af242 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx @@ -6,6 +6,7 @@ import { i18n } from '@kbn/i18n'; import React, { Component } from 'react'; +import { toMountPoint } from '../../../../../../../../../../src/plugins/kibana_react/public'; import { startMLJob } from '../../../../../services/rest/ml'; import { IUrlParams } from '../../../../../context/UrlParamsContext/types'; import { MLJobLink } from '../../../../shared/Links/MachineLearningLinks/MLJobLink'; @@ -71,7 +72,7 @@ export class MachineLearningFlyout extends Component { defaultMessage: 'Job creation failed' } ), - text: ( + text: toMountPoint(

{i18n.translate( 'xpack.apm.serviceDetails.enableAnomalyDetectionPanel.jobCreationFailedNotificationText', @@ -105,7 +106,7 @@ export class MachineLearningFlyout extends Component { defaultMessage: 'Job successfully created' } ), - text: ( + text: toMountPoint(

{i18n.translate( 'xpack.apm.serviceDetails.enableAnomalyDetectionPanel.jobCreatedNotificationText', diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx index 291208b2d9032..d52c869b95872 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx @@ -30,6 +30,7 @@ import { padLeft, range } from 'lodash'; import moment from 'moment-timezone'; import React, { Component } from 'react'; import styled from 'styled-components'; +import { toMountPoint } from '../../../../../../../../../src/plugins/kibana_react/public'; import { KibanaCoreContext } from '../../../../../../observability/public'; import { IUrlParams } from '../../../../context/UrlParamsContext/types'; import { KibanaLink } from '../../../shared/Links/KibanaLink'; @@ -219,7 +220,7 @@ export class WatcherFlyout extends Component< defaultMessage: 'Watch creation failed' } ), - text: ( + text: toMountPoint(

{i18n.translate( 'xpack.apm.serviceDetails.enableErrorReportsPanel.watchCreationFailedNotificationText', @@ -243,7 +244,7 @@ export class WatcherFlyout extends Component< defaultMessage: 'New watch created!' } ), - text: ( + text: toMountPoint(

{i18n.translate( 'xpack.apm.serviceDetails.enableErrorReportsPanel.watchCreatedNotificationText', diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMetrics/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMetrics/index.tsx index 276d309cbb3e3..8005fc17f2a20 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMetrics/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMetrics/index.tsx @@ -29,9 +29,7 @@ export function ServiceMetrics({ agentName }: ServiceMetricsProps) { const { data } = useServiceMetricCharts(urlParams, agentName); const { start, end } = urlParams; - const localFiltersConfig: React.ComponentProps< - typeof LocalUIFilters - > = useMemo( + const localFiltersConfig: React.ComponentProps = useMemo( () => ({ filterNames: ['host', 'containerId', 'podName'], params: { diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx index b69076b3a1f70..a118871a5e268 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx @@ -34,9 +34,7 @@ const ServiceNodeOverview = () => { const { uiFilters, urlParams } = useUrlParams(); const { serviceName, start, end } = urlParams; - const localFiltersConfig: React.ComponentProps< - typeof LocalUIFilters - > = useMemo( + const localFiltersConfig: React.ComponentProps = useMemo( () => ({ filterNames: ['host', 'containerId', 'podName'], params: { diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/index.tsx index d03e70fc99cc6..0702e092a714f 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/index.tsx @@ -9,6 +9,7 @@ import { EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useEffect, useMemo } from 'react'; import url from 'url'; +import { toMountPoint } from '../../../../../../../../src/plugins/kibana_react/public'; import { useFetcher } from '../../../hooks/useFetcher'; import { NoServicesMessage } from './NoServicesMessage'; import { ServiceList } from './ServiceList'; @@ -55,7 +56,7 @@ export function ServiceOverview() { defaultMessage: 'Legacy data was detected within the selected time range' }), - text: ( + text: toMountPoint(

{i18n.translate('xpack.apm.serviceOverview.toastText', { defaultMessage: @@ -84,9 +85,7 @@ export function ServiceOverview() { useTrackPageview({ app: 'apm', path: 'services_overview' }); useTrackPageview({ app: 'apm', path: 'services_overview', delay: 15000 }); - const localFiltersConfig: React.ComponentProps< - typeof LocalUIFilters - > = useMemo( + const localFiltersConfig: React.ComponentProps = useMemo( () => ({ filterNames: ['host', 'agentName'], projection: PROJECTION.SERVICES diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx index db0ddb56e7088..fc86f4bb78afb 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx @@ -195,10 +195,9 @@ export const TransactionDistribution: FunctionComponent = ( } backgroundHover={(bucket: IChartPoint) => bucket.y > 0 && bucket.sample} tooltipHeader={(bucket: IChartPoint) => - `${timeFormatter(bucket.x0, { withUnit: false })} - ${timeFormatter( - bucket.x, - { withUnit: false } - )} ${unit}` + `${timeFormatter(bucket.x0, { + withUnit: false + })} - ${timeFormatter(bucket.x, { withUnit: false })} ${unit}` } tooltipFooter={(bucket: IChartPoint) => !bucket.sample && diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx index d81b7417570a5..f016052df56a2 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx @@ -94,9 +94,7 @@ export function TransactionOverview() { } }, [http, serviceName, transactionType]); - const localFiltersConfig: React.ComponentProps< - typeof LocalUIFilters - > = useMemo( + const localFiltersConfig: React.ComponentProps = useMemo( () => ({ filterNames: ['transactionResult', 'host', 'containerId', 'podName'], params: { diff --git a/x-pack/legacy/plugins/apm/public/components/shared/KueryBar/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/KueryBar/index.tsx index 7c0b6f24f87a7..9918f162a01f4 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/KueryBar/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/KueryBar/index.tsx @@ -103,12 +103,14 @@ export function KueryBar() { const boolFilter = getBoolFilter(urlParams); try { - const suggestions = (await getSuggestions( - inputValue, - selectionStart, - indexPattern, - boolFilter - )) + const suggestions = ( + await getSuggestions( + inputValue, + selectionStart, + indexPattern, + boolFilter + ) + ) .filter(suggestion => !startsWith(suggestion.text, 'span.')) .slice(0, 15); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/Section.tsx b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/Section.tsx index 6f67b2458ea10..0aaeae3e4ce44 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/Section.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/Section.tsx @@ -5,17 +5,18 @@ */ import React from 'react'; +import { isEmpty } from 'lodash'; import { i18n } from '@kbn/i18n'; import { EuiText } from '@elastic/eui'; import { KeyValueTable } from '../KeyValueTable'; import { KeyValuePair } from '../../../utils/flattenObject'; interface Props { - keyValuePairs?: KeyValuePair[]; + keyValuePairs: KeyValuePair[]; } export function Section({ keyValuePairs }: Props) { - if (keyValuePairs) { + if (!isEmpty(keyValuePairs)) { return ; } return ( diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/MetadataTable.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/MetadataTable.test.tsx index bdf895f423913..4398c129aa7b8 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/MetadataTable.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/MetadataTable.test.tsx @@ -21,7 +21,10 @@ describe('MetadataTable', () => { label: 'Bar', required: false, properties: ['props.A', 'props.B'], - rows: [{ key: 'props.A', value: 'A' }, { key: 'props.B', value: 'B' }] + rows: [ + { key: 'props.A', value: 'A' }, + { key: 'props.B', value: 'B' } + ] } ] as unknown) as SectionsWithRows; const output = render(); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/Section.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/Section.test.tsx index 7e68b2f84eead..4378c7fdeee0c 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/Section.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/Section.test.tsx @@ -11,7 +11,7 @@ import { expectTextsInDocument } from '../../../../utils/testHelpers'; describe('Section', () => { it('shows "empty state message" if no data is available', () => { - const output = render(

); - expectTextsInDocument(output, ['No data available']); + const component = render(
); + expectTextsInDocument(component, ['No data available']); }); }); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/index.tsx index ca14be237d22b..b7963b5c75a92 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/index.tsx @@ -78,29 +78,26 @@ interface StackframesGroup { } export function getGroupedStackframes(stackframes: IStackframe[]) { - return stackframes.reduce( - (acc, stackframe) => { - const prevGroup = last(acc); - const shouldAppend = - prevGroup && - prevGroup.isLibraryFrame === stackframe.library_frame && - !prevGroup.excludeFromGrouping && - !stackframe.exclude_from_grouping; + return stackframes.reduce((acc, stackframe) => { + const prevGroup = last(acc); + const shouldAppend = + prevGroup && + prevGroup.isLibraryFrame === stackframe.library_frame && + !prevGroup.excludeFromGrouping && + !stackframe.exclude_from_grouping; - // append to group - if (shouldAppend) { - prevGroup.stackframes.push(stackframe); - return acc; - } - - // create new group - acc.push({ - isLibraryFrame: Boolean(stackframe.library_frame), - excludeFromGrouping: Boolean(stackframe.exclude_from_grouping), - stackframes: [stackframe] - }); + // append to group + if (shouldAppend) { + prevGroup.stackframes.push(stackframe); return acc; - }, - [] as StackframesGroup[] - ); + } + + // create new group + acc.push({ + isLibraryFrame: Boolean(stackframe.library_frame), + excludeFromGrouping: Boolean(stackframe.exclude_from_grouping), + stackframes: [stackframe] + }); + return acc; + }, [] as StackframesGroup[]); } diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/VoronoiPlot.js b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/VoronoiPlot.js index 52afdffcb0839..d765a57a56a18 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/VoronoiPlot.js +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/VoronoiPlot.js @@ -32,7 +32,10 @@ class VoronoiPlot extends PureComponent { onMouseLeave={this.props.onMouseLeave} > { formatYShort={t => `${asDecimal(t)} occ.`} formatYLong={t => `${asDecimal(t)} occurrences`} tooltipHeader={bucket => - `${timeFormatter(bucket.x0, { withUnit: false })} - ${timeFormatter( - bucket.x, - { withUnit: false } - )} ${unit}` + `${timeFormatter(bucket.x0, { + withUnit: false + })} - ${timeFormatter(bucket.x, { withUnit: false })} ${unit}` } width={800} /> diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Histogram/index.js b/x-pack/legacy/plugins/apm/public/components/shared/charts/Histogram/index.js index 7b9586634c7d0..50c94fe88e6ad 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/Histogram/index.js +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/Histogram/index.js @@ -209,7 +209,10 @@ export class HistogramInner extends PureComponent { )} { return { ...bucket, diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/ChoroplethToolTip.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/ChoroplethToolTip.tsx index 12872fd64a3c4..adcce161c7ac1 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/ChoroplethToolTip.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/ChoroplethToolTip.tsx @@ -19,7 +19,7 @@ export const ChoroplethToolTip: React.SFC<{
{name}
{i18n.translate( - 'xpack.apm.metrics.pageLoadCharts.RegionMapChart.ToolTip.avgPageLoadDuration', + 'xpack.apm.metrics.durationByCountryMap.RegionMapChart.ToolTip.avgPageLoadDuration', { defaultMessage: 'Avg. page load duration:' } @@ -31,7 +31,7 @@ export const ChoroplethToolTip: React.SFC<{
( {i18n.translate( - 'xpack.apm.metrics.pageLoadCharts.RegionMapChart.ToolTip.countPageLoads', + 'xpack.apm.metrics.durationByCountryMap.RegionMapChart.ToolTip.countPageLoads', { values: { docCount: asInteger(docCount) }, defaultMessage: '{docCount} page loads' diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/PageLoadCharts/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/DurationByCountryMap/index.tsx similarity index 88% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/PageLoadCharts/index.tsx rename to x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/DurationByCountryMap/index.tsx index 40c6150149eb5..6176397170797 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/PageLoadCharts/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/DurationByCountryMap/index.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { useAvgDurationByCountry } from '../../../../../hooks/useAvgDurationByCountry'; import { ChoroplethMap } from '../ChoroplethMap'; -export const PageLoadCharts: React.SFC = () => { +export const DurationByCountryMap: React.SFC = () => { const { data } = useAvgDurationByCountry(); return ( @@ -20,7 +20,7 @@ export const PageLoadCharts: React.SFC = () => { {i18n.translate( - 'xpack.apm.metrics.pageLoadCharts.avgPageLoadByCountryLabel', + 'xpack.apm.metrics.durationByCountryMap.avgPageLoadByCountryLabel', { defaultMessage: 'Avg. page load duration distribution by country' diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx index 132067a6c32b7..94f30a8a2325a 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx @@ -34,7 +34,12 @@ import { LicenseContext } from '../../../../context/LicenseContext'; import { TransactionLineChart } from './TransactionLineChart'; import { isValidCoordinateValue } from '../../../../utils/isValidCoordinateValue'; import { getTimeFormatter } from '../../../../utils/formatters'; -import { PageLoadCharts } from './PageLoadCharts'; +import { DurationByCountryMap } from './DurationByCountryMap'; +import { + TRANSACTION_PAGE_LOAD, + TRANSACTION_ROUTE_CHANGE, + TRANSACTION_REQUEST +} from '../../../../../common/transaction_types'; interface TransactionChartProps { hasMLJob: boolean; @@ -55,8 +60,6 @@ const ShiftedEuiText = styled(EuiText)` top: 5px; `; -const RUM_PAGE_LOAD_TYPE = 'page-load'; - export class TransactionCharts extends Component { public getMaxY = (responseTimeSeries: TimeSeries[]) => { const coordinates = flatten( @@ -200,19 +203,19 @@ export class TransactionCharts extends Component { - {transactionType === RUM_PAGE_LOAD_TYPE ? ( + {transactionType === TRANSACTION_PAGE_LOAD && ( <> - + - ) : null} + )} ); } } function tpmLabel(type?: string) { - return type === 'request' + return type === TRANSACTION_REQUEST ? i18n.translate( 'xpack.apm.metrics.transactionChart.requestsPerMinuteLabel', { @@ -229,14 +232,14 @@ function tpmLabel(type?: string) { function responseTimeLabel(type?: string) { switch (type) { - case RUM_PAGE_LOAD_TYPE: + case TRANSACTION_PAGE_LOAD: return i18n.translate( 'xpack.apm.metrics.transactionChart.pageLoadTimesLabel', { defaultMessage: 'Page load times' } ); - case 'route-change': + case TRANSACTION_ROUTE_CHANGE: return i18n.translate( 'xpack.apm.metrics.transactionChart.routeChangeTimesLabel', { diff --git a/x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByCountry.ts b/x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByCountry.ts index b794332d4aa63..6b3fa1f0d98f7 100644 --- a/x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByCountry.ts +++ b/x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByCountry.ts @@ -9,7 +9,7 @@ import { useUrlParams } from './useUrlParams'; export function useAvgDurationByCountry() { const { - urlParams: { serviceName, start, end }, + urlParams: { serviceName, start, end, transactionName }, uiFilters } = useUrlParams(); @@ -24,13 +24,14 @@ export function useAvgDurationByCountry() { query: { start, end, - uiFilters: JSON.stringify(uiFilters) + uiFilters: JSON.stringify(uiFilters), + transactionName } } }); } }, - [serviceName, start, end, uiFilters] + [serviceName, start, end, uiFilters, transactionName] ); return { diff --git a/x-pack/legacy/plugins/apm/public/hooks/useFetcher.tsx b/x-pack/legacy/plugins/apm/public/hooks/useFetcher.tsx index ba74b0175ff71..bc6382841be3f 100644 --- a/x-pack/legacy/plugins/apm/public/hooks/useFetcher.tsx +++ b/x-pack/legacy/plugins/apm/public/hooks/useFetcher.tsx @@ -8,6 +8,7 @@ import React, { useContext, useEffect, useState, useMemo } from 'react'; import { idx } from '@kbn/elastic-idx'; import { i18n } from '@kbn/i18n'; import { IHttpFetchError } from 'src/core/public'; +import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public'; import { LoadingIndicatorContext } from '../context/LoadingIndicatorContext'; import { useComponentId } from './useComponentId'; import { useKibanaCore } from '../../../observability/public'; @@ -92,7 +93,7 @@ export function useFetcher( title: i18n.translate('xpack.apm.fetcher.error.title', { defaultMessage: `Error while fetching resource` }), - text: ( + text: toMountPoint(
{i18n.translate('xpack.apm.fetcher.error.status', { diff --git a/x-pack/legacy/plugins/apm/public/selectors/__tests__/chartSelectors.test.ts b/x-pack/legacy/plugins/apm/public/selectors/__tests__/chartSelectors.test.ts index 80a1b96efb3d6..2b0263f69db8f 100644 --- a/x-pack/legacy/plugins/apm/public/selectors/__tests__/chartSelectors.test.ts +++ b/x-pack/legacy/plugins/apm/public/selectors/__tests__/chartSelectors.test.ts @@ -35,9 +35,18 @@ describe('chartSelectors', () => { describe('getResponseTimeSeries', () => { const apmTimeseries = { responseTimes: { - avg: [{ x: 0, y: 100 }, { x: 1000, y: 200 }], - p95: [{ x: 0, y: 200 }, { x: 1000, y: 300 }], - p99: [{ x: 0, y: 300 }, { x: 1000, y: 400 }] + avg: [ + { x: 0, y: 100 }, + { x: 1000, y: 200 } + ], + p95: [ + { x: 0, y: 200 }, + { x: 1000, y: 300 } + ], + p99: [ + { x: 0, y: 300 }, + { x: 1000, y: 400 } + ] }, tpmBuckets: [], overallAvgDuration: 200 @@ -49,21 +58,30 @@ describe('chartSelectors', () => { ).toEqual([ { color: '#3185fc', - data: [{ x: 0, y: 100 }, { x: 1000, y: 200 }], + data: [ + { x: 0, y: 100 }, + { x: 1000, y: 200 } + ], legendValue: '0 ms', title: 'Avg.', type: 'linemark' }, { color: '#e6c220', - data: [{ x: 0, y: 200 }, { x: 1000, y: 300 }], + data: [ + { x: 0, y: 200 }, + { x: 1000, y: 300 } + ], title: '95th percentile', titleShort: '95th', type: 'linemark' }, { color: '#f98510', - data: [{ x: 0, y: 300 }, { x: 1000, y: 400 }], + data: [ + { x: 0, y: 300 }, + { x: 1000, y: 400 } + ], title: '99th percentile', titleShort: '99th', type: 'linemark' @@ -87,7 +105,13 @@ describe('chartSelectors', () => { p99: [] }, tpmBuckets: [ - { key: 'HTTP 2xx', dataPoints: [{ x: 0, y: 5 }, { x: 0, y: 2 }] }, + { + key: 'HTTP 2xx', + dataPoints: [ + { x: 0, y: 5 }, + { x: 0, y: 2 } + ] + }, { key: 'HTTP 4xx', dataPoints: [{ x: 0, y: 1 }] }, { key: 'HTTP 5xx', dataPoints: [{ x: 0, y: 0 }] } ], @@ -99,7 +123,10 @@ describe('chartSelectors', () => { expect(getTpmSeries(apmTimeseries, transactionType)).toEqual([ { color: successColor, - data: [{ x: 0, y: 5 }, { x: 0, y: 2 }], + data: [ + { x: 0, y: 5 }, + { x: 0, y: 2 } + ], legendValue: '3.5 tpm', title: 'HTTP 2xx', type: 'linemark' @@ -220,9 +247,18 @@ describe('chartSelectors', () => { describe('when empty', () => { it('produces an empty series', () => { const responseTimes = { - avg: [{ x: 0, y: 1 }, { x: 100, y: 1 }], - p95: [{ x: 0, y: 1 }, { x: 100, y: 1 }], - p99: [{ x: 0, y: 1 }, { x: 100, y: 1 }] + avg: [ + { x: 0, y: 1 }, + { x: 100, y: 1 } + ], + p95: [ + { x: 0, y: 1 }, + { x: 100, y: 1 } + ], + p99: [ + { x: 0, y: 1 }, + { x: 100, y: 1 } + ] }; const series = getTpmSeries( { ...apmTimeseries, responseTimes, tpmBuckets: [] }, diff --git a/x-pack/legacy/plugins/apm/server/lib/apm_telemetry/__test__/apm_telemetry.test.ts b/x-pack/legacy/plugins/apm/server/lib/apm_telemetry/__test__/index.test.ts similarity index 83% rename from x-pack/legacy/plugins/apm/server/lib/apm_telemetry/__test__/apm_telemetry.test.ts rename to x-pack/legacy/plugins/apm/server/lib/apm_telemetry/__test__/index.test.ts index 6db6e8848ef07..26cae303542a4 100644 --- a/x-pack/legacy/plugins/apm/server/lib/apm_telemetry/__test__/apm_telemetry.test.ts +++ b/x-pack/legacy/plugins/apm/server/lib/apm_telemetry/__test__/index.test.ts @@ -5,11 +5,11 @@ */ import { SavedObjectAttributes } from 'src/core/server'; +import { createApmTelementry, storeApmServicesTelemetry } from '../index'; import { - APM_TELEMETRY_DOC_ID, - createApmTelementry, - storeApmTelemetry -} from '../apm_telemetry'; + APM_SERVICES_TELEMETRY_SAVED_OBJECT_TYPE, + APM_SERVICES_TELEMETRY_SAVED_OBJECT_ID +} from '../../../../common/apm_saved_object_constants'; describe('apm_telemetry', () => { describe('createApmTelementry', () => { @@ -44,7 +44,7 @@ describe('apm_telemetry', () => { }); }); - describe('storeApmTelemetry', () => { + describe('storeApmServicesTelemetry', () => { let server: any; let apmTelemetry: SavedObjectAttributes; let savedObjectsClientInstance: any; @@ -75,24 +75,24 @@ describe('apm_telemetry', () => { }); it('should call savedObjectsClient create with the given ApmTelemetry object', () => { - storeApmTelemetry(server, apmTelemetry); + storeApmServicesTelemetry(server, apmTelemetry); expect(savedObjectsClientInstance.create.mock.calls[0][1]).toBe( apmTelemetry ); }); it('should call savedObjectsClient create with the apm-telemetry document type and ID', () => { - storeApmTelemetry(server, apmTelemetry); + storeApmServicesTelemetry(server, apmTelemetry); expect(savedObjectsClientInstance.create.mock.calls[0][0]).toBe( - 'apm-telemetry' + APM_SERVICES_TELEMETRY_SAVED_OBJECT_TYPE ); expect(savedObjectsClientInstance.create.mock.calls[0][2].id).toBe( - APM_TELEMETRY_DOC_ID + APM_SERVICES_TELEMETRY_SAVED_OBJECT_ID ); }); it('should call savedObjectsClient create with overwrite: true', () => { - storeApmTelemetry(server, apmTelemetry); + storeApmServicesTelemetry(server, apmTelemetry); expect(savedObjectsClientInstance.create.mock.calls[0][2].overwrite).toBe( true ); diff --git a/x-pack/legacy/plugins/apm/server/lib/apm_telemetry/apm_telemetry.ts b/x-pack/legacy/plugins/apm/server/lib/apm_telemetry/apm_telemetry.ts deleted file mode 100644 index 54106cce10bac..0000000000000 --- a/x-pack/legacy/plugins/apm/server/lib/apm_telemetry/apm_telemetry.ts +++ /dev/null @@ -1,39 +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 { Server } from 'hapi'; -import { countBy } from 'lodash'; -import { SavedObjectAttributes } from 'src/core/server'; -import { isAgentName } from '../../../common/agent_name'; -import { getSavedObjectsClient } from '../helpers/saved_objects_client'; - -export const APM_TELEMETRY_DOC_ID = 'apm-telemetry'; - -export function createApmTelementry( - agentNames: string[] = [] -): SavedObjectAttributes { - const validAgentNames = agentNames.filter(isAgentName); - return { - has_any_services: validAgentNames.length > 0, - services_per_agent: countBy(validAgentNames) - }; -} - -export async function storeApmTelemetry( - server: Server, - apmTelemetry: SavedObjectAttributes -) { - try { - const savedObjectsClient = getSavedObjectsClient(server); - await savedObjectsClient.create('apm-telemetry', apmTelemetry, { - id: APM_TELEMETRY_DOC_ID, - overwrite: true - }); - } catch (e) { - // eslint-disable-next-line no-console - console.error('Could not send APM telemetry:', e.message); - } -} diff --git a/x-pack/legacy/plugins/apm/server/lib/apm_telemetry/index.ts b/x-pack/legacy/plugins/apm/server/lib/apm_telemetry/index.ts index 754666b0a9fa2..640072d6ec4d8 100644 --- a/x-pack/legacy/plugins/apm/server/lib/apm_telemetry/index.ts +++ b/x-pack/legacy/plugins/apm/server/lib/apm_telemetry/index.ts @@ -4,9 +4,77 @@ * you may not use this file except in compliance with the Elastic License. */ -export { - storeApmTelemetry, - createApmTelementry, - APM_TELEMETRY_DOC_ID -} from './apm_telemetry'; -export { makeApmUsageCollector } from './make_apm_usage_collector'; +import { Server } from 'hapi'; +import { countBy } from 'lodash'; +import { SavedObjectAttributes } from 'src/core/server'; +import { CoreSetup } from 'src/core/server'; +import { isAgentName } from '../../../common/agent_name'; +import { getInternalSavedObjectsClient } from '../helpers/saved_objects_client'; +import { + APM_SERVICES_TELEMETRY_SAVED_OBJECT_TYPE, + APM_SERVICES_TELEMETRY_SAVED_OBJECT_ID +} from '../../../common/apm_saved_object_constants'; +import { LegacySetup } from '../../new-platform/plugin'; + +export function createApmTelementry( + agentNames: string[] = [] +): SavedObjectAttributes { + const validAgentNames = agentNames.filter(isAgentName); + return { + has_any_services: validAgentNames.length > 0, + services_per_agent: countBy(validAgentNames) + }; +} + +export async function storeApmServicesTelemetry( + server: Server, + apmTelemetry: SavedObjectAttributes +) { + try { + const internalSavedObjectsClient = getInternalSavedObjectsClient(server); + await internalSavedObjectsClient.create( + APM_SERVICES_TELEMETRY_SAVED_OBJECT_TYPE, + apmTelemetry, + { + id: APM_SERVICES_TELEMETRY_SAVED_OBJECT_ID, + overwrite: true + } + ); + } catch (e) { + server.log(['error'], `Unable to save APM telemetry data: ${e.message}`); + } +} + +interface LegacySetupWithUsageCollector extends LegacySetup { + server: LegacySetup['server'] & { + usage: { + collectorSet: { + makeUsageCollector: (options: unknown) => unknown; + register: (options: unknown) => unknown; + }; + }; + }; +} + +export function makeApmUsageCollector( + core: CoreSetup, + { server }: LegacySetupWithUsageCollector +) { + const apmUsageCollector = server.usage.collectorSet.makeUsageCollector({ + type: 'apm', + fetch: async () => { + const internalSavedObjectsClient = getInternalSavedObjectsClient(server); + try { + const apmTelemetrySavedObject = await internalSavedObjectsClient.get( + APM_SERVICES_TELEMETRY_SAVED_OBJECT_TYPE, + APM_SERVICES_TELEMETRY_SAVED_OBJECT_ID + ); + return apmTelemetrySavedObject.attributes; + } catch (err) { + return createApmTelementry(); + } + }, + isReady: () => true + }); + server.usage.collectorSet.register(apmUsageCollector); +} diff --git a/x-pack/legacy/plugins/apm/server/lib/apm_telemetry/make_apm_usage_collector.ts b/x-pack/legacy/plugins/apm/server/lib/apm_telemetry/make_apm_usage_collector.ts deleted file mode 100644 index 886c3890f1a9a..0000000000000 --- a/x-pack/legacy/plugins/apm/server/lib/apm_telemetry/make_apm_usage_collector.ts +++ /dev/null @@ -1,44 +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 } from 'src/core/server'; -import { getSavedObjectsClient } from '../helpers/saved_objects_client'; -import { APM_TELEMETRY_DOC_ID, createApmTelementry } from './apm_telemetry'; -import { LegacySetup } from '../../new-platform/plugin'; - -export interface LegacySetupWithUsageCollector extends LegacySetup { - server: LegacySetup['server'] & { - usage: { - collectorSet: { - makeUsageCollector: (options: unknown) => unknown; - register: (options: unknown) => unknown; - }; - }; - }; -} - -export function makeApmUsageCollector( - core: CoreSetup, - { server }: LegacySetupWithUsageCollector -) { - const apmUsageCollector = server.usage.collectorSet.makeUsageCollector({ - type: 'apm', - fetch: async () => { - const savedObjectsClient = getSavedObjectsClient(server); - try { - const apmTelemetrySavedObject = await savedObjectsClient.get( - 'apm-telemetry', - APM_TELEMETRY_DOC_ID - ); - return apmTelemetrySavedObject.attributes; - } catch (err) { - return createApmTelementry(); - } - }, - isReady: () => true - }); - server.usage.collectorSet.register(apmUsageCollector); -} diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/es_client.ts b/x-pack/legacy/plugins/apm/server/lib/helpers/es_client.ts index f38184fe460b1..9c111910f16f9 100644 --- a/x-pack/legacy/plugins/apm/server/lib/helpers/es_client.ts +++ b/x-pack/legacy/plugins/apm/server/lib/helpers/es_client.ts @@ -66,8 +66,12 @@ async function getParamsForSearchRequest( apmOptions?: APMOptions ) { const uiSettings = req.getUiSettingsService(); + const { server } = req; const [indices, includeFrozen] = await Promise.all([ - getApmIndices(req.server), + getApmIndices({ + config: server.config(), + savedObjectsClient: server.savedObjects.getScopedSavedObjectsClient(req) + }), uiSettings.get('search:includeFrozen') ]); diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/saved_objects_client.test.ts b/x-pack/legacy/plugins/apm/server/lib/helpers/saved_objects_client.test.ts index 3af1d8c706f46..c685ffdd801dc 100644 --- a/x-pack/legacy/plugins/apm/server/lib/helpers/saved_objects_client.test.ts +++ b/x-pack/legacy/plugins/apm/server/lib/helpers/saved_objects_client.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getSavedObjectsClient } from './saved_objects_client'; +import { getInternalSavedObjectsClient } from './saved_objects_client'; describe('saved_objects/client', () => { describe('getSavedObjectsClient', () => { @@ -31,7 +31,7 @@ describe('saved_objects/client', () => { }); it('should use internal user "admin"', () => { - getSavedObjectsClient(server); + getInternalSavedObjectsClient(server); expect(server.plugins.elasticsearch.getCluster).toHaveBeenCalledWith( 'admin' @@ -39,7 +39,7 @@ describe('saved_objects/client', () => { }); it('should call getSavedObjectsRepository with a cluster using the internal user context', () => { - getSavedObjectsClient(server); + getInternalSavedObjectsClient(server); expect( server.savedObjects.getSavedObjectsRepository @@ -47,9 +47,9 @@ describe('saved_objects/client', () => { }); it('should return a SavedObjectsClient initialized with the saved objects internal repository', () => { - const result = getSavedObjectsClient(server); + const internalSavedObjectsClient = getInternalSavedObjectsClient(server); - expect(result).toBe(savedObjectsClientInstance); + expect(internalSavedObjectsClient).toBe(savedObjectsClientInstance); expect(server.savedObjects.SavedObjectsClient).toHaveBeenCalledWith( internalRepository ); diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/saved_objects_client.ts b/x-pack/legacy/plugins/apm/server/lib/helpers/saved_objects_client.ts index 81dd8b34c8847..f164ca39d51c0 100644 --- a/x-pack/legacy/plugins/apm/server/lib/helpers/saved_objects_client.ts +++ b/x-pack/legacy/plugins/apm/server/lib/helpers/saved_objects_client.ts @@ -6,7 +6,10 @@ import { Server } from 'hapi'; -export function getSavedObjectsClient(server: Server, clusterName = 'admin') { +export function getInternalSavedObjectsClient( + server: Server, + clusterName = 'admin' +) { const { SavedObjectsClient, getSavedObjectsRepository } = server.savedObjects; const { callWithInternalUser } = server.plugins.elasticsearch.getCluster( clusterName diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/setup_request.test.ts b/x-pack/legacy/plugins/apm/server/lib/helpers/setup_request.test.ts index 6ebf7a896591f..f43b9ea11487a 100644 --- a/x-pack/legacy/plugins/apm/server/lib/helpers/setup_request.test.ts +++ b/x-pack/legacy/plugins/apm/server/lib/helpers/setup_request.test.ts @@ -34,6 +34,9 @@ function getMockRequest() { callWithInternalUser: callWithInternalUserSpy }) } + }, + savedObjects: { + getScopedSavedObjectsClient: () => ({ get: async () => false }) } }, getUiSettingsService: () => ({ get: async () => false }) diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/setup_request.ts b/x-pack/legacy/plugins/apm/server/lib/helpers/setup_request.ts index 850de4939d599..ab0f47eb04d62 100644 --- a/x-pack/legacy/plugins/apm/server/lib/helpers/setup_request.ts +++ b/x-pack/legacy/plugins/apm/server/lib/helpers/setup_request.ts @@ -7,10 +7,15 @@ import { Legacy } from 'kibana'; import { Server } from 'hapi'; import moment from 'moment'; +import { KibanaConfig } from 'src/legacy/server/kbn_server'; import { getESClient } from './es_client'; import { getUiFiltersES } from './convert_ui_filters/get_ui_filters_es'; -import { PromiseReturnType } from '../../../typings/common'; -import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; +import { + getApmIndices, + ApmIndicesConfig +} from '../settings/apm_indices/get_apm_indices'; +import { ESFilter } from '../../../typings/elasticsearch'; +import { ESClient } from './es_client'; function decodeUiFilters(server: Server, uiFiltersEncoded?: string) { if (!uiFiltersEncoded) { @@ -26,15 +31,29 @@ export interface APMRequestQuery { end?: string; uiFilters?: string; } +// Explicitly type Setup to prevent TS initialization errors +// https://github.com/microsoft/TypeScript/issues/34933 -export type Setup = PromiseReturnType; -export async function setupRequest(req: Legacy.Request) { +export interface Setup { + start: number; + end: number; + uiFiltersES: ESFilter[]; + client: ESClient; + internalClient: ESClient; + config: KibanaConfig; + indices: ApmIndicesConfig; +} + +export async function setupRequest(req: Legacy.Request): Promise { const query = (req.query as unknown) as APMRequestQuery; const { server } = req; + const savedObjectsClient = server.savedObjects.getScopedSavedObjectsClient( + req + ); const config = server.config(); const [uiFiltersES, indices] = await Promise.all([ decodeUiFilters(server, query.uiFilters), - getApmIndices(server) + getApmIndices({ config, savedObjectsClient }) ]); return { diff --git a/x-pack/legacy/plugins/apm/server/lib/index_pattern/index.ts b/x-pack/legacy/plugins/apm/server/lib/index_pattern/index.ts index 0b9407b288b1d..1aff1d772c5c2 100644 --- a/x-pack/legacy/plugins/apm/server/lib/index_pattern/index.ts +++ b/x-pack/legacy/plugins/apm/server/lib/index_pattern/index.ts @@ -4,18 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ import { Server } from 'hapi'; -import { getSavedObjectsClient } from '../helpers/saved_objects_client'; +import { getInternalSavedObjectsClient } from '../helpers/saved_objects_client'; import apmIndexPattern from '../../../../../../../src/legacy/core_plugins/kibana/server/tutorials/apm/index_pattern.json'; export async function getAPMIndexPattern(server: Server) { const config = server.config(); const apmIndexPatternTitle = config.get('apm_oss.indexPattern'); - const savedObjectsClient = getSavedObjectsClient(server); + const internalSavedObjectsClient = getInternalSavedObjectsClient(server); try { - return await savedObjectsClient.get('index-pattern', apmIndexPattern.id); + return await internalSavedObjectsClient.get( + 'index-pattern', + apmIndexPattern.id + ); } catch (error) { // if GET fails, then create a new index pattern saved object - return await savedObjectsClient.create( + return await internalSavedObjectsClient.create( 'index-pattern', { ...apmIndexPattern.attributes, diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/transform_metrics_chart.ts b/x-pack/legacy/plugins/apm/server/lib/metrics/transform_metrics_chart.ts index 594a0d35ed176..3764f18a6d692 100644 --- a/x-pack/legacy/plugins/apm/server/lib/metrics/transform_metrics_chart.ts +++ b/x-pack/legacy/plugins/apm/server/lib/metrics/transform_metrics_chart.ts @@ -48,9 +48,10 @@ type GenericMetricsRequest = Overwrite< } >; -export function transformDataToMetricsChart< - TRequest extends GenericMetricsRequest ->(result: ESSearchResponse, chartBase: ChartBase) { +export function transformDataToMetricsChart( + result: ESSearchResponse, + chartBase: ChartBase +) { const { aggregations, hits } = result; const timeseriesData = idx(aggregations, _ => _.timeseriesData); diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/create_agent_config_index.ts b/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/create_agent_config_index.ts index 18f6aea610a68..434eda8c0f46e 100644 --- a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/create_agent_config_index.ts +++ b/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/create_agent_config_index.ts @@ -8,13 +8,19 @@ import { CoreSetup } from 'src/core/server'; import { CallCluster } from '../../../../../../../../src/legacy/core_plugins/elasticsearch'; import { getApmIndices } from '../apm_indices/get_apm_indices'; import { LegacySetup } from '../../../new-platform/plugin'; +import { getInternalSavedObjectsClient } from '../../helpers/saved_objects_client'; export async function createApmAgentConfigurationIndex( core: CoreSetup, { server }: LegacySetup ) { try { - const indices = await getApmIndices(server); + const config = server.config(); + const internalSavedObjectsClient = getInternalSavedObjectsClient(server); + const indices = await getApmIndices({ + savedObjectsClient: internalSavedObjectsClient, + config + }); const index = indices['apm_oss.apmAgentConfigurationIndex']; const { callWithInternalUser } = server.plugins.elasticsearch.getCluster( 'admin' diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts b/x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts index cd237a5264099..e942a26da373e 100644 --- a/x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts +++ b/x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts @@ -4,12 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Server } from 'hapi'; import { merge } from 'lodash'; import { KibanaConfig } from 'src/legacy/server/kbn_server'; -import { getSavedObjectsClient } from '../../helpers/saved_objects_client'; -import { Setup } from '../../helpers/setup_request'; +import { Server } from 'hapi'; import { PromiseReturnType } from '../../../../typings/common'; +import { + APM_INDICES_SAVED_OBJECT_TYPE, + APM_INDICES_SAVED_OBJECT_ID +} from '../../../../common/apm_saved_object_constants'; export interface ApmIndicesConfig { 'apm_oss.sourcemapIndices': string; @@ -23,11 +25,13 @@ export interface ApmIndicesConfig { export type ApmIndicesName = keyof ApmIndicesConfig; -export const APM_INDICES_SAVED_OBJECT_TYPE = 'apm-indices'; -export const APM_INDICES_SAVED_OBJECT_ID = 'apm-indices'; +export type ScopedSavedObjectsClient = ReturnType< + Server['savedObjects']['getScopedSavedObjectsClient'] +>; -async function getApmIndicesSavedObject(server: Server) { - const savedObjectsClient = getSavedObjectsClient(server, 'data'); +async function getApmIndicesSavedObject( + savedObjectsClient: ScopedSavedObjectsClient +) { const apmIndices = await savedObjectsClient.get>( APM_INDICES_SAVED_OBJECT_TYPE, APM_INDICES_SAVED_OBJECT_ID @@ -53,13 +57,21 @@ function getApmIndicesConfig(config: KibanaConfig): ApmIndicesConfig { }; } -export async function getApmIndices(server: Server) { +export async function getApmIndices({ + savedObjectsClient, + config +}: { + savedObjectsClient: ScopedSavedObjectsClient; + config: KibanaConfig; +}) { try { - const apmIndicesSavedObject = await getApmIndicesSavedObject(server); - const apmIndicesConfig = getApmIndicesConfig(server.config()); + const apmIndicesSavedObject = await getApmIndicesSavedObject( + savedObjectsClient + ); + const apmIndicesConfig = getApmIndicesConfig(config); return merge({}, apmIndicesConfig, apmIndicesSavedObject); } catch (error) { - return getApmIndicesConfig(server.config()); + return getApmIndicesConfig(config); } } @@ -74,16 +86,15 @@ const APM_UI_INDICES: ApmIndicesName[] = [ ]; export async function getApmIndexSettings({ - setup, - server + config, + savedObjectsClient }: { - setup: Setup; - server: Server; + config: KibanaConfig; + savedObjectsClient: ScopedSavedObjectsClient; }) { - const { config } = setup; let apmIndicesSavedObject: PromiseReturnType; try { - apmIndicesSavedObject = await getApmIndicesSavedObject(server); + apmIndicesSavedObject = await getApmIndicesSavedObject(savedObjectsClient); } catch (error) { if (error.output && error.output.statusCode === 404) { apmIndicesSavedObject = {}; diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/save_apm_indices.ts b/x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/save_apm_indices.ts index 8de47c5c44144..e57e64942ab89 100644 --- a/x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/save_apm_indices.ts +++ b/x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/save_apm_indices.ts @@ -4,19 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Server } from 'hapi'; -import { getSavedObjectsClient } from '../../helpers/saved_objects_client'; +import { ApmIndicesConfig, ScopedSavedObjectsClient } from './get_apm_indices'; import { - ApmIndicesConfig, APM_INDICES_SAVED_OBJECT_TYPE, APM_INDICES_SAVED_OBJECT_ID -} from './get_apm_indices'; +} from '../../../../common/apm_saved_object_constants'; export async function saveApmIndices( - server: Server, + savedObjectsClient: ScopedSavedObjectsClient, apmIndicesSavedObject: Partial ) { - const savedObjectsClient = getSavedObjectsClient(server, 'data'); return await savedObjectsClient.create( APM_INDICES_SAVED_OBJECT_TYPE, apmIndicesSavedObject, diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_country/index.ts b/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_country/index.ts index e092942a25ba6..ed6bdf203f2d4 100644 --- a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_country/index.ts +++ b/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_country/index.ts @@ -9,19 +9,26 @@ import { PROCESSOR_EVENT, SERVICE_NAME, TRANSACTION_DURATION, - TRANSACTION_TYPE + TRANSACTION_TYPE, + TRANSACTION_NAME } from '../../../../common/elasticsearch_fieldnames'; import { Setup } from '../../helpers/setup_request'; import { rangeFilter } from '../../helpers/range_filter'; +import { TRANSACTION_PAGE_LOAD } from '../../../../common/transaction_types'; export async function getTransactionAvgDurationByCountry({ setup, - serviceName + serviceName, + transactionName }: { setup: Setup; serviceName: string; + transactionName?: string; }) { const { uiFiltersES, client, start, end, indices } = setup; + const transactionNameFilter = transactionName + ? [{ term: { [TRANSACTION_NAME]: transactionName } }] + : []; const params = { index: indices['apm_oss.transactionIndices'], body: { @@ -30,8 +37,9 @@ export async function getTransactionAvgDurationByCountry({ bool: { filter: [ { term: { [SERVICE_NAME]: serviceName } }, + ...transactionNameFilter, { term: { [PROCESSOR_EVENT]: 'transaction' } }, - { term: { [TRANSACTION_TYPE]: 'page-load' } }, + { term: { [TRANSACTION_TYPE]: TRANSACTION_PAGE_LOAD } }, { exists: { field: CLIENT_GEO_COUNTRY_ISO_CODE } }, { range: rangeFilter(start, end) }, ...uiFiltersES diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/index.ts b/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/index.ts index a21c4f38ac30c..3d425415de832 100644 --- a/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/index.ts +++ b/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/index.ts @@ -151,56 +151,53 @@ export async function getTransactionBreakdown({ const bucketsByDate = idx(resp.aggregations, _ => _.by_date.buckets) || []; - const timeseriesPerSubtype = bucketsByDate.reduce( - (prev, bucket) => { - const formattedValues = formatBucket(bucket); - const time = bucket.key; - - const updatedSeries = kpiNames.reduce((p, kpiName) => { - const { name, percentage } = formattedValues.find( - val => val.name === kpiName - ) || { - name: kpiName, - percentage: null - }; - - if (!p[name]) { - p[name] = []; - } - return { - ...p, - [name]: p[name].concat({ - x: time, - y: percentage - }) - }; - }, prev); - - const lastValues = Object.values(updatedSeries).map(last); - - // If for a given timestamp, some series have data, but others do not, - // we have to set any null values to 0 to make sure the stacked area chart - // is drawn correctly. - // If we set all values to 0, the chart always displays null values as 0, - // and the chart looks weird. - const hasAnyValues = lastValues.some(value => value.y !== null); - const hasNullValues = lastValues.some(value => value.y === null); - - if (hasAnyValues && hasNullValues) { - Object.values(updatedSeries).forEach(series => { - const value = series[series.length - 1]; - const isEmpty = value.y === null; - if (isEmpty) { - // local mutation to prevent complicated map/reduce calls - value.y = 0; - } - }); + const timeseriesPerSubtype = bucketsByDate.reduce((prev, bucket) => { + const formattedValues = formatBucket(bucket); + const time = bucket.key; + + const updatedSeries = kpiNames.reduce((p, kpiName) => { + const { name, percentage } = formattedValues.find( + val => val.name === kpiName + ) || { + name: kpiName, + percentage: null + }; + + if (!p[name]) { + p[name] = []; } + return { + ...p, + [name]: p[name].concat({ + x: time, + y: percentage + }) + }; + }, prev); + + const lastValues = Object.values(updatedSeries).map(last); + + // If for a given timestamp, some series have data, but others do not, + // we have to set any null values to 0 to make sure the stacked area chart + // is drawn correctly. + // If we set all values to 0, the chart always displays null values as 0, + // and the chart looks weird. + const hasAnyValues = lastValues.some(value => value.y !== null); + const hasNullValues = lastValues.some(value => value.y === null); + + if (hasAnyValues && hasNullValues) { + Object.values(updatedSeries).forEach(series => { + const value = series[series.length - 1]; + const isEmpty = value.y === null; + if (isEmpty) { + // local mutation to prevent complicated map/reduce calls + value.y = 0; + } + }); + } - return updatedSeries; - }, - {} as Record> - ); + return updatedSeries; + }, {} as Record>); const timeseries = kpis.map(kpi => ({ title: kpi.name, diff --git a/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/config.ts b/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/config.ts index 5d10a4ae27060..a0149bec728c5 100644 --- a/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/config.ts +++ b/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/config.ts @@ -61,17 +61,14 @@ export const localUIFilterNames = Object.keys( filtersByName ) as LocalUIFilterName[]; -export const localUIFilters = localUIFilterNames.reduce( - (acc, key) => { - const field = filtersByName[key]; +export const localUIFilters = localUIFilterNames.reduce((acc, key) => { + const field = filtersByName[key]; - return { - ...acc, - [key]: { - ...field, - name: key - } - }; - }, - {} as LocalUIFilterMap -); + return { + ...acc, + [key]: { + ...field, + name: key + } + }; +}, {} as LocalUIFilterMap); diff --git a/x-pack/legacy/plugins/apm/server/new-platform/plugin.ts b/x-pack/legacy/plugins/apm/server/new-platform/plugin.ts index 351afe618901e..e1cb1774469f2 100644 --- a/x-pack/legacy/plugins/apm/server/new-platform/plugin.ts +++ b/x-pack/legacy/plugins/apm/server/new-platform/plugin.ts @@ -7,7 +7,6 @@ import { Server } from 'hapi'; import { CoreSetup } from 'src/core/server'; import { makeApmUsageCollector } from '../lib/apm_telemetry'; -import { LegacySetupWithUsageCollector } from '../lib/apm_telemetry/make_apm_usage_collector'; import { createApmAgentConfigurationIndex } from '../lib/settings/agent_configuration/create_agent_config_index'; import { createApmApi } from '../routes/create_apm_api'; @@ -19,6 +18,6 @@ export class Plugin { public setup(core: CoreSetup, __LEGACY: LegacySetup) { createApmApi().init(core, __LEGACY); createApmAgentConfigurationIndex(core, __LEGACY); - makeApmUsageCollector(core, __LEGACY as LegacySetupWithUsageCollector); + makeApmUsageCollector(core, __LEGACY); } } diff --git a/x-pack/legacy/plugins/apm/server/routes/create_api/index.ts b/x-pack/legacy/plugins/apm/server/routes/create_api/index.ts index 2ce27fbc5e5e4..66f28a9bf6d44 100644 --- a/x-pack/legacy/plugins/apm/server/routes/create_api/index.ts +++ b/x-pack/legacy/plugins/apm/server/routes/create_api/index.ts @@ -68,41 +68,38 @@ export function createApi() { const parsedParams = (Object.keys(rts) as Array< keyof typeof rts - >).reduce( - (acc, key) => { - const codec = rts[key]; - const value = paramMap[key]; + >).reduce((acc, key) => { + const codec = rts[key]; + const value = paramMap[key]; - const result = codec.decode(value); + const result = codec.decode(value); - if (isLeft(result)) { - throw Boom.badRequest(PathReporter.report(result)[0]); - } + if (isLeft(result)) { + throw Boom.badRequest(PathReporter.report(result)[0]); + } - const strippedKeys = difference( - Object.keys(value || {}), - Object.keys(result.right || {}) - ); + const strippedKeys = difference( + Object.keys(value || {}), + Object.keys(result.right || {}) + ); - if (strippedKeys.length) { - throw Boom.badRequest( - `Unknown keys specified: ${strippedKeys}` - ); - } + if (strippedKeys.length) { + throw Boom.badRequest( + `Unknown keys specified: ${strippedKeys}` + ); + } - // hide _debug from route handlers - const parsedValue = - key === 'query' - ? omit(result.right, '_debug') - : result.right; + // hide _debug from route handlers + const parsedValue = + key === 'query' + ? omit(result.right, '_debug') + : result.right; - return { - ...acc, - [key]: parsedValue - }; - }, - {} as Record - ); + return { + ...acc, + [key]: parsedValue + }; + }, {} as Record); return route.handler( request, diff --git a/x-pack/legacy/plugins/apm/server/routes/services.ts b/x-pack/legacy/plugins/apm/server/routes/services.ts index 26fdf2ab65d1a..4b955c7a6e981 100644 --- a/x-pack/legacy/plugins/apm/server/routes/services.ts +++ b/x-pack/legacy/plugins/apm/server/routes/services.ts @@ -6,7 +6,10 @@ import * as t from 'io-ts'; import { AgentName } from '../../typings/es_schemas/ui/fields/Agent'; -import { createApmTelementry, storeApmTelemetry } from '../lib/apm_telemetry'; +import { + createApmTelementry, + storeApmServicesTelemetry +} from '../lib/apm_telemetry'; import { setupRequest } from '../lib/helpers/setup_request'; import { getServiceAgentName } from '../lib/services/get_service_agent_name'; import { getServices } from '../lib/services/get_services'; @@ -30,7 +33,7 @@ export const servicesRoute = createRoute((core, { server }) => ({ ({ agentName }) => agentName as AgentName ); const apmTelemetry = createApmTelementry(agentNames); - storeApmTelemetry(server, apmTelemetry); + storeApmServicesTelemetry(server, apmTelemetry); return services; } diff --git a/x-pack/legacy/plugins/apm/server/routes/settings/apm_indices.ts b/x-pack/legacy/plugins/apm/server/routes/settings/apm_indices.ts index 40c29f3050455..4afcf135a1a76 100644 --- a/x-pack/legacy/plugins/apm/server/routes/settings/apm_indices.ts +++ b/x-pack/legacy/plugins/apm/server/routes/settings/apm_indices.ts @@ -5,7 +5,6 @@ */ import * as t from 'io-ts'; -import { setupRequest } from '../../lib/helpers/setup_request'; import { createRoute } from '../create_route'; import { getApmIndices, @@ -18,8 +17,11 @@ export const apmIndexSettingsRoute = createRoute((core, { server }) => ({ method: 'GET', path: '/api/apm/settings/apm-index-settings', handler: async req => { - const setup = await setupRequest(req); - return await getApmIndexSettings({ setup, server }); + const config = server.config(); + const savedObjectsClient = req.server.savedObjects.getScopedSavedObjectsClient( + req + ); + return await getApmIndexSettings({ config, savedObjectsClient }); } })); @@ -28,12 +30,16 @@ export const apmIndicesRoute = createRoute((core, { server }) => ({ method: 'GET', path: '/api/apm/settings/apm-indices', handler: async req => { - return await getApmIndices(server); + const config = server.config(); + const savedObjectsClient = req.server.savedObjects.getScopedSavedObjectsClient( + req + ); + return await getApmIndices({ config, savedObjectsClient }); } })); // save ui indices -export const saveApmIndicesRoute = createRoute((core, { server }) => ({ +export const saveApmIndicesRoute = createRoute(() => ({ method: 'POST', path: '/api/apm/settings/apm-indices/save', params: { @@ -48,6 +54,9 @@ export const saveApmIndicesRoute = createRoute((core, { server }) => ({ }) }, handler: async (req, { body }) => { - return await saveApmIndices(server, body); + const savedObjectsClient = req.server.savedObjects.getScopedSavedObjectsClient( + req + ); + return await saveApmIndices(savedObjectsClient, body); } })); diff --git a/x-pack/legacy/plugins/apm/server/routes/transaction_groups.ts b/x-pack/legacy/plugins/apm/server/routes/transaction_groups.ts index cde9fd1dd4ca9..0b5c29fc29857 100644 --- a/x-pack/legacy/plugins/apm/server/routes/transaction_groups.ts +++ b/x-pack/legacy/plugins/apm/server/routes/transaction_groups.ts @@ -150,14 +150,20 @@ export const transactionGroupsAvgDurationByCountry = createRoute(() => ({ path: t.type({ serviceName: t.string }), - query: t.intersection([uiFiltersRt, rangeRt]) + query: t.intersection([ + uiFiltersRt, + rangeRt, + t.partial({ transactionName: t.string }) + ]) }, handler: async (req, { path, query }) => { const setup = await setupRequest(req); const { serviceName } = path; + const { transactionName } = query; return getTransactionAvgDurationByCountry({ serviceName, + transactionName, setup }); } diff --git a/x-pack/legacy/plugins/apm/server/routes/typings.ts b/x-pack/legacy/plugins/apm/server/routes/typings.ts index cf1a6cf769452..77d96d3677494 100644 --- a/x-pack/legacy/plugins/apm/server/routes/typings.ts +++ b/x-pack/legacy/plugins/apm/server/routes/typings.ts @@ -95,7 +95,9 @@ type GetOptionalParamKeys = keyof PickByValue< { [key in keyof TParams]: TParams[key] extends t.PartialType ? false - : (TParams[key] extends t.Any ? true : false); + : TParams[key] extends t.Any + ? true + : false; }, false >; diff --git a/x-pack/legacy/plugins/apm/server/routes/ui_filters.ts b/x-pack/legacy/plugins/apm/server/routes/ui_filters.ts index 9d36946d29cf6..36508e53acce7 100644 --- a/x-pack/legacy/plugins/apm/server/routes/ui_filters.ts +++ b/x-pack/legacy/plugins/apm/server/routes/ui_filters.ts @@ -45,9 +45,11 @@ export const uiFiltersEnvironmentsRoute = createRoute(() => ({ const filterNamesRt = t.type({ filterNames: jsonRt.pipe( t.array( - t.keyof(Object.fromEntries( - localUIFilterNames.map(filterName => [filterName, null]) - ) as Record) + t.keyof( + Object.fromEntries( + localUIFilterNames.map(filterName => [filterName, null]) + ) as Record + ) ) ) }); diff --git a/x-pack/legacy/plugins/apm/typings/elasticsearch/aggregations.ts b/x-pack/legacy/plugins/apm/typings/elasticsearch/aggregations.ts index 9f17b0197a5b2..b694e9526e2b8 100644 --- a/x-pack/legacy/plugins/apm/typings/elasticsearch/aggregations.ts +++ b/x-pack/legacy/plugins/apm/typings/elasticsearch/aggregations.ts @@ -202,22 +202,19 @@ interface AggregationResponsePart< TDocument > > - : (TAggregationOptionsMap extends { + : TAggregationOptionsMap extends { filters: { filters: Record; }; } - ? { - buckets: { - [key in keyof TAggregationOptionsMap['filters']['filters']]: { - doc_count: number; - } & AggregationResponseMap< - TAggregationOptionsMap['aggs'], - TDocument - >; - }; - } - : never); + ? { + buckets: { + [key in keyof TAggregationOptionsMap['filters']['filters']]: { + doc_count: number; + } & AggregationResponseMap; + }; + } + : never; sampler: { doc_count: number; } & AggregationResponseMap; diff --git a/x-pack/legacy/plugins/apm/typings/elasticsearch/index.ts b/x-pack/legacy/plugins/apm/typings/elasticsearch/index.ts index 56cd0ff23a3fb..eff39838bd957 100644 --- a/x-pack/legacy/plugins/apm/typings/elasticsearch/index.ts +++ b/x-pack/legacy/plugins/apm/typings/elasticsearch/index.ts @@ -36,8 +36,7 @@ export type ESSearchResponse< TDocument >; } - : {}) & - ({ + : {}) & { hits: Omit['hits'], 'total'> & (TOptions['restTotalHitsAsInt'] extends true ? { @@ -49,7 +48,7 @@ export type ESSearchResponse< relation: 'eq' | 'gte'; }; }); - }); + }; export interface ESFilter { [key: string]: { diff --git a/x-pack/legacy/plugins/beats_management/common/config_block_validation.ts b/x-pack/legacy/plugins/beats_management/common/config_block_validation.ts index 92137bdf5cc6e..8972084018d98 100644 --- a/x-pack/legacy/plugins/beats_management/common/config_block_validation.ts +++ b/x-pack/legacy/plugins/beats_management/common/config_block_validation.ts @@ -27,20 +27,17 @@ export const validateConfigurationBlocks = (configurationBlocks: ConfigurationBl ); } - const interfaceConfig = blockSchema.configs.reduce( - (props, config) => { - if (config.options) { - props[config.id] = t.keyof(Object.fromEntries( - config.options.map(opt => [opt.value, null]) - ) as Record); - } else if (config.validation) { - props[config.id] = validationMap[config.validation]; - } + const interfaceConfig = blockSchema.configs.reduce((props, config) => { + if (config.options) { + props[config.id] = t.keyof( + Object.fromEntries(config.options.map(opt => [opt.value, null])) as Record + ); + } else if (config.validation) { + props[config.id] = validationMap[config.validation]; + } - return props; - }, - {} as t.Props - ); + return props; + }, {} as t.Props); const runtimeInterface = createConfigurationBlockInterface( t.literal(blockSchema.id), diff --git a/x-pack/legacy/plugins/beats_management/common/domain_types.ts b/x-pack/legacy/plugins/beats_management/common/domain_types.ts index 0d5c67d09da8a..bc77abc9815be 100644 --- a/x-pack/legacy/plugins/beats_management/common/domain_types.ts +++ b/x-pack/legacy/plugins/beats_management/common/domain_types.ts @@ -13,9 +13,9 @@ export const OutputTypesArray = ['elasticsearch', 'logstash', 'kafka', 'redis']; // We can also pass in optional params to create spacific runtime checks that // can be used to validate blocs on the API and UI export const createConfigurationBlockInterface = ( - configType: t.LiteralType | t.KeyofC> = t.keyof(Object.fromEntries( - configBlockSchemas.map(s => [s.id, null]) - ) as Record), + configType: t.LiteralType | t.KeyofC> = t.keyof( + Object.fromEntries(configBlockSchemas.map(s => [s.id, null])) as Record + ), beatConfigInterface: t.Mixed = t.Dictionary ) => t.interface( diff --git a/x-pack/legacy/plugins/beats_management/public/components/autocomplete_field/index.tsx b/x-pack/legacy/plugins/beats_management/public/components/autocomplete_field/index.tsx index 479be32ce1e12..3ac2ff72c0116 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/autocomplete_field/index.tsx +++ b/x-pack/legacy/plugins/beats_management/public/components/autocomplete_field/index.tsx @@ -265,13 +265,11 @@ const withSuggestionsHidden = (state: AutocompleteFieldState) => ({ selectedIndex: null, }); -const FixedEuiFieldSearch: React.SFC< - React.InputHTMLAttributes & - EuiFieldSearchProps & { - inputRef?: (element: HTMLInputElement | null) => void; - onSearch: (value: string) => void; - } -> = EuiFieldSearch as any; +const FixedEuiFieldSearch: React.SFC & + EuiFieldSearchProps & { + inputRef?: (element: HTMLInputElement | null) => void; + onSearch: (value: string) => void; + }> = EuiFieldSearch as any; const AutocompleteContainer = styled.div` position: relative; diff --git a/x-pack/legacy/plugins/beats_management/public/components/navigation/breadcrumb/provider.tsx b/x-pack/legacy/plugins/beats_management/public/components/navigation/breadcrumb/provider.tsx index 8736663dd08af..f15e08c2ca230 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/navigation/breadcrumb/provider.tsx +++ b/x-pack/legacy/plugins/beats_management/public/components/navigation/breadcrumb/provider.tsx @@ -53,16 +53,13 @@ export class BreadcrumbProvider extends Component { - if (crumbStorageItem.parents) { - crumbs = crumbs.concat(crumbStorageItem.parents); - } - crumbs.push(crumbStorageItem.breadcrumb); - return crumbs; - }, - [] as Breadcrumb[] - ), + breadcrumbs: breadcrumbs.reduce((crumbs, crumbStorageItem) => { + if (crumbStorageItem.parents) { + crumbs = crumbs.concat(crumbStorageItem.parents); + } + crumbs.push(crumbStorageItem.breadcrumb); + return crumbs; + }, [] as Breadcrumb[]), addCrumb: this.addCrumb, removeCrumb: this.removeCrumb, }; diff --git a/x-pack/legacy/plugins/beats_management/public/components/table/table_type_configs.tsx b/x-pack/legacy/plugins/beats_management/public/components/table/table_type_configs.tsx index a93000a3e80f0..6f03f884563e1 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/table/table_type_configs.tsx +++ b/x-pack/legacy/plugins/beats_management/public/components/table/table_type_configs.tsx @@ -246,7 +246,10 @@ export const BeatsTableType: TableType = { name: i18n.translate('xpack.beatsManagement.beatsTable.typeLabel', { defaultMessage: 'Type', }), - options: uniq(data.map(({ type }: { type: any }) => ({ value: type })), 'value'), + options: uniq( + data.map(({ type }: { type: any }) => ({ value: type })), + 'value' + ), }, ], }), diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/beats/rest_beats_adapter.ts b/x-pack/legacy/plugins/beats_management/public/lib/adapters/beats/rest_beats_adapter.ts index 4a7f768bd4740..b2e11461007fd 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/beats/rest_beats_adapter.ts +++ b/x-pack/legacy/plugins/beats_management/public/lib/adapters/beats/rest_beats_adapter.ts @@ -26,9 +26,9 @@ export class RestBeatsAdapter implements CMBeatsAdapter { public async getBeatWithToken(enrollmentToken: string): Promise { try { - return (await this.REST.get>( - `/api/beats/agent/unknown/${enrollmentToken}` - )).item; + return ( + await this.REST.get>(`/api/beats/agent/unknown/${enrollmentToken}`) + ).item; } catch (e) { return null; } @@ -59,16 +59,20 @@ export class RestBeatsAdapter implements CMBeatsAdapter { public async removeTagsFromBeats( removals: BeatsTagAssignment[] ): Promise { - return (await this.REST.post(`/api/beats/agents_tags/removals`, { - removals, - })).results; + return ( + await this.REST.post(`/api/beats/agents_tags/removals`, { + removals, + }) + ).results; } public async assignTagsToBeats( assignments: BeatsTagAssignment[] ): Promise { - return (await this.REST.post(`/api/beats/agents_tags/assignments`, { - assignments, - })).results; + return ( + await this.REST.post(`/api/beats/agents_tags/assignments`, { + assignments, + }) + ).results; } } diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/tags/rest_tags_adapter.ts b/x-pack/legacy/plugins/beats_management/public/lib/adapters/tags/rest_tags_adapter.ts index 31c55b9272193..190c9e265463d 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/tags/rest_tags_adapter.ts +++ b/x-pack/legacy/plugins/beats_management/public/lib/adapters/tags/rest_tags_adapter.ts @@ -20,9 +20,9 @@ export class RestTagsAdapter implements CMTagsAdapter { public async getTagsWithIds(tagIds: string[]): Promise { try { - return (await this.REST.get>( - `/api/beats/tags/${uniq(tagIds).join(',')}` - )).items; + return ( + await this.REST.get>(`/api/beats/tags/${uniq(tagIds).join(',')}`) + ).items; } catch (e) { return []; } @@ -37,9 +37,9 @@ export class RestTagsAdapter implements CMTagsAdapter { } public async delete(tagIds: string[]): Promise { - return (await this.REST.delete( - `/api/beats/tags/${uniq(tagIds).join(',')}` - )).success; + return ( + await this.REST.delete(`/api/beats/tags/${uniq(tagIds).join(',')}`) + ).success; } public async upsertTag(tag: BeatTag): Promise { @@ -53,9 +53,11 @@ export class RestTagsAdapter implements CMTagsAdapter { public async getAssignable(beats: CMBeat[]) { try { - return (await this.REST.get>( - `/api/beats/tags/assignable/${beats.map(beat => beat.id).join(',')}` - )).items; + return ( + await this.REST.get>( + `/api/beats/tags/assignable/${beats.map(beat => beat.id).join(',')}` + ) + ).items; } catch (e) { return []; } diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/tokens/rest_tokens_adapter.ts b/x-pack/legacy/plugins/beats_management/public/lib/adapters/tokens/rest_tokens_adapter.ts index 8274764e759ab..92cfcc935ad9b 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/tokens/rest_tokens_adapter.ts +++ b/x-pack/legacy/plugins/beats_management/public/lib/adapters/tokens/rest_tokens_adapter.ts @@ -12,12 +12,11 @@ export class RestTokensAdapter implements CMTokensAdapter { constructor(private readonly REST: RestAPIAdapter) {} public async createEnrollmentTokens(numTokens: number = 1): Promise { - const results = (await this.REST.post>( - '/api/beats/enrollment_tokens', - { + const results = ( + await this.REST.post>('/api/beats/enrollment_tokens', { num_tokens: numTokens, - } - )).results; + }) + ).results; return results.map(result => result.item); } } diff --git a/x-pack/legacy/plugins/beats_management/public/lib/compose/scripts.ts b/x-pack/legacy/plugins/beats_management/public/lib/compose/scripts.ts index 4718abc1b7153..83129384a77df 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/compose/scripts.ts +++ b/x-pack/legacy/plugins/beats_management/public/lib/compose/scripts.ts @@ -22,7 +22,11 @@ import { FrontendLibs } from '../types'; export function compose(basePath: string): FrontendLibs { const api = new NodeAxiosAPIAdapter('elastic', 'changeme', basePath); - const esAdapter = new MemoryElasticsearchAdapter(() => true, () => '', []); + const esAdapter = new MemoryElasticsearchAdapter( + () => true, + () => '', + [] + ); const elasticsearchLib = new ElasticsearchLib(esAdapter); const configBlocks = new ConfigBlocksLib( new RestConfigBlocksAdapter(api), diff --git a/x-pack/legacy/plugins/beats_management/public/utils/typed_react.ts b/x-pack/legacy/plugins/beats_management/public/utils/typed_react.ts index 5557befa9d7e5..dbc0894ab8071 100644 --- a/x-pack/legacy/plugins/beats_management/public/utils/typed_react.ts +++ b/x-pack/legacy/plugins/beats_management/public/utils/typed_react.ts @@ -43,7 +43,9 @@ export const asChildFunctionRenderer = ( } public render() { - return this.props.children(this.getRendererArgs()); + return (this.props.children as ChildFunctionRendererProps['children'])( + this.getRendererArgs() + ); } private getRendererArgs = () => diff --git a/x-pack/legacy/plugins/beats_management/server/lib/tags.ts b/x-pack/legacy/plugins/beats_management/server/lib/tags.ts index cfcc2c4eda397..df9c65034115b 100644 --- a/x-pack/legacy/plugins/beats_management/server/lib/tags.ts +++ b/x-pack/legacy/plugins/beats_management/server/lib/tags.ts @@ -40,15 +40,12 @@ export class CMTagsDomain { public async getNonConflictingTags(user: FrameworkUser, existingTagIds: string[]) { const tags = await this.adapter.getTagsWithIds(user, existingTagIds); const existingUniqueBlockTypes = uniq( - tags.reduce( - (existingUniqueTypes, tag) => { - if (tag.hasConfigurationBlocksTypes) { - existingUniqueTypes = existingUniqueTypes.concat(tag.hasConfigurationBlocksTypes); - } - return existingUniqueTypes; - }, - [] as string[] - ) + tags.reduce((existingUniqueTypes, tag) => { + if (tag.hasConfigurationBlocksTypes) { + existingUniqueTypes = existingUniqueTypes.concat(tag.hasConfigurationBlocksTypes); + } + return existingUniqueTypes; + }, [] as string[]) ).filter(type => UNIQUENESS_ENFORCING_TYPES.includes(type)); const safeTags = await this.adapter.getWithoutConfigTypes(user, existingUniqueBlockTypes); diff --git a/x-pack/legacy/plugins/beats_management/server/rest_api/__tests__/beats_assignments.test.ts b/x-pack/legacy/plugins/beats_management/server/rest_api/__tests__/beats_assignments.test.ts index bb0376ab87d29..156304443431d 100644 --- a/x-pack/legacy/plugins/beats_management/server/rest_api/__tests__/beats_assignments.test.ts +++ b/x-pack/legacy/plugins/beats_management/server/rest_api/__tests__/beats_assignments.test.ts @@ -73,7 +73,10 @@ describe('assign_tags_to_beats', () => { authorization: 'loggedin', }, payload: { - assignments: [{ beatId: 'foo', tag: 'development' }, { beatId: 'bar', tag: 'development' }], + assignments: [ + { beatId: 'foo', tag: 'development' }, + { beatId: 'bar', tag: 'development' }, + ], }, }); @@ -115,7 +118,10 @@ describe('assign_tags_to_beats', () => { authorization: 'loggedin', }, payload: { - assignments: [{ beatId: 'bar', tag: 'development' }, { beatId: 'bar', tag: 'production' }], + assignments: [ + { beatId: 'bar', tag: 'development' }, + { beatId: 'bar', tag: 'production' }, + ], }, }); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/location.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/location.ts index c80c37bb1ed56..e592c6d22ef73 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/location.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/location.ts @@ -32,7 +32,10 @@ export function location(): ExpressionFunction<'location', null, {}, Promise { const fn = functionWrapper(csv); const expected = { type: 'datatable', - columns: [{ name: 'name', type: 'string' }, { name: 'number', type: 'string' }], + columns: [ + { name: 'name', type: 'string' }, + { name: 'number', type: 'string' }, + ], rows: [ { name: 'one', number: '1' }, { name: 'two', number: '2' }, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/get_flot_axis_config.test.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/get_flot_axis_config.test.js index bfc3212fa234c..a5b65e6bdc62b 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/get_flot_axis_config.test.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/get_flot_axis_config.test.js @@ -84,10 +84,16 @@ describe('getFlotAxisConfig', () => { describe('ticks', () => { it('adds a tick mark mapping for string columns', () => { let result = getFlotAxisConfig('x', true, { columns, ticks }); - expect(result.ticks).toEqual([[2, 'product1'], [1, 'product2']]); + expect(result.ticks).toEqual([ + [2, 'product1'], + [1, 'product2'], + ]); result = getFlotAxisConfig('x', xAxisConfig, { columns, ticks }); - expect(result.ticks).toEqual([[2, 'product1'], [1, 'product2']]); + expect(result.ticks).toEqual([ + [2, 'product1'], + [1, 'product2'], + ]); }); }); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/get_tick_hash.test.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/get_tick_hash.test.js index f44d37abe166f..74c13be3abd7b 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/get_tick_hash.test.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/get_tick_hash.test.js @@ -30,7 +30,12 @@ describe('getTickHash', () => { x: { type: 'number', role: 'dimension', expression: 'id' }, y: { type: 'boolean', role: 'dimension', expression: 'running' }, }; - const rows = [{ x: 1, y: true }, { x: 2, y: true }, { x: 1, y: false }, { x: 2, y: false }]; + const rows = [ + { x: 1, y: true }, + { x: 2, y: true }, + { x: 1, y: false }, + { x: 2, y: false }, + ]; expect(getTickHash(columns, rows)).toEqual({ x: { hash: {}, counter: 0 }, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/math.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/math.ts index c180b6186ee92..718f2d8b11e1a 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/math.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/math.ts @@ -44,7 +44,10 @@ export function math(): ExpressionFunction<'math', Context, Arguments, number> { } const mathContext = isDatatable(context) - ? pivotObjectArray(context.rows, context.columns.map(col => col.name)) + ? pivotObjectArray( + context.rows, + context.columns.map(col => col.name) + ) : { value: context }; try { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts index 5889864df3c1d..5d5c3c1a735a0 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts @@ -187,7 +187,10 @@ export function pointseries(): ExpressionFunction< // Then compute that 1 value for each measure Object.values(measureKeys).forEach(valueRows => { const subtable = { type: 'datatable', columns: context.columns, rows: valueRows }; - const subScope = pivotObjectArray(subtable.rows, subtable.columns.map(col => col.name)); + const subScope = pivotObjectArray( + subtable.rows, + subtable.columns.map(col => col.name) + ); const measureValues = measureNames.map(measure => { try { const ev = evaluate(args[measure], subScope); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/pie/plugins/pie.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/pie/plugins/pie.js index 9229def146f8b..042cbe83fb10f 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/pie/plugins/pie.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/pie/plugins/pie.js @@ -683,7 +683,14 @@ function init(plot) { const p4Y = radius * Math.sin(s.startAngle + s.angle / 1.5); const p5X = radius * Math.cos(s.startAngle + s.angle); const p5Y = radius * Math.sin(s.startAngle + s.angle); - const arrPoly = [[0, 0], [p1X, p1Y], [p2X, p2Y], [p3X, p3Y], [p4X, p4Y], [p5X, p5Y]]; + const arrPoly = [ + [0, 0], + [p1X, p1Y], + [p2X, p2Y], + [p3X, p3Y], + [p4X, p4Y], + [p5X, p5Y], + ]; const arrPoint = [x, y]; // TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt? diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__examples__/__snapshots__/simple_template.examples.storyshot b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__examples__/__snapshots__/simple_template.examples.storyshot index bf68d217f18ab..0b9358714e71c 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__examples__/__snapshots__/simple_template.examples.storyshot +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__examples__/__snapshots__/simple_template.examples.storyshot @@ -13,23 +13,26 @@ exports[`Storyshots arguments/AxisConfig simple 1`] = `
- - - - + className="euiSwitch__body" + > + + + +
`; @@ -47,23 +50,26 @@ exports[`Storyshots arguments/AxisConfig/components simple template 1`] = `
- - - - + className="euiSwitch__body" + > + + + +
`; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/simple_template.tsx b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/simple_template.tsx index eb32881bc1f6d..068854866dc1b 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/simple_template.tsx +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/simple_template.tsx @@ -19,6 +19,8 @@ export const SimpleTemplate: FunctionComponent = ({ onValueChange, argVal compressed checked={Boolean(argValue)} onChange={() => onValueChange(!Boolean(argValue))} + showLabel={false} + label="" /> ); }; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/pie.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/pie.js index 6a7a89231f250..4bb68973e80ea 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/pie.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/pie.js @@ -14,7 +14,10 @@ const { Pie: strings } = ViewStrings; export const pie = () => ({ name: 'pie', displayName: strings.getDisplayName(), - modelArgs: [['color', { label: 'Slice Labels' }], ['size', { label: 'Slice Angles' }]], + modelArgs: [ + ['color', { label: 'Slice Labels' }], + ['size', { label: 'Slice Angles' }], + ], args: [ { name: 'palette', diff --git a/x-pack/legacy/plugins/canvas/public/apps/export/export/index.js b/x-pack/legacy/plugins/canvas/public/apps/export/export/index.js index 16ca644160c0a..d40c5f787e44f 100644 --- a/x-pack/legacy/plugins/canvas/public/apps/export/export/index.js +++ b/x-pack/legacy/plugins/canvas/public/apps/export/export/index.js @@ -25,9 +25,6 @@ const mapDispatchToProps = dispatch => ({ const branches = [branch(({ workpad }) => workpad == null, renderComponent(LoadWorkpad))]; export const ExportApp = compose( - connect( - mapStateToProps, - mapDispatchToProps - ), + connect(mapStateToProps, mapDispatchToProps), ...branches )(Component); diff --git a/x-pack/legacy/plugins/canvas/public/apps/home/home_app/index.js b/x-pack/legacy/plugins/canvas/public/apps/home/home_app/index.js index 8ecde2cc721e3..f26b3510c7682 100644 --- a/x-pack/legacy/plugins/canvas/public/apps/home/home_app/index.js +++ b/x-pack/legacy/plugins/canvas/public/apps/home/home_app/index.js @@ -14,7 +14,4 @@ const mapDispatchToProps = dispatch => ({ }, }); -export const HomeApp = connect( - null, - mapDispatchToProps -)(Component); +export const HomeApp = connect(null, mapDispatchToProps)(Component); diff --git a/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/index.js b/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/index.js index 8789577c92f63..f0a5325baf3c0 100644 --- a/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/index.js +++ b/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/index.js @@ -35,10 +35,7 @@ const mapDispatchToProps = dispatch => ({ const branches = [branch(({ workpad }) => workpad == null, renderComponent(LoadWorkpad))]; export const WorkpadApp = compose( - connect( - mapStateToProps, - mapDispatchToProps - ), + connect(mapStateToProps, mapDispatchToProps), ...branches, withElementsLoadedTelemetry )(Component); diff --git a/x-pack/legacy/plugins/canvas/public/components/app/index.js b/x-pack/legacy/plugins/canvas/public/components/app/index.js index b3aa9c393096b..65b811fe68134 100644 --- a/x-pack/legacy/plugins/canvas/public/components/app/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/app/index.js @@ -97,11 +97,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { }; export const App = compose( - connect( - mapStateToProps, - mapDispatchToProps, - mergeProps - ), + connect(mapStateToProps, mapDispatchToProps, mergeProps), withProps(() => ({ onRouteChange: trackRouteChange, })) diff --git a/x-pack/legacy/plugins/canvas/public/components/asset_manager/index.js b/x-pack/legacy/plugins/canvas/public/components/asset_manager/index.js index 69c4463e7d210..6c05eec0c3c09 100644 --- a/x-pack/legacy/plugins/canvas/public/components/asset_manager/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/asset_manager/index.js @@ -89,10 +89,6 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { }; export const AssetManager = compose( - connect( - mapStateToProps, - mapDispatchToProps, - mergeProps - ), + connect(mapStateToProps, mapDispatchToProps, mergeProps), withProps({ onAssetCopy: asset => notify.success(`Copied '${asset.id}' to clipboard`) }) )(Component); diff --git a/x-pack/legacy/plugins/canvas/public/components/datasource/index.js b/x-pack/legacy/plugins/canvas/public/components/datasource/index.js index 2c7820df26ca9..7fb73b1672eb1 100644 --- a/x-pack/legacy/plugins/canvas/public/components/datasource/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/datasource/index.js @@ -82,11 +82,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { }; export const Datasource = compose( - connect( - mapStateToProps, - mapDispatchToProps, - mergeProps - ), + connect(mapStateToProps, mapDispatchToProps, mergeProps), withState('stateArgs', 'updateArgs', ({ args }) => args), withState('selecting', 'setSelecting', false), withState('previewing', 'setPreviewing', false), diff --git a/x-pack/legacy/plugins/canvas/public/components/element_content/element_content.js b/x-pack/legacy/plugins/canvas/public/components/element_content/element_content.js index e85ce8c10da64..89c0b5b21c581 100644 --- a/x-pack/legacy/plugins/canvas/public/components/element_content/element_content.js +++ b/x-pack/legacy/plugins/canvas/public/components/element_content/element_content.js @@ -21,9 +21,12 @@ import { InvalidElementType } from './invalid_element_type'; */ const branches = [ // no renderable or renderable config value, render loading - branch(({ renderable, state }) => { - return !state || !renderable; - }, renderComponent(({ backgroundColor }) => )), + branch( + ({ renderable, state }) => { + return !state || !renderable; + }, + renderComponent(({ backgroundColor }) => ) + ), // renderable is available, but no matching element is found, render invalid branch(({ renderable, renderFunction }) => { diff --git a/x-pack/legacy/plugins/canvas/public/components/element_types/element_types.js b/x-pack/legacy/plugins/canvas/public/components/element_types/element_types.js index 26a0d92655362..dabf06a24aeb6 100644 --- a/x-pack/legacy/plugins/canvas/public/components/element_types/element_types.js +++ b/x-pack/legacy/plugins/canvas/public/components/element_types/element_types.js @@ -116,7 +116,10 @@ export class ElementTypes extends Component { }; _sortElements = elements => - sortBy(map(elements, (element, name) => ({ name, ...element })), 'displayName'); + sortBy( + map(elements, (element, name) => ({ name, ...element })), + 'displayName' + ); render() { const { diff --git a/x-pack/legacy/plugins/canvas/public/components/element_types/index.js b/x-pack/legacy/plugins/canvas/public/components/element_types/index.js index 20d19d2fcbd82..8faaf278a07de 100644 --- a/x-pack/legacy/plugins/canvas/public/components/element_types/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/element_types/index.js @@ -95,11 +95,7 @@ export const ElementTypes = compose( withState('customElements', 'setCustomElements', []), withState('filterTags', 'setFilterTags', []), withProps(() => ({ elements: elementsRegistry.toJS() })), - connect( - mapStateToProps, - mapDispatchToProps, - mergeProps - ) + connect(mapStateToProps, mapDispatchToProps, mergeProps) )(Component); ElementTypes.propTypes = { diff --git a/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/index.tsx b/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/index.tsx index 8a040c0c90c83..612406c30f88e 100644 --- a/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/index.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/index.tsx @@ -105,9 +105,5 @@ export class EmbeddableFlyoutPortal extends React.Component { } export const AddEmbeddablePanel = compose void }>( - connect( - mapStateToProps, - mapDispatchToProps, - mergeProps - ) + connect(mapStateToProps, mapDispatchToProps, mergeProps) )(EmbeddableFlyoutPortal); diff --git a/x-pack/legacy/plugins/canvas/public/components/expression/index.js b/x-pack/legacy/plugins/canvas/public/components/expression/index.js index 6ae4ef984264f..4bcdb547186d1 100644 --- a/x-pack/legacy/plugins/canvas/public/components/expression/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/expression/index.js @@ -70,11 +70,7 @@ const expressionLifecycle = lifecycle({ }); export const Expression = compose( - connect( - mapStateToProps, - mapDispatchToProps, - mergeProps - ), + connect(mapStateToProps, mapDispatchToProps, mergeProps), withState('functionDefinitions', 'setFunctionDefinitions', []), withState('formState', 'setFormState', ({ expression }) => ({ expression, diff --git a/x-pack/legacy/plugins/canvas/public/components/function_form/index.js b/x-pack/legacy/plugins/canvas/public/components/function_form/index.js index 32e9cbbd1d91d..774214cf68cec 100644 --- a/x-pack/legacy/plugins/canvas/public/components/function_form/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/function_form/index.js @@ -105,11 +105,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { }; }; -export const FunctionForm = connect( - mapStateToProps, - mapDispatchToProps, - mergeProps -)(Component); +export const FunctionForm = connect(mapStateToProps, mapDispatchToProps, mergeProps)(Component); FunctionForm.propTypes = { expressionIndex: PropTypes.number, diff --git a/x-pack/legacy/plugins/canvas/public/components/page_config/index.js b/x-pack/legacy/plugins/canvas/public/components/page_config/index.js index a0692584d4986..a51e6b4b5d987 100644 --- a/x-pack/legacy/plugins/canvas/public/components/page_config/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/page_config/index.js @@ -43,8 +43,4 @@ const mergeProps = (stateProps, dispatchProps) => { }; }; -export const PageConfig = connect( - mapStateToProps, - mapDispatchToProps, - mergeProps -)(Component); +export const PageConfig = connect(mapStateToProps, mapDispatchToProps, mergeProps)(Component); diff --git a/x-pack/legacy/plugins/canvas/public/components/page_manager/index.js b/x-pack/legacy/plugins/canvas/public/components/page_manager/index.js index 06507e4eb9a71..ef5de3feb575c 100644 --- a/x-pack/legacy/plugins/canvas/public/components/page_manager/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/page_manager/index.js @@ -32,9 +32,6 @@ const mapDispatchToProps = dispatch => ({ }); export const PageManager = compose( - connect( - mapStateToProps, - mapDispatchToProps - ), + connect(mapStateToProps, mapDispatchToProps), withState('deleteId', 'setDeleteId', null) )(Component); diff --git a/x-pack/legacy/plugins/canvas/public/components/router/index.js b/x-pack/legacy/plugins/canvas/public/components/router/index.js index 51d856b0fc7a6..430d6a5343662 100644 --- a/x-pack/legacy/plugins/canvas/public/components/router/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/router/index.js @@ -20,7 +20,4 @@ const mapDispatchToState = { setRefreshInterval, }; -export const Router = connect( - null, - mapDispatchToState -)(Component); +export const Router = connect(null, mapDispatchToState)(Component); diff --git a/x-pack/legacy/plugins/canvas/public/components/sidebar/sidebar_content.js b/x-pack/legacy/plugins/canvas/public/components/sidebar/sidebar_content.js index 825ea8a4f2494..9de5f81b440ba 100644 --- a/x-pack/legacy/plugins/canvas/public/components/sidebar/sidebar_content.js +++ b/x-pack/legacy/plugins/canvas/public/components/sidebar/sidebar_content.js @@ -93,10 +93,6 @@ const branches = [ ]; export const SidebarContent = compose( - connect( - mapStateToProps, - null, - mergeProps - ), + connect(mapStateToProps, null, mergeProps), ...branches )(GlobalConfig); diff --git a/x-pack/legacy/plugins/canvas/public/components/sidebar_header/index.js b/x-pack/legacy/plugins/canvas/public/components/sidebar_header/index.js index f60aad1fa3e72..ac282962afb54 100644 --- a/x-pack/legacy/plugins/canvas/public/components/sidebar_header/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/sidebar_header/index.js @@ -56,10 +56,7 @@ const mapDispatchToProps = dispatch => ({ }); export const SidebarHeader = compose( - connect( - mapStateToProps, - mapDispatchToProps - ), + connect(mapStateToProps, mapDispatchToProps), withHandlers(basicHandlerCreators), withHandlers(clipboardHandlerCreators), withHandlers(layerHandlerCreators), diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad/index.js b/x-pack/legacy/plugins/canvas/public/components/workpad/index.js index 723b30aac37fa..fca663f8532d8 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/workpad/index.js @@ -72,11 +72,7 @@ export const Workpad = compose( router: PropTypes.object, }), withState('grid', 'setGrid', false), - connect( - mapStateToProps, - mapDispatchToProps, - mergeProps - ), + connect(mapStateToProps, mapDispatchToProps, mergeProps), withState('transition', 'setTransition', null), withState('prevSelectedPageNumber', 'setPrevSelectedPageNumber', 0), withProps(({ selectedPageNumber, prevSelectedPageNumber, transition }) => { diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_color_picker/index.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_color_picker/index.ts index 2b966e7bd6bb8..c6dddab3b5dd1 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_color_picker/index.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_color_picker/index.ts @@ -21,7 +21,4 @@ const mapDispatchToProps = { onRemoveColor: removeColor, }; -export const WorkpadColorPicker = connect( - mapStateToProps, - mapDispatchToProps -)(Component); +export const WorkpadColorPicker = connect(mapStateToProps, mapDispatchToProps)(Component); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_config/index.js b/x-pack/legacy/plugins/canvas/public/components/workpad_config/index.js index b13740f177a23..aa3bbdce97863 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_config/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_config/index.js @@ -31,7 +31,4 @@ const mapDispatchToProps = { setWorkpadCSS: css => setWorkpadCSS(css), }; -export const WorkpadConfig = connect( - mapStateToProps, - mapDispatchToProps -)(Component); +export const WorkpadConfig = connect(mapStateToProps, mapDispatchToProps)(Component); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/index.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/index.ts index dc4c4461c5921..316a49c85c09d 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/index.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/index.ts @@ -32,7 +32,4 @@ const mapDispatchToProps = { setAutoplayInterval, }; -export const ControlSettings = connect( - mapStateToProps, - mapDispatchToProps -)(Component); +export const ControlSettings = connect(mapStateToProps, mapDispatchToProps)(Component); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/fullscreen_control/index.js b/x-pack/legacy/plugins/canvas/public/components/workpad_header/fullscreen_control/index.js index cf2e80cee03bf..fc0bd4c74ba24 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/fullscreen_control/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/fullscreen_control/index.js @@ -68,11 +68,7 @@ export const FullscreenControl = compose( getContext({ router: PropTypes.object, }), - connect( - mapStateToProps, - mapDispatchToProps, - mergeProps - ), + connect(mapStateToProps, mapDispatchToProps, mergeProps), withState('transition', 'setTransition', null), withState('prevSelectedPageNumber', 'setPrevSelectedPageNumber', 0), withProps(({ selectedPageNumber, prevSelectedPageNumber, transition }) => { diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/index.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/index.tsx index 6d3c9a0640ba2..d2fece567a8ad 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/index.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/index.tsx @@ -46,8 +46,4 @@ const mergeProps = ( toggleWriteable: () => dispatchProps.setWriteable(!stateProps.isWriteable), }); -export const WorkpadHeader = connect( - mapStateToProps, - mapDispatchToProps, - mergeProps -)(Component); +export const WorkpadHeader = connect(mapStateToProps, mapDispatchToProps, mergeProps)(Component); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/refresh_control/index.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/refresh_control/index.ts index 718fec9f59f77..53c053811a273 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/refresh_control/index.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/refresh_control/index.ts @@ -20,7 +20,4 @@ const mapDispatchToProps = { doRefresh: fetchAllRenderables, }; -export const RefreshControl = connect( - mapStateToProps, - mapDispatchToProps -)(Component); +export const RefreshControl = connect(mapStateToProps, mapDispatchToProps)(Component); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/index.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/index.tsx index 406d6b54729b9..b22a9d35aa793 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/index.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/index.tsx @@ -33,9 +33,6 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ }); export const WorkpadZoom = compose( - connect( - mapStateToProps, - mapDispatchToProps - ), + connect(mapStateToProps, mapDispatchToProps), withHandlers(zoomHandlerCreators) )(Component); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_page/integration_utils.js b/x-pack/legacy/plugins/canvas/public/components/workpad_page/integration_utils.js index 34c3d446c5ca7..731656dd4e095 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_page/integration_utils.js +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_page/integration_utils.js @@ -135,7 +135,12 @@ export const globalStateUpdater = (dispatch, globalState) => state => { if (elementsToRemove.length) { // remove elements for groups that were ungrouped - dispatch(removeElements(elementsToRemove.map(e => e.id), page)); + dispatch( + removeElements( + elementsToRemove.map(e => e.id), + page + ) + ); } // set the selected element on the global store, if one element is selected diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_page/workpad_interactive_page/index.js b/x-pack/legacy/plugins/canvas/public/components/workpad_page/workpad_interactive_page/index.js index 454fa6f917aec..4ee3a65172a2e 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_page/workpad_interactive_page/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_page/workpad_interactive_page/index.js @@ -161,11 +161,7 @@ const mergeProps = ( }); export const InteractivePage = compose( - connect( - mapStateToProps, - mapDispatchToProps, - mergeProps - ), + connect(mapStateToProps, mapDispatchToProps, mergeProps), withState('aeroStore', 'setAeroStore'), withProps(componentLayoutState), withProps(({ aeroStore, updateGlobalState }) => ({ diff --git a/x-pack/legacy/plugins/canvas/public/expression_types/arg_types/font.js b/x-pack/legacy/plugins/canvas/public/expression_types/arg_types/font.js index 893caba1465b8..46a97f7c15d74 100644 --- a/x-pack/legacy/plugins/canvas/public/expression_types/arg_types/font.js +++ b/x-pack/legacy/plugins/canvas/public/expression_types/arg_types/font.js @@ -24,7 +24,11 @@ export const FontArgInput = props => { const spec = mapValues(chainArgs, '[0]'); function handleChange(newSpec) { - const newValue = set(argValue, ['chain', 0, 'arguments'], mapValues(newSpec, v => [v])); + const newValue = set( + argValue, + ['chain', 0, 'arguments'], + mapValues(newSpec, v => [v]) + ); return onValueChange(newValue); } diff --git a/x-pack/legacy/plugins/canvas/public/lib/aeroelastic/layout_functions.js b/x-pack/legacy/plugins/canvas/public/lib/aeroelastic/layout_functions.js index 4b99a5ef298d8..d3da1b5553958 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/aeroelastic/layout_functions.js +++ b/x-pack/legacy/plugins/canvas/public/lib/aeroelastic/layout_functions.js @@ -62,17 +62,46 @@ const resizeVertexTuples = [ ]; const connectorVertices = [ - [[-1, -1], [0, -1]], - [[0, -1], [1, -1]], - [[1, -1], [1, 0]], - [[1, 0], [1, 1]], - [[1, 1], [0, 1]], - [[0, 1], [-1, 1]], - [[-1, 1], [-1, 0]], - [[-1, 0], [-1, -1]], + [ + [-1, -1], + [0, -1], + ], + [ + [0, -1], + [1, -1], + ], + [ + [1, -1], + [1, 0], + ], + [ + [1, 0], + [1, 1], + ], + [ + [1, 1], + [0, 1], + ], + [ + [0, 1], + [-1, 1], + ], + [ + [-1, 1], + [-1, 0], + ], + [ + [-1, 0], + [-1, -1], + ], ]; -const cornerVertices = [[-1, -1], [1, -1], [-1, 1], [1, 1]]; +const cornerVertices = [ + [-1, -1], + [1, -1], + [-1, 1], + [1, 1], +]; const resizeMultiplierHorizontal = { left: -1, center: 0, right: 1 }; const resizeMultiplierVertical = { top: -1, center: 0, bottom: 1 }; @@ -91,7 +120,10 @@ const bidirectionalCursors = { '315': 'nwse-resize', }; -const identityAABB = () => [[Infinity, Infinity], [-Infinity, -Infinity]]; +const identityAABB = () => [ + [Infinity, Infinity], + [-Infinity, -Infinity], +]; const extend = ([[xMin, yMin], [xMax, yMax]], [x0, y0], [x1, y1]) => [ [Math.min(xMin, x0, x1), Math.min(yMin, y0, y1)], @@ -547,14 +579,18 @@ export const applyLocalTransforms = (shapes, transformIntents) => { // eslint-disable-next-line const getUpstreamTransforms = (shapes, shape) => shape.parent - ? getUpstreamTransforms(shapes, shapes.find(s => s.id === shape.parent)).concat([ - shape.localTransformMatrix, - ]) + ? getUpstreamTransforms( + shapes, + shapes.find(s => s.id === shape.parent) + ).concat([shape.localTransformMatrix]) : [shape.localTransformMatrix]; const getUpstreams = (shapes, shape) => shape.parent - ? getUpstreams(shapes, shapes.find(s => s.id === shape.parent)).concat([shape]) + ? getUpstreams( + shapes, + shapes.find(s => s.id === shape.parent) + ).concat([shape]) : [shape]; const snappedA = shape => shape.a + (shape.snapResizeVector ? shape.snapResizeVector[0] : 0); @@ -877,7 +913,12 @@ function resizeAnnotation(config, shapes, selectedShapes, shape) { const b = snappedB(properShape); const allowResize = properShape.type !== 'group' || - (config.groupResize && magic(config, properShape, shapes.filter(s => s.type !== 'annotation'))); + (config.groupResize && + magic( + config, + properShape, + shapes.filter(s => s.type !== 'annotation') + )); const resizeVertices = allowResize ? resizeVertexTuples : []; const resizePoints = resizeVertices.map(resizePointAnnotations(config, shape, a, b)); const connectors = connectorVertices.map(resizeEdgeAnnotations(config, shape, a, b)); @@ -1235,7 +1276,10 @@ export const getGrouping = (config, shapes, selectedShapes, groupAction, tuple) return config.groupResize ? { shapes: [ - ...resizeGroup(shapes.filter(s => s.type !== 'annotation'), elements[0]), + ...resizeGroup( + shapes.filter(s => s.type !== 'annotation'), + elements[0] + ), ...shapes.filter(s => s.type === 'annotation'), ], selectedShapes, diff --git a/x-pack/legacy/plugins/canvas/public/state/actions/elements.js b/x-pack/legacy/plugins/canvas/public/state/actions/elements.js index 1005cc60e50ba..7b7e87b027af5 100644 --- a/x-pack/legacy/plugins/canvas/public/state/actions/elements.js +++ b/x-pack/legacy/plugins/canvas/public/state/actions/elements.js @@ -227,9 +227,12 @@ export const removeElements = createThunk( // todo consider doing the group membership collation in aeroelastic, or the Redux reducer, when adding templates const allElements = getNodes(state, pageId); const allRoots = rootElementIds.map(id => allElements.find(e => id === e.id)).filter(d => d); - const elementIds = subMultitree(e => e.id, e => e.position.parent, allElements, allRoots).map( - e => e.id - ); + const elementIds = subMultitree( + e => e.id, + e => e.position.parent, + allElements, + allRoots + ).map(e => e.id); const shouldRefresh = elementIds.some(elementId => { const element = getNodeById(state, elementId, pageId); diff --git a/x-pack/legacy/plugins/canvas/shareable_runtime/components/__tests__/app.test.tsx b/x-pack/legacy/plugins/canvas/shareable_runtime/components/__tests__/app.test.tsx index db0aac34336ea..9cf2ddc3a22e3 100644 --- a/x-pack/legacy/plugins/canvas/shareable_runtime/components/__tests__/app.test.tsx +++ b/x-pack/legacy/plugins/canvas/shareable_runtime/components/__tests__/app.test.tsx @@ -111,7 +111,7 @@ describe('', () => { wrapper.update(); expect(footer(wrapper).prop('isHidden')).toEqual(false); expect(footer(wrapper).prop('isAutohide')).toEqual(false); - toolbarCheck(wrapper).simulate('change'); + toolbarCheck(wrapper).simulate('click'); expect(footer(wrapper).prop('isAutohide')).toEqual(true); canvas(wrapper).simulate('mouseEnter'); expect(footer(wrapper).prop('isHidden')).toEqual(false); @@ -132,7 +132,7 @@ describe('', () => { .simulate('click'); await tick(20); wrapper.update(); - toolbarCheck(wrapper).simulate('change'); + toolbarCheck(wrapper).simulate('click'); await tick(20); // Simulate the mouse leaving the container diff --git a/x-pack/legacy/plugins/canvas/shareable_runtime/components/footer/settings/__examples__/__snapshots__/autoplay_settings.examples.storyshot b/x-pack/legacy/plugins/canvas/shareable_runtime/components/footer/settings/__examples__/__snapshots__/autoplay_settings.examples.storyshot index b159e6499ed9f..1e66e19b3c0e1 100644 --- a/x-pack/legacy/plugins/canvas/shareable_runtime/components/footer/settings/__examples__/__snapshots__/autoplay_settings.examples.storyshot +++ b/x-pack/legacy/plugins/canvas/shareable_runtime/components/footer/settings/__examples__/__snapshots__/autoplay_settings.examples.storyshot @@ -25,49 +25,52 @@ exports[`Storyshots shareables/Footer/Settings/AutoplaySettings component: off,
- - - - - + + + + - -

- - - - - + + + + - -

- - - - - + + + + - -

- - - - - + + + + - -
- - - - - + + + + - -
- - - - - + + + + - -
can navigate Autoplay Settings 1`] = ` data-focus-lock-disabled="disabled" >