diff --git a/docs/user/reporting/images/shareable-container.png b/docs/user/reporting/images/shareable-container.png new file mode 100644 index 0000000000000..db5a41dcff471 Binary files /dev/null and b/docs/user/reporting/images/shareable-container.png differ diff --git a/docs/user/reporting/index.asciidoc b/docs/user/reporting/index.asciidoc index 06af9e6038445..fde88130a26b4 100644 --- a/docs/user/reporting/index.asciidoc +++ b/docs/user/reporting/index.asciidoc @@ -6,11 +6,11 @@ -- -You can generate a report that contains a {kib} dashboard, visualization, -saved search, or Canvas workpad. Depending on the object type, you can export the data as +You can generate a report that contains a {kib} dashboard, visualization, +saved search, or Canvas workpad. Depending on the object type, you can export the data as a PDF, PNG, or CSV document, which you can keep for yourself, or share with others. -Reporting is available from the *Share* menu +Reporting is available from the *Share* menu in *Discover*, *Visualize*, *Dashboard*, and *Canvas*. [role="screenshot"] @@ -40,9 +40,9 @@ for an example. [[manually-generate-reports]] == Generate a report manually -. Open the dashboard, visualization, Canvas workpad, or saved search that you want to include in the report. +. Open the dashboard, visualization, Canvas workpad, or saved search that you want to include in the report. -. In the {kib} toolbar, click *Share*. If you are working in Canvas, +. In the {kib} toolbar, click *Share*. If you are working in Canvas, click the share icon image:user/reporting/images/canvas-share-button.png["Canvas Share button"]. . Select the option appropriate for your object. You can export: @@ -55,14 +55,36 @@ click the share icon image:user/reporting/images/canvas-share-button.png["Canvas + A notification appears when the report is complete. +[float] +[[reporting-layout-sizing]] +== Layout and sizing +The layout and size of the PDF or PNG image depends on the {kib} app +with which the Reporting plugin is integrated. For Canvas, the +worksheet dimensions determine the size for Reporting. In other apps, +the dimensions are taken on the fly by looking at +the size of the visualization elements or panels on the page. + +The size dimensions are part of the reporting job parameters. Therefore, to +make the report output larger or smaller, you can change the size of the browser. +This resizes the shareable container before generating the +report, so the desired dimensions are passed in the job parameters. + +In the following {kib} dashboard, the shareable container is highlighted. +The shareable container is captured when you click the +*Generate* or *Copy POST URL* button. It might take some trial and error +before you're satisfied with the layout and dimensions in the resulting +PNG or PDF image. + +[role="screenshot"] +image::user/reporting/images/shareable-container.png["Shareable Container"] + + + [float] [[optimize-pdf]] == Optimize PDF for print—dashboard only -By default, {kib} creates a PDF -using the existing layout and size of the dashboard. To create a -printer-friendly PDF with multiple A4 portrait pages and two visualizations -per page, turn on *Optimize for printing*. +To create a printer-friendly PDF with multiple A4 portrait pages and two visualizations per page, turn on *Optimize for printing*. [role="screenshot"] image::user/reporting/images/preserve-layout-switch.png["Share"] @@ -72,8 +94,8 @@ image::user/reporting/images/preserve-layout-switch.png["Share"] [[manage-report-history]] == View and manage report history -For a list of your reports, go to *Management > Reporting*. -From this view, you can monitor the generation of a report and +For a list of your reports, go to *Management > Reporting*. +From this view, you can monitor the generation of a report and download reports that you previously generated. [float] diff --git a/docs/user/reporting/reporting-troubleshooting.asciidoc b/docs/user/reporting/reporting-troubleshooting.asciidoc index ca7fa6abcc9d9..dc4ffdfebdae9 100644 --- a/docs/user/reporting/reporting-troubleshooting.asciidoc +++ b/docs/user/reporting/reporting-troubleshooting.asciidoc @@ -7,12 +7,20 @@ Having trouble? Here are solutions to common problems you might encounter while using Reporting. +* <> +* <> +* <> +* <> +* <> +* <> +* <> + [float] [[reporting-troubleshooting-system-dependencies]] === System dependencies Reporting launches a "headless" web browser called Chromium on the Kibana server. It is a custom build made by Elastic of an open source project, and it is intended to have minimal dependencies on OS libraries. However, the Kibana server OS might still require additional -dependencies for Chromium. +dependencies to run the Chromium executable. Make sure Kibana server OS has the appropriate packages installed for the distribution. @@ -33,19 +41,30 @@ If you are using Ubuntu/Debian systems, install the following packages: * `fonts-liberation` * `libfontconfig1` +If the system is missing dependencies, then Reporting will fail in a non-deterministic way. {kib} runs a self-test at server startup, and +if it encounters errors, logs them in the Console. Unfortunately, the error message does not include +information about why Chromium failed to run. The most common error message is `Error: connect ECONNREFUSED`, which indicates +that {kib} could not connect to the Chromium process. + +To troubleshoot the problem, start the {kib} server with environment variables that tell Chromium to print verbose logs. See the +<> for more information. + [float] -=== Text is rendered incorrectly in generated reports +[[reporting-troubleshooting-text-incorrect]] +=== Text rendered incorrectly in generated reports If a report label is rendered as an empty rectangle, no system fonts are available. Install at least one font package on the system. If the report is missing certain Chinese, Japanese or Korean characters, ensure that a system font with those characters is installed. [float] +[[reporting-troubleshooting-missing-data]] === Missing data in PDF report of data table visualization There is currently a known limitation with the Data Table visualization that only the first page of data rows, which are the only data visible on the screen, are shown in PDF reports. [float] +[[reporting-troubleshooting-file-permissions]] === File permissions Ensure that the `headless_shell` binary located in your Kibana data directory is owned by the user who is running Kibana, that the user has the execute permission, and if applicable, that the filesystem is mounted with the `exec` option. @@ -63,25 +82,25 @@ Whenever possible, a Reporting error message tries to be as self-explanatory as along with the solution. [float] -==== "Max attempts reached" +==== Max attempts reached There are two primary causes of this error: -. You're creating a PDF of a visualization or dashboard that spans a large amount of data and Kibana is hitting the `xpack.reporting.queue.timeout` +* You're creating a PDF of a visualization or dashboard that spans a large amount of data and Kibana is hitting the `xpack.reporting.queue.timeout` -. Kibana is hosted behind a reverse-proxy, and the <> are not configured correctly +* Kibana is hosted behind a reverse-proxy, and the <> are not configured correctly Create a Markdown visualization and then create a PDF report. If this succeeds, increase the `xpack.reporting.queue.timeout` setting. If the PDF report fails with "Max attempts reached," check your <>. [float] [[reporting-troubleshooting-nss-dependency]] -==== "You must install nss for Reporting to work" +==== You must install nss for Reporting to work Reporting using the Chromium browser relies on the Network Security Service libraries (NSS). Install the appropriate nss package for your distribution. [float] [[reporting-troubleshooting-sandbox-dependency]] -==== "Unable to use Chromium sandbox" +==== Unable to use Chromium sandbox Chromium uses sandboxing techniques that are built on top of operating system primitives. The Linux sandbox depends on user namespaces, which were introduced with the 3.8 Linux kernel. However, many distributions don't have user namespaces enabled by default, or they require the CAP_SYS_ADMIN capability. @@ -90,6 +109,7 @@ Elastic recommends that you research the feasibility of enabling unprivileged us is if you are running Kibana in Docker because the container runs in a user namespace with the built-in seccomp/bpf filters. [float] +[[reporting-troubleshooting-verbose-logs]] === Verbose logs {kib} server logs have a lot of useful information for troubleshooting and understanding how things work. If you're having any issues at all, the full logs from Reporting will be the first place to look. In `kibana.yml`: @@ -101,10 +121,12 @@ logging.verbose: true For more information about logging, see <>. +[float] +[[reporting-troubleshooting-puppeteer-debug-logs]] === Puppeteer debug logs The Chromium browser that {kib} launches on the server is driven by a NodeJS library for Chromium called Puppeteer. The Puppeteer library has its own command-line method to generate its own debug logs, which can sometimes be helpful, particularly to figure out if a problem is -caused by Kibana or Chromium. See more at https://github.com/GoogleChrome/puppeteer/blob/v1.19.0/README.md#debugging-tips +caused by Kibana or Chromium. See more at https://github.com/GoogleChrome/puppeteer/blob/v1.19.0/README.md#debugging-tips[debugging tips]. Using Puppeteer's debug method when launching Kibana would look like: ``` @@ -114,3 +136,14 @@ The internal DevTools protocol traffic will be logged via the `debug` module und The Puppeteer logs are very verbose and could possibly contain sensitive information. Handle the generated output with care. + +[float] +[[reporting-troubleshooting-system-requirements]] +=== System requirements +In Elastic Cloud, the {kib} instances that most configurations provide by default is for 1GB of RAM for the instance. That is enough for +{kib} Reporting when the visualization or dashboard is relatively simple, such as a single pie chart or a dashboard with +a few visualizations. However, certain visualization types incur more load than others. For example, a TSVB panel has a lot of network +requests to render. + +If the {kib} instance doesn't have enough memory to run the report, the report fails with an error such as `Error: Page crashed!` +In this case, try increasing the memory for the {kib} instance to 2GB. diff --git a/packages/kbn-analytics/src/report.ts b/packages/kbn-analytics/src/report.ts index 1c0b37966355f..16c0a3069e5fd 100644 --- a/packages/kbn-analytics/src/report.ts +++ b/packages/kbn-analytics/src/report.ts @@ -78,6 +78,7 @@ export class ReportManager { } assignReports(newMetrics: Metric | Metric[]) { wrapArray(newMetrics).forEach(newMetric => this.assignReport(this.report, newMetric)); + return { report: this.report }; } static createMetricKey(metric: Metric): string { switch (metric.type) { @@ -101,7 +102,7 @@ export class ReportManager { case METRIC_TYPE.USER_AGENT: { const { appName, type, userAgent } = metric; if (userAgent) { - this.report.userAgent = { + report.userAgent = { [key]: { key, appName, @@ -110,23 +111,22 @@ export class ReportManager { }, }; } + return; } case METRIC_TYPE.CLICK: case METRIC_TYPE.LOADED: case METRIC_TYPE.COUNT: { const { appName, type, eventName, count } = metric; - if (report.uiStatsMetrics) { - const existingStats = (report.uiStatsMetrics[key] || {}).stats; - this.report.uiStatsMetrics = this.report.uiStatsMetrics || {}; - this.report.uiStatsMetrics[key] = { - key, - appName, - eventName, - type, - stats: this.incrementStats(count, existingStats), - }; - } + report.uiStatsMetrics = report.uiStatsMetrics || {}; + const existingStats = (report.uiStatsMetrics[key] || {}).stats; + report.uiStatsMetrics[key] = { + key, + appName, + eventName, + type, + stats: this.incrementStats(count, existingStats), + }; return; } default: diff --git a/src/legacy/core_plugins/console/public/kibana.json b/src/legacy/core_plugins/console/public/kibana.json index 3363af353912a..c58a5a90fb9f2 100644 --- a/src/legacy/core_plugins/console/public/kibana.json +++ b/src/legacy/core_plugins/console/public/kibana.json @@ -3,5 +3,6 @@ "version": "kibana", "server": true, "ui": true, - "requiredPlugins": ["home"] + "requiredPlugins": ["home"], + "optionalPlugins": ["usageCollection"] } diff --git a/src/legacy/core_plugins/console/public/legacy.ts b/src/legacy/core_plugins/console/public/legacy.ts index c456d777187aa..d151a27d27e5c 100644 --- a/src/legacy/core_plugins/console/public/legacy.ts +++ b/src/legacy/core_plugins/console/public/legacy.ts @@ -22,7 +22,13 @@ import { I18nContext } from 'ui/i18n'; import chrome from 'ui/chrome'; import { FeatureCatalogueCategory } from 'ui/registry/feature_catalogue'; +import { plugin } from './np_ready'; +import { DevToolsSetup } from '../../../../plugins/dev_tools/public'; +import { HomePublicPluginSetup } from '../../../../plugins/home/public'; +import { UsageCollectionSetup } from '../../../../plugins/usage_collection/public'; + export interface XPluginSet { + usageCollection: UsageCollectionSetup; dev_tools: DevToolsSetup; home: HomePublicPluginSetup; __LEGACY: { @@ -32,10 +38,6 @@ export interface XPluginSet { }; } -import { plugin } from './np_ready'; -import { DevToolsSetup } from '../../../../plugins/dev_tools/public'; -import { HomePublicPluginSetup } from '../../../../plugins/home/public'; - const pluginInstance = plugin({} as any); (async () => { diff --git a/src/legacy/core_plugins/console/public/np_ready/application/containers/editor/legacy/console_editor/editor.test.mock.tsx b/src/legacy/core_plugins/console/public/np_ready/application/containers/editor/legacy/console_editor/editor.test.mock.tsx index 5df72c0f03496..0ee7998d331f5 100644 --- a/src/legacy/core_plugins/console/public/np_ready/application/containers/editor/legacy/console_editor/editor.test.mock.tsx +++ b/src/legacy/core_plugins/console/public/np_ready/application/containers/editor/legacy/console_editor/editor.test.mock.tsx @@ -22,6 +22,7 @@ jest.mock('../../../../contexts/editor_context/editor_registry.ts', () => ({ setInputEditor: () => {}, getInputEditor: () => ({ getRequestsInRange: async () => [{ test: 'test' }], + getCoreEditor: () => ({ getCurrentPosition: jest.fn() }), }), }, })); @@ -52,3 +53,6 @@ jest.mock('../../../../models/sense_editor', () => { jest.mock('../../../../hooks/use_send_current_request_to_es/send_request_to_es', () => ({ sendRequestToES: jest.fn(), })); +jest.mock('../../../../../lib/autocomplete/get_endpoint_from_position', () => ({ + getEndpointFromPosition: jest.fn(), +})); diff --git a/src/legacy/core_plugins/console/public/np_ready/application/containers/editor/legacy/console_editor/editor.test.tsx b/src/legacy/core_plugins/console/public/np_ready/application/containers/editor/legacy/console_editor/editor.test.tsx index 6162397ce0650..73ee6d160613f 100644 --- a/src/legacy/core_plugins/console/public/np_ready/application/containers/editor/legacy/console_editor/editor.test.tsx +++ b/src/legacy/core_plugins/console/public/np_ready/application/containers/editor/legacy/console_editor/editor.test.tsx @@ -32,14 +32,18 @@ import { ServicesContextProvider, EditorContextProvider, RequestContextProvider, + ContextValue, } from '../../../../contexts'; +// Mocked functions import { sendRequestToES } from '../../../../hooks/use_send_current_request_to_es/send_request_to_es'; +import { getEndpointFromPosition } from '../../../../../lib/autocomplete/get_endpoint_from_position'; + import * as consoleMenuActions from '../console_menu_actions'; import { Editor } from './editor'; describe('Legacy (Ace) Console Editor Component Smoke Test', () => { - let mockedAppContextValue: any; + let mockedAppContextValue: ContextValue; const sandbox = sinon.createSandbox(); const doMount = () => @@ -58,11 +62,15 @@ describe('Legacy (Ace) Console Editor Component Smoke Test', () => { beforeEach(() => { document.queryCommandSupported = sinon.fake(() => true); mockedAppContextValue = { + elasticsearchUrl: 'test', services: { + trackUiMetric: { count: () => {}, load: () => {} }, + settings: {} as any, + storage: {} as any, history: { - getSavedEditorState: () => null, + getSavedEditorState: () => ({} as any), updateCurrentState: jest.fn(), - }, + } as any, notifications: notificationServiceMock.createSetupContract(), }, docLinkVersion: 'NA', @@ -70,10 +78,12 @@ describe('Legacy (Ace) Console Editor Component Smoke Test', () => { }); afterEach(() => { + jest.clearAllMocks(); sandbox.restore(); }); it('calls send current request to ES', async () => { + (getEndpointFromPosition as jest.Mock).mockReturnValue({ patterns: [] }); (sendRequestToES as jest.Mock).mockRejectedValue({}); const editor = doMount(); act(() => { diff --git a/src/legacy/core_plugins/console/public/np_ready/application/containers/editor/legacy/console_menu_actions.ts b/src/legacy/core_plugins/console/public/np_ready/application/containers/editor/legacy/console_menu_actions.ts index 797ff5744eec3..2bbe49cd53eac 100644 --- a/src/legacy/core_plugins/console/public/np_ready/application/containers/editor/legacy/console_menu_actions.ts +++ b/src/legacy/core_plugins/console/public/np_ready/application/containers/editor/legacy/console_menu_actions.ts @@ -17,8 +17,7 @@ * under the License. */ -// @ts-ignore -import { getEndpointFromPosition } from '../../../../lib/autocomplete/autocomplete'; +import { getEndpointFromPosition } from '../../../../lib/autocomplete/get_endpoint_from_position'; import { SenseEditor } from '../../../models/sense_editor'; export async function autoIndent(editor: SenseEditor, event: Event) { @@ -40,7 +39,7 @@ export function getDocumentation( } const position = requests[0].range.end; position.column = position.column - 1; - const endpoint = getEndpointFromPosition(editor, position, editor.parser); + const endpoint = getEndpointFromPosition(editor.getCoreEditor(), position, editor.parser); if (endpoint && endpoint.documentation && endpoint.documentation.indexOf('http') !== -1) { return endpoint.documentation .replace('/master/', `/${docLinkVersion}/`) diff --git a/src/legacy/core_plugins/console/public/np_ready/application/contexts/index.ts b/src/legacy/core_plugins/console/public/np_ready/application/contexts/index.ts index 18234acf15957..e489bd50c9ce0 100644 --- a/src/legacy/core_plugins/console/public/np_ready/application/contexts/index.ts +++ b/src/legacy/core_plugins/console/public/np_ready/application/contexts/index.ts @@ -17,7 +17,7 @@ * under the License. */ -export { useServicesContext, ServicesContextProvider } from './services_context'; +export { useServicesContext, ServicesContextProvider, ContextValue } from './services_context'; export { useRequestActionContext, diff --git a/src/legacy/core_plugins/console/public/np_ready/application/contexts/services_context.tsx b/src/legacy/core_plugins/console/public/np_ready/application/contexts/services_context.tsx index 77f0924a51842..f14685ecd4ac7 100644 --- a/src/legacy/core_plugins/console/public/np_ready/application/contexts/services_context.tsx +++ b/src/legacy/core_plugins/console/public/np_ready/application/contexts/services_context.tsx @@ -20,13 +20,15 @@ import React, { createContext, useContext } from 'react'; import { NotificationsSetup } from 'kibana/public'; import { History, Storage, Settings } from '../../services'; +import { MetricsTracker } from '../../types'; -interface ContextValue { +export interface ContextValue { services: { history: History; storage: Storage; settings: Settings; notifications: NotificationsSetup; + trackUiMetric: MetricsTracker; }; elasticsearchUrl: string; docLinkVersion: string; diff --git a/src/legacy/core_plugins/console/public/np_ready/application/hooks/use_send_current_request_to_es/send_request_to_es.ts b/src/legacy/core_plugins/console/public/np_ready/application/hooks/use_send_current_request_to_es/send_request_to_es.ts index 11c1f6638e9cf..10dab65b61d44 100644 --- a/src/legacy/core_plugins/console/public/np_ready/application/hooks/use_send_current_request_to_es/send_request_to_es.ts +++ b/src/legacy/core_plugins/console/public/np_ready/application/hooks/use_send_current_request_to_es/send_request_to_es.ts @@ -39,7 +39,8 @@ export interface ESRequestResult { } let CURRENT_REQ_ID = 0; -export function sendRequestToES({ requests }: EsRequestArgs): Promise { +export function sendRequestToES(args: EsRequestArgs): Promise { + const requests = args.requests.slice(); return new Promise((resolve, reject) => { const reqId = ++CURRENT_REQ_ID; const results: ESRequestResult[] = []; diff --git a/src/legacy/core_plugins/console/public/np_ready/application/hooks/use_send_current_request_to_es/track.ts b/src/legacy/core_plugins/console/public/np_ready/application/hooks/use_send_current_request_to_es/track.ts new file mode 100644 index 0000000000000..4d993512c8fa7 --- /dev/null +++ b/src/legacy/core_plugins/console/public/np_ready/application/hooks/use_send_current_request_to_es/track.ts @@ -0,0 +1,40 @@ +/* + * 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 { SenseEditor } from '../../models/sense_editor'; +import { getEndpointFromPosition } from '../../../lib/autocomplete/get_endpoint_from_position'; +import { MetricsTracker } from '../../../types'; + +export const track = (requests: any[], editor: SenseEditor, trackUiMetric: MetricsTracker) => { + const coreEditor = editor.getCoreEditor(); + // `getEndpointFromPosition` gets values from the server-side generated JSON files which + // are a combination of JS, automatically generated JSON and manual overrides. That means + // the metrics reported from here will be tied to the definitions in those files. + // See src/legacy/core_plugins/console/server/api_server/spec + const endpointDescription = getEndpointFromPosition( + coreEditor, + coreEditor.getCurrentPosition(), + editor.parser + ); + + if (requests[0] && endpointDescription) { + const eventName = `${requests[0].method}_${endpointDescription.id ?? 'unknown'}`; + trackUiMetric.count(eventName); + } +}; diff --git a/src/legacy/core_plugins/console/public/np_ready/application/hooks/use_send_current_request_to_es/use_send_current_request_to_es.ts b/src/legacy/core_plugins/console/public/np_ready/application/hooks/use_send_current_request_to_es/use_send_current_request_to_es.ts index b51c29f8e9db6..6bf0b5024376b 100644 --- a/src/legacy/core_plugins/console/public/np_ready/application/hooks/use_send_current_request_to_es/use_send_current_request_to_es.ts +++ b/src/legacy/core_plugins/console/public/np_ready/application/hooks/use_send_current_request_to_es/use_send_current_request_to_es.ts @@ -19,15 +19,16 @@ import { i18n } from '@kbn/i18n'; import { useCallback } from 'react'; import { instance as registry } from '../../contexts/editor_context/editor_registry'; -import { useServicesContext } from '../../contexts'; +import { useRequestActionContext, useServicesContext } from '../../contexts'; import { sendRequestToES } from './send_request_to_es'; -import { useRequestActionContext } from '../../contexts'; +import { track } from './track'; + // @ts-ignore import mappings from '../../../lib/mappings/mappings'; export const useSendCurrentRequestToES = () => { const { - services: { history, settings, notifications }, + services: { history, settings, notifications, trackUiMetric }, } = useServicesContext(); const dispatch = useRequestActionContext(); @@ -45,9 +46,10 @@ export const useSendCurrentRequestToES = () => { return; } - const results = await sendRequestToES({ - requests, - }); + // Fire and forget + setTimeout(() => track(requests, editor, trackUiMetric), 0); + + const results = await sendRequestToES({ requests }); results.forEach(({ request: { path, method, data } }) => { history.addToHistory(path, method, data); @@ -82,5 +84,5 @@ export const useSendCurrentRequestToES = () => { }); } } - }, [dispatch, settings, history, notifications]); + }, [dispatch, settings, history, notifications, trackUiMetric]); }; diff --git a/src/legacy/core_plugins/console/public/np_ready/application/index.tsx b/src/legacy/core_plugins/console/public/np_ready/application/index.tsx index 239e4320f00f8..89756513b2b22 100644 --- a/src/legacy/core_plugins/console/public/np_ready/application/index.tsx +++ b/src/legacy/core_plugins/console/public/np_ready/application/index.tsx @@ -22,6 +22,7 @@ import { NotificationsSetup } from 'kibana/public'; import { ServicesContextProvider, EditorContextProvider, RequestContextProvider } from './contexts'; import { Main } from './containers'; import { createStorage, createHistory, createSettings, Settings } from '../services'; +import { createUsageTracker } from '../services/tracker'; let settingsRef: Settings; export function legacyBackDoorToSettings() { @@ -36,6 +37,9 @@ export function boot(deps: { }) { const { I18nContext, notifications, docLinkVersion, elasticsearchUrl } = deps; + const trackUiMetric = createUsageTracker(); + trackUiMetric.load('opened_app'); + const storage = createStorage({ engine: window.localStorage, prefix: 'sense:', @@ -50,7 +54,13 @@ export function boot(deps: { value={{ elasticsearchUrl, docLinkVersion, - services: { storage, history, settings, notifications }, + services: { + storage, + history, + settings, + notifications, + trackUiMetric, + }, }} > diff --git a/src/legacy/core_plugins/console/public/np_ready/application/models/sense_editor/index.ts b/src/legacy/core_plugins/console/public/np_ready/application/models/sense_editor/index.ts index 9310de2724fbe..f2102d75685fd 100644 --- a/src/legacy/core_plugins/console/public/np_ready/application/models/sense_editor/index.ts +++ b/src/legacy/core_plugins/console/public/np_ready/application/models/sense_editor/index.ts @@ -21,3 +21,4 @@ export * from './create'; export * from '../legacy_core_editor/create_readonly'; export { MODE } from '../../../lib/row_parser'; export { SenseEditor } from './sense_editor'; +export { getEndpointFromPosition } from '../../../lib/autocomplete/get_endpoint_from_position'; diff --git a/src/legacy/core_plugins/console/public/np_ready/lib/autocomplete/autocomplete.ts b/src/legacy/core_plugins/console/public/np_ready/lib/autocomplete/autocomplete.ts index 7520807ca77f5..ac8fa1ea48caa 100644 --- a/src/legacy/core_plugins/console/public/np_ready/lib/autocomplete/autocomplete.ts +++ b/src/legacy/core_plugins/console/public/np_ready/lib/autocomplete/autocomplete.ts @@ -38,7 +38,6 @@ import { URL_PATH_END_MARKER } from './components/index'; import { createTokenIterator } from '../../application/factories'; import { Position, Token, Range, CoreEditor } from '../../types'; -import { SenseEditor } from '../../application/models/sense_editor'; let LAST_EVALUATED_TOKEN: any = null; @@ -54,11 +53,20 @@ function isUrlParamsToken(token: any) { return false; } } -function getCurrentMethodAndTokenPaths( + +/** + * Get the method and token paths for a specific position in the current editor buffer. + * + * This function can be used for getting autocomplete information or for getting more information + * about the endpoint associated with autocomplete. In future, these concerns should be better + * separated. + * + */ +export function getCurrentMethodAndTokenPaths( editor: CoreEditor, pos: Position, parser: any, - forceEndOfUrl?: boolean + forceEndOfUrl?: boolean /* Flag for indicating whether we want to avoid early escape optimization. */ ) { const tokenIter = createTokenIterator({ editor, @@ -186,7 +194,7 @@ function getCurrentMethodAndTokenPaths( } } - if (walkedSomeBody && (!bodyTokenPath || bodyTokenPath.length === 0)) { + if (walkedSomeBody && (!bodyTokenPath || bodyTokenPath.length === 0) && !forceEndOfUrl) { // we had some content and still no path -> the cursor is position after a closed body -> no auto complete return {}; } @@ -298,20 +306,6 @@ function getCurrentMethodAndTokenPaths( } return ret; } -export function getEndpointFromPosition(senseEditor: SenseEditor, pos: Position, parser: any) { - const editor = senseEditor.getCoreEditor(); - const context = { - ...getCurrentMethodAndTokenPaths( - editor, - { column: pos.column, lineNumber: pos.lineNumber }, - parser, - true - ), - }; - const components = getTopLevelUrlCompleteComponents(context.method); - populateContext(context.urlTokenPath, context, editor, true, components); - return context.endpoint; -} // eslint-disable-next-line export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor; parser: any }) { @@ -812,7 +806,6 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor if (!ret.urlTokenPath) { // zero length tokenPath is true - // console.log("Can't extract a valid url token path."); return context; } @@ -825,13 +818,11 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor ); if (!context.endpoint) { - // console.log("couldn't resolve an endpoint."); return context; } if (!ret.urlParamsTokenPath) { // zero length tokenPath is true - // console.log("Can't extract a valid urlParams token path."); return context; } let tokenPath: any[] = []; @@ -859,7 +850,6 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor context.requestStartRow = ret.requestStartRow; if (!ret.urlTokenPath) { // zero length tokenPath is true - // console.log("Can't extract a valid url token path."); return context; } @@ -875,7 +865,6 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor if (!ret.bodyTokenPath) { // zero length tokenPath is true - // console.log("Can't extract a valid body token path."); return context; } diff --git a/src/legacy/core_plugins/console/public/np_ready/lib/autocomplete/get_endpoint_from_position.ts b/src/legacy/core_plugins/console/public/np_ready/lib/autocomplete/get_endpoint_from_position.ts new file mode 100644 index 0000000000000..cb037e29e33f6 --- /dev/null +++ b/src/legacy/core_plugins/console/public/np_ready/lib/autocomplete/get_endpoint_from_position.ts @@ -0,0 +1,41 @@ +/* + * 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 { CoreEditor, Position } from '../../types'; +import { getCurrentMethodAndTokenPaths } from './autocomplete'; + +// @ts-ignore +import { getTopLevelUrlCompleteComponents } from '../kb/kb'; +// @ts-ignore +import { populateContext } from './engine'; + +export function getEndpointFromPosition(editor: CoreEditor, pos: Position, parser: any) { + const lineValue = editor.getLineValue(pos.lineNumber); + const context = { + ...getCurrentMethodAndTokenPaths( + editor, + { column: lineValue.length, lineNumber: pos.lineNumber }, + parser, + true + ), + }; + const components = getTopLevelUrlCompleteComponents(context.method); + populateContext(context.urlTokenPath, context, editor, true, components); + return context.endpoint; +} diff --git a/src/legacy/core_plugins/console/public/np_ready/lib/es/es.js b/src/legacy/core_plugins/console/public/np_ready/lib/es/es.js index 9012b875e0f2b..e36976fb7acee 100644 --- a/src/legacy/core_plugins/console/public/np_ready/lib/es/es.js +++ b/src/legacy/core_plugins/console/public/np_ready/lib/es/es.js @@ -18,7 +18,6 @@ */ import { stringify as formatQueryString } from 'querystring'; - import $ from 'jquery'; const esVersion = []; diff --git a/src/legacy/core_plugins/console/public/np_ready/services/tracker.ts b/src/legacy/core_plugins/console/public/np_ready/services/tracker.ts new file mode 100644 index 0000000000000..13d5f875b3c6f --- /dev/null +++ b/src/legacy/core_plugins/console/public/np_ready/services/tracker.ts @@ -0,0 +1,31 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { METRIC_TYPE } from '@kbn/analytics'; +import { MetricsTracker } from '../types'; +import { createUiStatsReporter } from '../../../../ui_metric/public'; + +const APP_TRACKER_NAME = 'console'; +export const createUsageTracker = (): MetricsTracker => { + const track = createUiStatsReporter(APP_TRACKER_NAME); + return { + count: (eventName: string) => track(METRIC_TYPE.COUNT, eventName), + load: (eventName: string) => track(METRIC_TYPE.LOADED, eventName), + }; +}; diff --git a/src/legacy/core_plugins/console/public/np_ready/types/common.ts b/src/legacy/core_plugins/console/public/np_ready/types/common.ts index ad9ed10d4188f..e44969cd9e80a 100644 --- a/src/legacy/core_plugins/console/public/np_ready/types/common.ts +++ b/src/legacy/core_plugins/console/public/np_ready/types/common.ts @@ -17,6 +17,11 @@ * under the License. */ +export interface MetricsTracker { + count: (eventName: string) => void; + load: (eventName: string) => void; +} + export type BaseResponseType = | 'application/json' | 'text/csv' diff --git a/src/legacy/core_plugins/console/public/np_ready/types/index.ts b/src/legacy/core_plugins/console/public/np_ready/types/index.ts index 9d82237d667b3..78c6b6c8f55cc 100644 --- a/src/legacy/core_plugins/console/public/np_ready/types/index.ts +++ b/src/legacy/core_plugins/console/public/np_ready/types/index.ts @@ -20,3 +20,4 @@ export * from './core_editor'; export * from './token'; export * from './tokens_provider'; +export * from './common'; diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js index 3d4292cef27f4..06424ea48a40f 100644 --- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js +++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js @@ -148,6 +148,8 @@ export const npStart = { legacy: { getSection: () => ({ register: sinon.fake(), + deregister: sinon.fake(), + hasItem: sinon.fake(), }), }, }, diff --git a/src/plugins/inspector/public/views/data/components/download_options.tsx b/src/plugins/inspector/public/views/data/components/download_options.tsx index 6d21dcdafa84d..e7bfbed23c074 100644 --- a/src/plugins/inspector/public/views/data/components/download_options.tsx +++ b/src/plugins/inspector/public/views/data/components/download_options.tsx @@ -20,6 +20,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; import { EuiButton, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } from '@elastic/eui'; import { DataViewColumn, DataViewRow } from '../types'; @@ -66,8 +67,14 @@ class DataDownloadOptions extends Component { + let filename = this.props.title; + if (!filename || filename.length === 0) { + filename = i18n.translate('inspector.data.downloadOptionsUnsavedFilename', { + defaultMessage: 'unsaved', + }); + } exportAsCsv({ - filename: `${this.props.title}.csv`, + filename: `${filename}.csv`, columns: this.props.columns, rows: this.props.rows, csvSeparator: this.props.csvSeparator, diff --git a/test/api_integration/apis/ui_metric/ui_metric.js b/test/api_integration/apis/ui_metric/ui_metric.js index 5b02ba7e72430..5ddbd8649589c 100644 --- a/test/api_integration/apis/ui_metric/ui_metric.js +++ b/test/api_integration/apis/ui_metric/ui_metric.js @@ -25,15 +25,13 @@ export default function({ getService }) { const es = getService('legacyEs'); const createStatsMetric = eventName => ({ - key: ReportManager.createMetricKey({ appName: 'myApp', type: METRIC_TYPE.CLICK, eventName }), eventName, appName: 'myApp', type: METRIC_TYPE.CLICK, - stats: { sum: 1, avg: 1, min: 1, max: 1 }, + count: 1, }); const createUserAgentMetric = appName => ({ - key: ReportManager.createMetricKey({ appName, type: METRIC_TYPE.USER_AGENT }), appName, type: METRIC_TYPE.USER_AGENT, userAgent: @@ -42,12 +40,9 @@ export default function({ getService }) { describe('ui_metric API', () => { it('increments the count field in the document defined by the {app}/{action_type} path', async () => { + const reportManager = new ReportManager(); const uiStatsMetric = createStatsMetric('myEvent'); - const report = { - uiStatsMetrics: { - [uiStatsMetric.key]: uiStatsMetric, - }, - }; + const { report } = reportManager.assignReports([uiStatsMetric]); await supertest .post('/api/ui_metric/report') .set('kbn-xsrf', 'kibana') @@ -61,21 +56,18 @@ export default function({ getService }) { }); it('supports multiple events', async () => { + const reportManager = new ReportManager(); const userAgentMetric = createUserAgentMetric('kibana'); const uiStatsMetric1 = createStatsMetric('myEvent'); const hrTime = process.hrtime(); const nano = hrTime[0] * 1000000000 + hrTime[1]; const uniqueEventName = `myEvent${nano}`; const uiStatsMetric2 = createStatsMetric(uniqueEventName); - const report = { - userAgent: { - [userAgentMetric.key]: userAgentMetric, - }, - uiStatsMetrics: { - [uiStatsMetric1.key]: uiStatsMetric1, - [uiStatsMetric2.key]: uiStatsMetric2, - }, - }; + const { report } = reportManager.assignReports([ + userAgentMetric, + uiStatsMetric1, + uiStatsMetric2, + ]); await supertest .post('/api/ui_metric/report') .set('kbn-xsrf', 'kibana') diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx index 4b6355034f16a..99d8a0790a816 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx @@ -31,11 +31,15 @@ describe('SpanMetadata', () => { name: 'opbeans-java' }, span: { - id: '7efbc7056b746fcb' + id: '7efbc7056b746fcb', + message: { + age: { ms: 1577958057123 }, + queue: { name: 'queue name' } + } } } as unknown) as Span; const output = render(, renderOptions); - expectTextsInDocument(output, ['Service', 'Agent']); + expectTextsInDocument(output, ['Service', 'Agent', 'Message']); }); }); describe('when a span is presented', () => { @@ -55,11 +59,15 @@ describe('SpanMetadata', () => { response: { status_code: 200 } }, subtype: 'http', - type: 'external' + type: 'external', + message: { + age: { ms: 1577958057123 }, + queue: { name: 'queue name' } + } } } as unknown) as Span; const output = render(, renderOptions); - expectTextsInDocument(output, ['Service', 'Agent', 'Span']); + expectTextsInDocument(output, ['Service', 'Agent', 'Span', 'Message']); }); }); describe('when there is no id inside span', () => { @@ -83,7 +91,7 @@ describe('SpanMetadata', () => { } as unknown) as Span; const output = render(, renderOptions); expectTextsInDocument(output, ['Service', 'Agent']); - expectTextsNotInDocument(output, ['Span']); + expectTextsNotInDocument(output, ['Span', 'Message']); }); }); }); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/sections.ts b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/sections.ts index 7012bbcc8fcea..5a83a9bf4ef9e 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/sections.ts +++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/sections.ts @@ -11,7 +11,8 @@ import { SPAN, LABELS, TRANSACTION, - TRACE + TRACE, + MESSAGE_SPAN } from '../sections'; export const SPAN_METADATA_SECTIONS: Section[] = [ @@ -20,5 +21,6 @@ export const SPAN_METADATA_SECTIONS: Section[] = [ TRANSACTION, TRACE, SERVICE, + MESSAGE_SPAN, AGENT ]; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx index 1fcb093fa0354..93e87e884ea76 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx @@ -35,6 +35,10 @@ function getTransaction() { notIncluded: 'transaction not included value', custom: { someKey: 'custom value' + }, + message: { + age: { ms: 1577958057123 }, + queue: { name: 'queue name' } } } } as unknown) as Transaction; @@ -59,7 +63,8 @@ describe('TransactionMetadata', () => { 'Agent', 'URL', 'User', - 'Custom' + 'Custom', + 'Message' ]); }); @@ -81,7 +86,9 @@ describe('TransactionMetadata', () => { 'agent.someKey', 'url.someKey', 'user.someKey', - 'transaction.custom.someKey' + 'transaction.custom.someKey', + 'transaction.message.age.ms', + 'transaction.message.queue.name' ]); // excluded keys @@ -109,7 +116,9 @@ describe('TransactionMetadata', () => { 'agent value', 'url value', 'user value', - 'custom value' + 'custom value', + '1577958057123', + 'queue name' ]); // excluded values @@ -138,7 +147,8 @@ describe('TransactionMetadata', () => { 'Process', 'Agent', 'URL', - 'Custom' + 'Custom', + 'Message' ]); }); }); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/sections.ts b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/sections.ts index 6b30c82bc35a0..18751efc6e1c1 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/sections.ts +++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/sections.ts @@ -18,7 +18,8 @@ import { PAGE, USER, USER_AGENT, - CUSTOM_TRANSACTION + CUSTOM_TRANSACTION, + MESSAGE_TRANSACTION } from '../sections'; export const TRANSACTION_METADATA_SECTIONS: Section[] = [ @@ -29,6 +30,7 @@ export const TRANSACTION_METADATA_SECTIONS: Section[] = [ CONTAINER, SERVICE, PROCESS, + MESSAGE_TRANSACTION, AGENT, URL, { ...PAGE, key: 'transaction.page' }, diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/sections.ts b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/sections.ts index 403663ce2095a..ac8e9559357e3 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/sections.ts +++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/sections.ts @@ -136,3 +136,20 @@ export const CUSTOM_TRANSACTION: Section = { key: 'transaction.custom', label: customLabel }; + +const messageLabel = i18n.translate( + 'xpack.apm.metadataTable.section.messageLabel', + { + defaultMessage: 'Message' + } +); + +export const MESSAGE_TRANSACTION: Section = { + key: 'transaction.message', + label: messageLabel +}; + +export const MESSAGE_SPAN: Section = { + key: 'span.message', + label: messageLabel +}; diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/SpanRaw.ts b/x-pack/legacy/plugins/apm/typings/es_schemas/raw/SpanRaw.ts index 5ba480221c997..60e523f1aa043 100644 --- a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/SpanRaw.ts +++ b/x-pack/legacy/plugins/apm/typings/es_schemas/raw/SpanRaw.ts @@ -40,6 +40,12 @@ export interface SpanRaw extends APMBaseDoc { statement?: string; type?: string; }; + message?: { + queue?: { name: string }; + age?: { ms: number }; + body?: string; + headers?: Record; + }; }; transaction?: { id: string; diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/TransactionRaw.ts b/x-pack/legacy/plugins/apm/typings/es_schemas/raw/TransactionRaw.ts index ce7c11f34a220..4dc5f8c897c26 100644 --- a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/TransactionRaw.ts +++ b/x-pack/legacy/plugins/apm/typings/es_schemas/raw/TransactionRaw.ts @@ -43,6 +43,12 @@ export interface TransactionRaw extends APMBaseDoc { }; type: string; custom?: Record; + message?: { + queue?: { name: string }; + age?: { ms: number }; + body?: string; + headers?: Record; + }; }; // Shared by errors and transactions diff --git a/x-pack/legacy/plugins/console_extensions/spec/overrides/sql.query.json b/x-pack/legacy/plugins/console_extensions/spec/overrides/sql.query.json index 843fba30bb489..c78cfeea8473d 100644 --- a/x-pack/legacy/plugins/console_extensions/spec/overrides/sql.query.json +++ b/x-pack/legacy/plugins/console_extensions/spec/overrides/sql.query.json @@ -19,6 +19,7 @@ "smile" ] }, - "template": "_sql?format=json\n{\n \"query\": \"\"\"\n SELECT * FROM \"${1:TABLE}\"\n \"\"\"\n}\n" + "template": "_sql?format=json\n{\n \"query\": \"\"\"\n SELECT * FROM \"${1:TABLE}\"\n \"\"\"\n}\n", + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/sql-rest-overview.html" } } diff --git a/x-pack/legacy/plugins/infra/common/ecs_allowed_list.ts b/x-pack/legacy/plugins/infra/common/ecs_allowed_list.ts index 1728cd1fa4b45..f1d0577b4cb19 100644 --- a/x-pack/legacy/plugins/infra/common/ecs_allowed_list.ts +++ b/x-pack/legacy/plugins/infra/common/ecs_allowed_list.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { first } from 'lodash'; +import { first, memoize } from 'lodash'; export const ECS_ALLOWED_LIST = [ 'host', @@ -46,8 +46,9 @@ export const DOCKER_ALLOWED_LIST = [ ]; export const AWS_S3_ALLOWED_LIST = ['aws.s3']; +export const AWS_METRICS_ALLOWED_LIST = ['aws.cloudwatch']; -export const getAllowedListForPrefix = (prefix: string) => { +export const getAllowedListForPrefix = memoize((prefix: string) => { const firstPart = first(prefix.split(/\./)); const defaultAllowedList = prefix ? [...ECS_ALLOWED_LIST, prefix] : ECS_ALLOWED_LIST; switch (firstPart) { @@ -61,7 +62,10 @@ export const getAllowedListForPrefix = (prefix: string) => { if (prefix === 'aws.s3_daily_storage') { return [...defaultAllowedList, ...AWS_S3_ALLOWED_LIST]; } + if (prefix === 'aws.metrics') { + return [...defaultAllowedList, ...AWS_METRICS_ALLOWED_LIST]; + } default: return defaultAllowedList; } -}; +}); diff --git a/x-pack/legacy/plugins/infra/server/lib/snapshot/create_timerange_with_interval.ts b/x-pack/legacy/plugins/infra/server/lib/snapshot/create_timerange_with_interval.ts index 6c27e54a78bee..4bb1aefb5b2c7 100644 --- a/x-pack/legacy/plugins/infra/server/lib/snapshot/create_timerange_with_interval.ts +++ b/x-pack/legacy/plugins/infra/server/lib/snapshot/create_timerange_with_interval.ts @@ -12,6 +12,7 @@ import { getMetricsAggregations } from './query_helpers'; import { calculateMetricInterval } from '../../utils/calculate_metric_interval'; import { SnapshotModel, SnapshotModelMetricAggRT } from '../../../common/inventory_models/types'; import { KibanaFramework } from '../adapters/framework/kibana_framework_adapter'; +import { getDatasetForField } from '../../routes/metrics_explorer/lib/get_dataset_for_field'; export const createTimeRangeWithInterval = async ( framework: KibanaFramework, @@ -19,7 +20,7 @@ export const createTimeRangeWithInterval = async ( options: InfraSnapshotRequestOptions ): Promise => { const aggregations = getMetricsAggregations(options); - const modules = aggregationsToModules(aggregations); + const modules = await aggregationsToModules(framework, requestContext, aggregations, options); const interval = (await calculateMetricInterval( framework, @@ -39,21 +40,30 @@ export const createTimeRangeWithInterval = async ( }; }; -const aggregationsToModules = (aggregations: SnapshotModel): string[] => { - return uniq( - Object.values(aggregations) - .reduce((modules, agg) => { - if (SnapshotModelMetricAggRT.is(agg)) { - return modules.concat(Object.values(agg).map(a => a?.field)); - } - return modules; - }, [] as Array) - .filter(v => v) - .map(field => - field! - .split(/\./) - .slice(0, 2) - .join('.') - ) - ) as string[]; +const aggregationsToModules = async ( + framework: KibanaFramework, + requestContext: RequestHandlerContext, + aggregations: SnapshotModel, + options: InfraSnapshotRequestOptions +): Promise => { + const uniqueFields = Object.values(aggregations) + .reduce>((fields, agg) => { + if (SnapshotModelMetricAggRT.is(agg)) { + return uniq(fields.concat(Object.values(agg).map(a => a?.field))); + } + return fields; + }, []) + .filter(v => v) as string[]; + const fields = await Promise.all( + uniqueFields.map( + async field => + await getDatasetForField( + framework, + requestContext, + field as string, + options.sourceConfiguration.metricAlias + ) + ) + ); + return fields.filter(f => f) as string[]; }; diff --git a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts b/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts new file mode 100644 index 0000000000000..66f0ca8fc706a --- /dev/null +++ b/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts @@ -0,0 +1,46 @@ +/* + * 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 { RequestHandlerContext } from 'kibana/server'; +import { KibanaFramework } from '../../../lib/adapters/framework/kibana_framework_adapter'; + +interface EventDatasetHit { + _source: { + event?: { + dataset?: string; + }; + }; +} + +export const getDatasetForField = async ( + framework: KibanaFramework, + requestContext: RequestHandlerContext, + field: string, + indexPattern: string +) => { + const params = { + allowNoIndices: true, + ignoreUnavailable: true, + terminateAfter: 1, + index: indexPattern, + body: { + query: { exists: { field } }, + size: 1, + _source: ['event.dataset'], + }, + }; + + const response = await framework.callWithRequest( + requestContext, + 'search', + params + ); + if (response.hits.total.value === 0) { + return null; + } + + return response.hits.hits?.[0]._source.event?.dataset; +}; diff --git a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts b/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts index 17fc46b41278a..8ab3fdccbe72b 100644 --- a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts +++ b/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { union } from 'lodash'; +import { union, uniq } from 'lodash'; import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; import { KibanaFramework } from '../../../lib/adapters/framework/kibana_framework_adapter'; import { @@ -16,6 +16,7 @@ import { import { createMetricModel } from './create_metrics_model'; import { JsonObject } from '../../../../common/typed_json'; import { calculateMetricInterval } from '../../../utils/calculate_metric_interval'; +import { getDatasetForField } from './get_dataset_for_field'; export const populateSeriesWithTSVBData = ( request: KibanaRequest, @@ -53,6 +54,12 @@ export const populateSeriesWithTSVBData = ( // Create the TSVB model based on the request options const model = createMetricModel(options); + const modules = await Promise.all( + uniq(options.metrics.filter(m => m.field)).map( + async m => + await getDatasetForField(framework, requestContext, m.field as string, options.indexPattern) + ) + ); const calculatedInterval = await calculateMetricInterval( framework, requestContext, @@ -61,14 +68,7 @@ export const populateSeriesWithTSVBData = ( timestampField: options.timerange.field, timerange: options.timerange, }, - options.metrics - .filter(metric => metric.field) - .map(metric => { - return metric - .field!.split(/\./) - .slice(0, 2) - .join('.'); - }) + modules.filter(m => m) as string[] ); if (calculatedInterval) { diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.test.tsx index 82e172b6bd7e2..626ef99ac13aa 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/dimension_panel.test.tsx @@ -610,7 +610,7 @@ describe('IndexPatternDimensionPanel', () => { }); }); - it('should indicate document compatibility with selected field operation', () => { + it('should select the Records field when count is selected', () => { const initialState: IndexPatternPrivateState = { ...state, layers: { @@ -639,12 +639,9 @@ describe('IndexPatternDimensionPanel', () => { .find('button[data-test-subj="lns-indexPatternDimensionIncompatible-count"]') .simulate('click'); - const options = wrapper.find(EuiComboBox).prop('options'); - - expect(options![0]['data-test-subj']).not.toContain('Incompatible'); - options![1].options!.map(option => - expect(option['data-test-subj']).toContain('Incompatible') - ); + const newColumnState = setState.mock.calls[0][0].layers.first.columns.col2; + expect(newColumnState.operationType).toEqual('count'); + expect(newColumnState.sourceField).toEqual('Records'); }); it('should indicate document and field compatibility with selected document operation', () => { diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/popover_editor.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/popover_editor.tsx index c44d63b01c1b3..98773c04db4a6 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/popover_editor.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/dimension_panel/popover_editor.tsx @@ -127,7 +127,7 @@ export function PopoverEditor(props: PopoverEditorProps) { compatibleWithCurrentField ? '' : 'Incompatible' }-${operationType}`, onClick() { - if (!selectedColumn) { + if (!selectedColumn || !compatibleWithCurrentField) { const possibleFields = fieldByOperation[operationType] || []; if (possibleFields.length === 1) { @@ -152,11 +152,6 @@ export function PopoverEditor(props: PopoverEditorProps) { trackUiEvent(`indexpattern_dimension_operation_${operationType}`); return; } - if (!compatibleWithCurrentField) { - setInvalidOperationType(operationType); - trackUiEvent(`indexpattern_dimension_operation_${operationType}`); - return; - } if (incompatibleSelectedOperationType) { setInvalidOperationType(null); } diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.tsx index 15f19bb9d97e6..b58a2d8ca52c7 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.tsx @@ -115,8 +115,12 @@ export function getIndexPatternDatasource({ const indexPatternDatasource: Datasource = { id: 'indexpattern', - initialize(state?: IndexPatternPersistedState) { - return loadInitialState({ state, savedObjectsClient }); + async initialize(state?: IndexPatternPersistedState) { + return loadInitialState({ + state, + savedObjectsClient, + defaultIndexPatternId: core.uiSettings.get('defaultIndex'), + }); }, getPersistableState({ currentIndexPatternId, layers }: IndexPatternPrivateState) { diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.test.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.test.ts index 2fb678aed5a54..e180ab690d418 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.test.ts +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.test.ts @@ -114,8 +114,9 @@ const sampleIndexPatterns = { { name: 'source', type: 'string', - aggregatable: true, - searchable: true, + aggregatable: false, + searchable: false, + scripted: true, aggregationRestrictions: { terms: { agg: 'terms', @@ -196,7 +197,7 @@ describe('loader', () => { expect(cache).toMatchObject(sampleIndexPatterns); }); - it('should not allow full text fields', async () => { + it('should allow scripted, but not full text fields', async () => { const cache = await loadIndexPatterns({ cache: {}, patterns: ['a', 'b'], @@ -286,6 +287,26 @@ describe('loader', () => { }); }); + it('should use the default index pattern id, if provided', async () => { + const state = await loadInitialState({ + defaultIndexPatternId: 'b', + savedObjectsClient: mockClient(), + }); + + expect(state).toMatchObject({ + currentIndexPatternId: 'b', + indexPatternRefs: [ + { id: 'a', title: sampleIndexPatterns.a.title }, + { id: 'b', title: sampleIndexPatterns.b.title }, + ], + indexPatterns: { + b: sampleIndexPatterns.b, + }, + layers: {}, + showEmptyFields: false, + }); + }); + it('should initialize from saved state', async () => { const savedState: IndexPatternPersistedState = { currentIndexPatternId: 'b', diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts index 661c627f3454f..7f46f50786cf4 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts @@ -84,9 +84,11 @@ export async function loadIndexPatterns({ export async function loadInitialState({ state, savedObjectsClient, + defaultIndexPatternId, }: { state?: IndexPatternPersistedState; savedObjectsClient: SavedObjectsClient; + defaultIndexPatternId?: string; }): Promise { const indexPatternRefs = await loadIndexPatternRefs(savedObjectsClient); const requiredPatterns = _.unique( @@ -94,7 +96,7 @@ export async function loadInitialState({ ? Object.values(state.layers) .map(l => l.indexPatternId) .concat(state.currentIndexPatternId) - : [indexPatternRefs[0].id] + : [defaultIndexPatternId || indexPatternRefs[0].id] ); const currentIndexPatternId = requiredPatterns[0]; @@ -280,7 +282,7 @@ function fromSavedObject( type, title: attributes.title, fields: (JSON.parse(attributes.fields) as IndexPatternField[]) - .filter(({ aggregatable }) => !!aggregatable) + .filter(({ aggregatable, scripted }) => !!aggregatable || !!scripted) .concat(documentField), typeMeta: attributes.typeMeta ? (JSON.parse(attributes.typeMeta) as SavedRestrictionsInfo) diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/types.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/types.ts index 9ed5083633314..50478515d19ce 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/types.ts +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/types.ts @@ -39,6 +39,7 @@ export interface IndexPatternField { type: string; esTypes?: string[]; aggregatable: boolean; + scripted?: boolean; searchable: boolean; aggregationRestrictions?: AggregationRestrictions; } diff --git a/x-pack/legacy/plugins/maps/common/constants.js b/x-pack/legacy/plugins/maps/common/constants.js index b97845a458d51..6e7776d43f4d4 100644 --- a/x-pack/legacy/plugins/maps/common/constants.js +++ b/x-pack/legacy/plugins/maps/common/constants.js @@ -23,6 +23,7 @@ export const EMS_TILES_VECTOR_TILE_PATH = 'ems/tiles/vector/tile'; export const MAP_SAVED_OBJECT_TYPE = 'map'; export const APP_ID = 'maps'; export const APP_ICON = 'gisApp'; +export const TELEMETRY_TYPE = 'maps-telemetry'; export const MAP_APP_PATH = `app/${APP_ID}`; export const GIS_API_PATH = `api/${APP_ID}`; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/ems_tms_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/ems_tms_source.js index 8850c4c07ab73..76ecc18f2f7d7 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/ems_tms_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/ems_tms_source.js @@ -11,7 +11,8 @@ import { AbstractTMSSource } from '../tms_source'; import { VectorTileLayer } from '../../vector_tile_layer'; import { getEMSClient } from '../../../meta'; -import { EMSTMSCreateSourceEditor } from './create_source_editor'; +import { TileServiceSelect } from './tile_service_select'; +import { UpdateSourceEditor } from './update_source_editor'; import { i18n } from '@kbn/i18n'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; import { EMS_TMS } from '../../../../common/constants'; @@ -41,7 +42,7 @@ export class EMSTMSSource extends AbstractTMSSource { onPreviewSource(source); }; - return ; + return ; } constructor(descriptor, inspectorAdapters) { @@ -55,6 +56,10 @@ export class EMSTMSSource extends AbstractTMSSource { ); } + renderSourceSettingsEditor({ onChange }) { + return ; + } + async getImmutableProperties() { const displayName = await this.getDisplayName(); const autoSelectMsg = i18n.translate('xpack.maps.source.emsTile.isAutoSelectLabel', { @@ -78,7 +83,7 @@ export class EMSTMSSource extends AbstractTMSSource { async _getEMSTMSService() { const emsClient = getEMSClient(); const emsTMSServices = await emsClient.getTMSServices(); - const emsTileLayerId = this._getEmsTileLayerId(); + const emsTileLayerId = this.getTileLayerId(); const tmsService = emsTMSServices.find(tmsService => tmsService.getId() === emsTileLayerId); if (!tmsService) { throw new Error( @@ -110,7 +115,7 @@ export class EMSTMSSource extends AbstractTMSSource { const emsTMSService = await this._getEMSTMSService(); return emsTMSService.getDisplayName(); } catch (error) { - return this._getEmsTileLayerId(); + return this.getTileLayerId(); } } @@ -129,7 +134,7 @@ export class EMSTMSSource extends AbstractTMSSource { } getSpriteNamespacePrefix() { - return 'ems/' + this._getEmsTileLayerId(); + return 'ems/' + this.getTileLayerId(); } async getVectorStyleSheetAndSpriteMeta(isRetina) { @@ -142,7 +147,7 @@ export class EMSTMSSource extends AbstractTMSSource { }; } - _getEmsTileLayerId() { + getTileLayerId() { if (!this._descriptor.isAutoSelect) { return this._descriptor.id; } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/create_source_editor.js b/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/tile_service_select.js similarity index 52% rename from x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/create_source_editor.js rename to x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/tile_service_select.js index 65986d5bc93df..337fc7aa46693 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/create_source_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/tile_service_select.js @@ -13,74 +13,80 @@ import { i18n } from '@kbn/i18n'; export const AUTO_SELECT = 'auto_select'; -export class EMSTMSCreateSourceEditor extends React.Component { +export class TileServiceSelect extends React.Component { state = { - emsTmsOptionsRaw: null, + emsTmsOptions: [], + hasLoaded: false, }; + componentWillUnmount() { + this._isMounted = false; + } + + componentDidMount() { + this._isMounted = true; + this._loadTmsOptions(); + } + _loadTmsOptions = async () => { const emsClient = getEMSClient(); const emsTMSServices = await emsClient.getTMSServices(); - const options = emsTMSServices.map(tmsService => { + + if (!this._isMounted) { + return; + } + + const emsTmsOptions = emsTMSServices.map(tmsService => { return { - id: tmsService.getId(), - name: tmsService.getDisplayName(), + value: tmsService.getId(), + text: tmsService.getDisplayName() ? tmsService.getDisplayName() : tmsService.getId(), }; }); - options.unshift({ - id: AUTO_SELECT, - name: i18n.translate('xpack.maps.source.emsTile.autoLabel', { + emsTmsOptions.unshift({ + value: AUTO_SELECT, + text: i18n.translate('xpack.maps.source.emsTile.autoLabel', { defaultMessage: 'Autoselect based on Kibana theme', }), }); - if (this._isMounted) { - this.setState({ - emsTmsOptionsRaw: options, - }); - } + this.setState({ emsTmsOptions, hasLoaded: true }); }; - _onEmsTileServiceChange = e => { + _onChange = e => { const value = e.target.value; const isAutoSelect = value === AUTO_SELECT; - this.props.onSourceConfigChange({ + this.props.onTileSelect({ id: isAutoSelect ? null : value, isAutoSelect, }); }; - componentWillUnmount() { - this._isMounted = false; - } - - componentDidMount() { - this._isMounted = true; - this._loadTmsOptions(); - } - render() { - if (!this.state.emsTmsOptionsRaw) { - // TODO display loading message - return null; - } + const helpText = + this.state.hasLoaded && this.state.emsTmsOptions.length === 0 + ? getEmsUnavailableMessage() + : null; - const emsTileOptions = this.state.emsTmsOptionsRaw.map(service => ({ - value: service.id, - text: service.name || service.id, - })); + let selectedId; + if (this.props.config) { + selectedId = this.props.config.isAutoSelect ? AUTO_SELECT : this.props.config.id; + } return ( ); diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/update_source_editor.js b/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/update_source_editor.js new file mode 100644 index 0000000000000..4d567b8dbb32a --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/update_source_editor.js @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment } from 'react'; +import { EuiTitle, EuiPanel, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { TileServiceSelect } from './tile_service_select'; + +export function UpdateSourceEditor({ onChange, config }) { + const _onTileSelect = ({ id, isAutoSelect }) => { + onChange({ propName: 'id', value: id }); + onChange({ propName: 'isAutoSelect', value: isAutoSelect }); + }; + + return ( + + + +
+ +
+
+ + + + +
+ + +
+ ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js new file mode 100644 index 0000000000000..5e0f7434b04d0 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js @@ -0,0 +1,62 @@ +/* + * 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 _ from 'lodash'; +import React, { Fragment } from 'react'; +import { FieldSelect } from '../field_select'; +import { ColorRampSelect } from './color_ramp_select'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; + +export function DynamicColorForm({ + fields, + onDynamicStyleChange, + staticDynamicSelect, + styleProperty, +}) { + const styleOptions = styleProperty.getOptions(); + + const onFieldChange = ({ field }) => { + onDynamicStyleChange(styleProperty.getStyleName(), { ...styleOptions, field }); + }; + + const onColorChange = colorOptions => { + onDynamicStyleChange(styleProperty.getStyleName(), { + ...styleOptions, + ...colorOptions, + }); + }; + + let colorRampSelect; + if (styleOptions.field && styleOptions.field.name) { + colorRampSelect = ( + + ); + } + + return ( + + + {staticDynamicSelect} + + + + + + {colorRampSelect} + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_selection.js deleted file mode 100644 index 84327635f2b65..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_selection.js +++ /dev/null @@ -1,48 +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 _ from 'lodash'; -import React, { Fragment } from 'react'; -import PropTypes from 'prop-types'; -import { dynamicColorShape } from '../style_option_shapes'; -import { FieldSelect, fieldShape } from '../field_select'; -import { ColorRampSelect } from './color_ramp_select'; -import { EuiSpacer } from '@elastic/eui'; - -export function DynamicColorSelection({ fields, onChange, styleOptions }) { - const onFieldChange = ({ field }) => { - onChange({ ...styleOptions, field }); - }; - - const onColorChange = colorOptions => { - onChange({ ...styleOptions, ...colorOptions }); - }; - - return ( - - - - - - ); -} - -DynamicColorSelection.propTypes = { - fields: PropTypes.arrayOf(fieldShape).isRequired, - styleOptions: dynamicColorShape.isRequired, - onChange: PropTypes.func.isRequired, -}; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/static_color_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/static_color_form.js new file mode 100644 index 0000000000000..48befa1ca74c0 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/static_color_form.js @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiColorPicker, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +export function StaticColorForm({ + onStaticStyleChange, + staticDynamicSelect, + styleProperty, + swatches, +}) { + const onColorChange = color => { + onStaticStyleChange(styleProperty.getStyleName(), { color }); + }; + + return ( + + {staticDynamicSelect} + + + + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/static_color_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/static_color_selection.js deleted file mode 100644 index e42b582dc3929..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/static_color_selection.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import PropTypes from 'prop-types'; -import { EuiColorPicker } from '@elastic/eui'; -import { staticColorShape } from '../style_option_shapes'; - -export function StaticColorSelection({ onChange, styleOptions, swatches }) { - const onColorChange = color => { - onChange({ color }); - }; - - return ( - - ); -} - -StaticColorSelection.propTypes = { - styleOptions: staticColorShape.isRequired, - onChange: PropTypes.func.isRequired, -}; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js index c7745fa69a82f..43e7050b3d1d2 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js @@ -6,21 +6,29 @@ import React from 'react'; -import { StaticDynamicStyleRow } from '../static_dynamic_style_row'; -import { DynamicColorSelection } from './dynamic_color_selection'; -import { StaticColorSelection } from './static_color_selection'; +import { StylePropEditor } from '../style_prop_editor'; +import { DynamicColorForm } from './dynamic_color_form'; +import { StaticColorForm } from './static_color_form'; +import { i18n } from '@kbn/i18n'; export function VectorStyleColorEditor(props) { + const colorForm = props.styleProperty.isDynamic() ? ( + + ) : ( + + ); + return ( - + + {colorForm} + ); } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_form.js new file mode 100644 index 0000000000000..bad13b487cc29 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_form.js @@ -0,0 +1,37 @@ +/* + * 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 _ from 'lodash'; +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { FieldSelect } from '../field_select'; + +export function DynamicLabelForm({ + fields, + onDynamicStyleChange, + staticDynamicSelect, + styleProperty, +}) { + const styleOptions = styleProperty.getOptions(); + + const onFieldChange = ({ field }) => { + onDynamicStyleChange(styleProperty.getStyleName(), { ...styleOptions, field }); + }; + + return ( + + {staticDynamicSelect} + + + + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_selector.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_selector.js deleted file mode 100644 index e393341b90696..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_selector.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import _ from 'lodash'; -import React from 'react'; -import { FieldSelect } from '../field_select'; - -export function DynamicLabelSelector({ fields, styleOptions, onChange }) { - const onFieldChange = ({ field }) => { - onChange({ ...styleOptions, field }); - }; - - return ( - - ); -} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/static_label_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/static_label_form.js new file mode 100644 index 0000000000000..721487b5d8ff0 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/static_label_form.js @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFieldText, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +export function StaticLabelForm({ onStaticStyleChange, staticDynamicSelect, styleProperty }) { + const onValueChange = event => { + onStaticStyleChange(styleProperty.getStyleName(), { value: event.target.value }); + }; + + return ( + + {staticDynamicSelect} + + + + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/static_label_selector.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/static_label_selector.js deleted file mode 100644 index ea296a3312799..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/static_label_selector.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiFieldText } from '@elastic/eui'; - -export function StaticLabelSelector({ onChange, styleOptions }) { - const onValueChange = event => { - onChange({ value: event.target.value }); - }; - - return ( - - ); -} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_editor.js index 6bca56425d38d..aaa21ea315f36 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_editor.js @@ -6,16 +6,16 @@ import React from 'react'; -import { StaticDynamicStyleRow } from '../static_dynamic_style_row'; -import { DynamicLabelSelector } from './dynamic_label_selector'; -import { StaticLabelSelector } from './static_label_selector'; +import { StylePropEditor } from '../style_prop_editor'; +import { DynamicLabelForm } from './dynamic_label_form'; +import { StaticLabelForm } from './static_label_form'; export function VectorStyleLabelEditor(props) { - return ( - + const labelForm = props.styleProperty.isDynamic() ? ( + + ) : ( + ); + + return {labelForm}; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_form.js new file mode 100644 index 0000000000000..e0b7e7b2865a2 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_form.js @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import _ from 'lodash'; +import React from 'react'; +import { FieldSelect } from '../field_select'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +export function DynamicOrientationForm({ + fields, + onDynamicStyleChange, + staticDynamicSelect, + styleProperty, +}) { + const styleOptions = styleProperty.getOptions(); + + const onFieldChange = ({ field }) => { + onDynamicStyleChange(styleProperty.getStyleName(), { + ...styleOptions, + field, + }); + }; + + return ( + + {staticDynamicSelect} + + + + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_selection.js deleted file mode 100644 index 8ad3916ac6509..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_selection.js +++ /dev/null @@ -1,32 +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 _ from 'lodash'; -import React from 'react'; -import PropTypes from 'prop-types'; -import { dynamicOrientationShape } from '../style_option_shapes'; -import { FieldSelect, fieldShape } from '../field_select'; - -export function DynamicOrientationSelection({ fields, styleOptions, onChange }) { - const onFieldChange = ({ field }) => { - onChange({ ...styleOptions, field }); - }; - - return ( - - ); -} - -DynamicOrientationSelection.propTypes = { - fields: PropTypes.arrayOf(fieldShape).isRequired, - styleOptions: dynamicOrientationShape.isRequired, - onChange: PropTypes.func.isRequired, -}; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/orientation_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/orientation_editor.js index e97252a5e79da..915fc92c9fb38 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/orientation_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/orientation_editor.js @@ -6,20 +6,16 @@ import React from 'react'; -import { StaticDynamicStyleRow } from '../static_dynamic_style_row'; -import { DynamicOrientationSelection } from './dynamic_orientation_selection'; -import { StaticOrientationSelection } from './static_orientation_selection'; +import { StylePropEditor } from '../style_prop_editor'; +import { DynamicOrientationForm } from './dynamic_orientation_form'; +import { StaticOrientationForm } from './static_orientation_form'; export function OrientationEditor(props) { - return ( - + const orientationForm = props.styleProperty.isDynamic() ? ( + + ) : ( + ); + + return {orientationForm}; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/static_orientation_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/static_orientation_form.js new file mode 100644 index 0000000000000..8c4418f95e1d2 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/static_orientation_form.js @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { ValidatedRange } from '../../../../../components/validated_range'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +export function StaticOrientationForm({ onStaticStyleChange, staticDynamicSelect, styleProperty }) { + const onOrientationChange = orientation => { + onStaticStyleChange(styleProperty.getStyleName(), { orientation }); + }; + + return ( + + {staticDynamicSelect} + + + + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/static_orientation_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/static_orientation_selection.js deleted file mode 100644 index b5529c6987459..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/static_orientation_selection.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import PropTypes from 'prop-types'; -import { staticOrientationShape } from '../style_option_shapes'; -import { ValidatedRange } from '../../../../../components/validated_range'; - -export function StaticOrientationSelection({ onChange, styleOptions }) { - const onOrientationChange = orientation => { - onChange({ orientation }); - }; - - return ( - - ); -} - -StaticOrientationSelection.propTypes = { - styleOptions: staticOrientationShape.isRequired, - onChange: PropTypes.func.isRequired, -}; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_form.js new file mode 100644 index 0000000000000..8b069cd53b731 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_form.js @@ -0,0 +1,63 @@ +/* + * 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 _ from 'lodash'; +import React, { Fragment } from 'react'; +import { FieldSelect } from '../field_select'; +import { SizeRangeSelector } from './size_range_selector'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; + +export function DynamicSizeForm({ + fields, + onDynamicStyleChange, + staticDynamicSelect, + styleProperty, +}) { + const styleOptions = styleProperty.getOptions(); + + const onFieldChange = ({ field }) => { + onDynamicStyleChange(styleProperty.getStyleName(), { ...styleOptions, field }); + }; + + const onSizeRangeChange = ({ minSize, maxSize }) => { + onDynamicStyleChange(styleProperty.getStyleName(), { + ...styleOptions, + minSize, + maxSize, + }); + }; + + let sizeRange; + if (styleOptions.field && styleOptions.field.name) { + sizeRange = ( + + ); + } + + return ( + + + {staticDynamicSelect} + + + + + + {sizeRange} + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_selection.js deleted file mode 100644 index 76c5b97976bbc..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_selection.js +++ /dev/null @@ -1,48 +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 _ from 'lodash'; -import React, { Fragment } from 'react'; -import PropTypes from 'prop-types'; -import { dynamicSizeShape } from '../style_option_shapes'; -import { FieldSelect, fieldShape } from '../field_select'; -import { SizeRangeSelector } from './size_range_selector'; -import { EuiSpacer } from '@elastic/eui'; - -export function DynamicSizeSelection({ fields, styleOptions, onChange }) { - const onFieldChange = ({ field }) => { - onChange({ ...styleOptions, field }); - }; - - const onSizeRangeChange = ({ minSize, maxSize }) => { - onChange({ ...styleOptions, minSize, maxSize }); - }; - - return ( - - - - - - ); -} - -DynamicSizeSelection.propTypes = { - fields: PropTypes.arrayOf(fieldShape).isRequired, - styleOptions: dynamicSizeShape.isRequired, - onChange: PropTypes.func.isRequired, -}; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/static_size_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/static_size_form.js new file mode 100644 index 0000000000000..d8fe1322db3e3 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/static_size_form.js @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { ValidatedRange } from '../../../../../components/validated_range'; +import { i18n } from '@kbn/i18n'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +export function StaticSizeForm({ onStaticStyleChange, staticDynamicSelect, styleProperty }) { + const onSizeChange = size => { + onStaticStyleChange(styleProperty.getStyleName(), { size }); + }; + + return ( + + {staticDynamicSelect} + + + + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/static_size_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/static_size_selection.js deleted file mode 100644 index 38f8fe53d1748..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/static_size_selection.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import PropTypes from 'prop-types'; -import { staticSizeShape } from '../style_option_shapes'; -import { ValidatedRange } from '../../../../../components/validated_range'; -import { i18n } from '@kbn/i18n'; - -export function StaticSizeSelection({ onChange, styleOptions }) { - const onSizeChange = size => { - onChange({ size }); - }; - - return ( - - ); -} - -StaticSizeSelection.propTypes = { - styleOptions: staticSizeShape.isRequired, - onChange: PropTypes.func.isRequired, -}; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/vector_style_size_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/vector_style_size_editor.js index 6580bfc00e0ad..e344f72bd429a 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/vector_style_size_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/vector_style_size_editor.js @@ -6,20 +6,16 @@ import React from 'react'; -import { StaticDynamicStyleRow } from '../static_dynamic_style_row'; -import { DynamicSizeSelection } from './dynamic_size_selection'; -import { StaticSizeSelection } from './static_size_selection'; +import { StylePropEditor } from '../style_prop_editor'; +import { DynamicSizeForm } from './dynamic_size_form'; +import { StaticSizeForm } from './static_size_form'; export function VectorStyleSizeEditor(props) { - return ( - + const sizeForm = props.styleProperty.isDynamic() ? ( + + ) : ( + ); + + return {sizeForm}; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/static_dynamic_style_row.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/static_dynamic_style_row.js deleted file mode 100644 index 311406731801a..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/static_dynamic_style_row.js +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { Component, Fragment } from 'react'; -import { VectorStyle } from '../vector_style'; -import { i18n } from '@kbn/i18n'; -import { FieldMetaOptionsPopover } from './field_meta_options_popover'; -import { getVectorStyleLabel } from './get_vector_style_label'; - -import { EuiFlexGroup, EuiFlexItem, EuiToolTip, EuiFormRow, EuiButtonToggle } from '@elastic/eui'; - -export class StaticDynamicStyleRow extends Component { - // Store previous options locally so when type is toggled, - // previous style options can be used. - prevStaticStyleOptions = this.props.defaultStaticStyleOptions; - prevDynamicStyleOptions = this.props.defaultDynamicStyleOptions; - - _canBeDynamic() { - return this.props.fields.length > 0; - } - - _isDynamic() { - return this.props.styleProperty.isDynamic(); - } - - _getStyleOptions() { - return this.props.styleProperty.getOptions(); - } - - _onFieldMetaOptionsChange = fieldMetaOptions => { - const styleDescriptor = { - type: VectorStyle.STYLE_TYPE.DYNAMIC, - options: { - ...this._getStyleOptions(), - fieldMetaOptions, - }, - }; - this.props.handlePropertyChange(this.props.styleProperty.getStyleName(), styleDescriptor); - }; - - _onStaticStyleChange = options => { - const styleDescriptor = { - type: VectorStyle.STYLE_TYPE.STATIC, - options, - }; - this.props.handlePropertyChange(this.props.styleProperty.getStyleName(), styleDescriptor); - }; - - _onDynamicStyleChange = options => { - const styleDescriptor = { - type: VectorStyle.STYLE_TYPE.DYNAMIC, - options, - }; - this.props.handlePropertyChange(this.props.styleProperty.getStyleName(), styleDescriptor); - }; - - _onTypeToggle = () => { - if (this._isDynamic()) { - // preserve current dynmaic style - this.prevDynamicStyleOptions = this._getStyleOptions(); - // toggle to static style - this._onStaticStyleChange(this.prevStaticStyleOptions); - return; - } - - // preserve current static style - this.prevStaticStyleOptions = this._getStyleOptions(); - // toggle to dynamic style - this._onDynamicStyleChange(this.prevDynamicStyleOptions); - }; - - _renderStyleSelector() { - if (this._isDynamic()) { - const DynamicSelector = this.props.DynamicSelector; - return ( - - - - - ); - } - - const StaticSelector = this.props.StaticSelector; - return ( - - ); - } - - render() { - const isDynamic = this._isDynamic(); - const dynamicTooltipContent = isDynamic - ? i18n.translate('xpack.maps.styles.staticDynamic.staticDescription', { - defaultMessage: 'Use static styling properties to symbolize features.', - }) - : i18n.translate('xpack.maps.styles.staticDynamic.dynamicDescription', { - defaultMessage: 'Use property values to symbolize features.', - }); - - return ( - - - - {this._renderStyleSelector()} - - - {this._canBeDynamic() && ( - - - - - - - - )} - - ); - } -} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_prop_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_prop_editor.js new file mode 100644 index 0000000000000..1ac8edfb2cc69 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_prop_editor.js @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Component, Fragment } from 'react'; +import { FieldMetaOptionsPopover } from './field_meta_options_popover'; +import { getVectorStyleLabel } from './get_vector_style_label'; +import { EuiFormRow, EuiSelect } from '@elastic/eui'; +import { VectorStyle } from '../vector_style'; +import { i18n } from '@kbn/i18n'; + +export class StylePropEditor extends Component { + _prevStaticStyleOptions = this.props.defaultStaticStyleOptions; + _prevDynamicStyleOptions = this.props.defaultDynamicStyleOptions; + + _onTypeToggle = () => { + if (this.props.styleProperty.isDynamic()) { + // preserve current dynmaic style + this._prevDynamicStyleOptions = this.props.styleProperty.getOptions(); + // toggle to static style + this.props.onStaticStyleChange( + this.props.styleProperty.getStyleName(), + this._prevStaticStyleOptions + ); + } else { + // preserve current static style + this._prevStaticStyleOptions = this.props.styleProperty.getOptions(); + // toggle to dynamic style + this.props.onDynamicStyleChange( + this.props.styleProperty.getStyleName(), + this._prevDynamicStyleOptions + ); + } + }; + + _onFieldMetaOptionsChange = fieldMetaOptions => { + const options = { + ...this.props.styleProperty.getOptions(), + fieldMetaOptions, + }; + this.props.onDynamicStyleChange(this.props.styleProperty.getStyleName(), options); + }; + + renderStaticDynamicSelect() { + const options = [ + { + value: VectorStyle.STYLE_TYPE.STATIC, + text: this.props.customStaticOptionLabel + ? this.props.customStaticOptionLabel + : i18n.translate('xpack.maps.styles.staticDynamicSelect.staticLabel', { + defaultMessage: 'Fixed', + }), + }, + { + value: VectorStyle.STYLE_TYPE.DYNAMIC, + text: i18n.translate('xpack.maps.styles.staticDynamicSelect.dynamicLabel', { + defaultMessage: 'By value', + }), + }, + ]; + + return ( + + ); + } + + render() { + const fieldMetaOptionsPopover = this.props.styleProperty.isDynamic() ? ( + + ) : null; + + return ( + + + {React.cloneElement(this.props.children, { + staticDynamicSelect: this.renderStaticDynamicSelect(), + })} + {fieldMetaOptionsPopover} + + + ); + } +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js index 44f630db9d890..8e80e036dbb8b 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js @@ -12,8 +12,13 @@ import { VectorStyleColorEditor } from './color/vector_style_color_editor'; import { VectorStyleSizeEditor } from './size/vector_style_size_editor'; import { VectorStyleSymbolEditor } from './vector_style_symbol_editor'; import { VectorStyleLabelEditor } from './label/vector_style_label_editor'; +import { VectorStyle } from '../vector_style'; import { OrientationEditor } from './orientation/orientation_editor'; -import { getDefaultDynamicProperties, getDefaultStaticProperties } from '../vector_style_defaults'; +import { + getDefaultDynamicProperties, + getDefaultStaticProperties, + VECTOR_STYLES, +} from '../vector_style_defaults'; import { DEFAULT_FILL_COLORS, DEFAULT_LINE_COLORS } from '../../color_utils'; import { VECTOR_SHAPE_TYPES } from '../../../sources/vector_feature_types'; import { SYMBOLIZE_AS_ICON } from '../vector_constants'; @@ -128,15 +133,36 @@ export class VectorStyleEditor extends Component { this.props.onIsTimeAwareChange(event.target.checked); }; + _onStaticStyleChange = (propertyName, options) => { + const styleDescriptor = { + type: VectorStyle.STYLE_TYPE.STATIC, + options, + }; + this.props.handlePropertyChange(propertyName, styleDescriptor); + }; + + _onDynamicStyleChange = (propertyName, options) => { + const styleDescriptor = { + type: VectorStyle.STYLE_TYPE.DYNAMIC, + options, + }; + this.props.handlePropertyChange(propertyName, styleDescriptor); + }; + _renderFillColor() { return ( ); } @@ -145,11 +171,16 @@ export class VectorStyleEditor extends Component { return ( ); } @@ -157,11 +188,16 @@ export class VectorStyleEditor extends Component { _renderLineWidth() { return ( ); } @@ -169,11 +205,16 @@ export class VectorStyleEditor extends Component { _renderSymbolSize() { return ( ); } @@ -182,30 +223,45 @@ export class VectorStyleEditor extends Component { return ( @@ -217,11 +273,16 @@ export class VectorStyleEditor extends Component { if (this.props.symbolDescriptor.options.symbolizeAs === SYMBOLIZE_AS_ICON) { iconOrientation = ( ); } diff --git a/x-pack/legacy/plugins/maps/public/layers/vector_tile_layer.js b/x-pack/legacy/plugins/maps/public/layers/vector_tile_layer.js index c1b590f56ae52..b09ccdc3af8ba 100644 --- a/x-pack/legacy/plugins/maps/public/layers/vector_tile_layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/vector_tile_layer.js @@ -31,32 +31,58 @@ export class VectorTileLayer extends TileLayer { return tileLayerDescriptor; } + _canSkipSync({ prevDataRequest, nextMeta }) { + if (!prevDataRequest) { + return false; + } + const prevMeta = prevDataRequest.getMeta(); + if (!prevMeta) { + return false; + } + + return prevMeta.tileLayerId === nextMeta.tileLayerId; + } + async syncData({ startLoading, stopLoading, onLoadError, dataFilters }) { if (!this.isVisible() || !this.showAtZoomLevel(dataFilters.zoom)) { return; } - const sourceDataRequest = this.getSourceDataRequest(); - if (sourceDataRequest) { - //data is immmutable + + const nextMeta = { tileLayerId: this._source.getTileLayerId() }; + const canSkipSync = this._canSkipSync({ + prevDataRequest: this.getSourceDataRequest(), + nextMeta, + }); + if (canSkipSync) { return; } + const requestToken = Symbol(`layer-source-refresh:${this.getId()} - source`); - startLoading(SOURCE_DATA_ID_ORIGIN, requestToken, dataFilters); try { + startLoading(SOURCE_DATA_ID_ORIGIN, requestToken, dataFilters); const styleAndSprites = await this._source.getVectorStyleSheetAndSpriteMeta(isRetina()); const spriteSheetImageData = await loadSpriteSheetImageData(styleAndSprites.spriteMeta.png); const data = { ...styleAndSprites, spriteSheetImageData, }; - stopLoading(SOURCE_DATA_ID_ORIGIN, requestToken, data, {}); + stopLoading(SOURCE_DATA_ID_ORIGIN, requestToken, data, nextMeta); } catch (error) { onLoadError(SOURCE_DATA_ID_ORIGIN, requestToken, error.message); } } _generateMbId(name) { - return this.getId() + '_' + name; + return `${this.getId()}_${name}`; + } + + _generateMbSourceIdPrefix() { + const DELIMITTER = '___'; + return `${this.getId()}${DELIMITTER}${this._source.getTileLayerId()}${DELIMITTER}`; + } + + _generateMbSourceId(name) { + return `${this._generateMbSourceIdPrefix()}${name}`; } _getVectorStyle() { @@ -103,19 +129,15 @@ export class VectorTileLayer extends TileLayer { return []; } const sourceIds = Object.keys(vectorStyle.sources); - return sourceIds.map(sourceId => this._generateMbId(sourceId)); + return sourceIds.map(sourceId => this._generateMbSourceId(sourceId)); } ownsMbLayerId(mbLayerId) { - //todo optimize: do not create temp array - const mbLayerIds = this.getMbLayerIds(); - return mbLayerIds.indexOf(mbLayerId) >= 0; + return mbLayerId.startsWith(this.getId()); } ownsMbSourceId(mbSourceId) { - //todo optimize: do not create temp array - const mbSourceIds = this.getMbSourceIds(); - return mbSourceIds.indexOf(mbSourceId) >= 0; + return mbSourceId.startsWith(this.getId()); } _makeNamespacedImageId(imageId) { @@ -123,19 +145,43 @@ export class VectorTileLayer extends TileLayer { return prefix + imageId; } + _requiresPrevSourceCleanup(mbMap) { + const sourceIdPrefix = this._generateMbSourceIdPrefix(); + const mbStyle = mbMap.getStyle(); + return Object.keys(mbStyle.sources).some(mbSourceId => { + const doesMbSourceBelongToLayer = this.ownsMbSourceId(mbSourceId); + const doesMbSourceBelongToSource = mbSourceId.startsWith(sourceIdPrefix); + return doesMbSourceBelongToLayer && !doesMbSourceBelongToSource; + }); + } + syncLayerWithMB(mbMap) { const vectorStyle = this._getVectorStyle(); if (!vectorStyle) { return; } + if (this._requiresPrevSourceCleanup(mbMap)) { + const mbStyle = mbMap.getStyle(); + mbStyle.layers.forEach(mbLayer => { + if (this.ownsMbLayerId(mbLayer.id)) { + mbMap.removeLayer(mbLayer.id); + } + }); + Object.keys(mbStyle.sources).some(mbSourceId => { + if (this.ownsMbSourceId(mbSourceId)) { + mbMap.removeSource(mbSourceId); + } + }); + } + let initialBootstrapCompleted = false; const sourceIds = Object.keys(vectorStyle.sources); sourceIds.forEach(sourceId => { if (initialBootstrapCompleted) { return; } - const mbSourceId = this._generateMbId(sourceId); + const mbSourceId = this._generateMbSourceId(sourceId); const mbSource = mbMap.getSource(mbSourceId); if (mbSource) { //if a single source is present, the layer already has bootstrapped with the mbMap @@ -174,7 +220,7 @@ export class VectorTileLayer extends TileLayer { } const newLayerObject = { ...layer, - source: this._generateMbId(layer.source), + source: this._generateMbSourceId(layer.source), id: mbLayerId, }; diff --git a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.js b/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.js index 6d078ae35ef85..848c964f4b6d4 100644 --- a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.js +++ b/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.js @@ -5,10 +5,16 @@ */ import _ from 'lodash'; -import { EMS_FILE, ES_GEO_FIELD_TYPE, MAP_SAVED_OBJECT_TYPE } from '../../common/constants'; +import { + EMS_FILE, + ES_GEO_FIELD_TYPE, + MAP_SAVED_OBJECT_TYPE, + TELEMETRY_TYPE, +} from '../../common/constants'; -function getSavedObjectsClient(server, callCluster) { +function getSavedObjectsClient(server) { const { SavedObjectsClient, getSavedObjectsRepository } = server.savedObjects; + const callCluster = server.plugins.elasticsearch.getCluster('admin').callWithInternalUser; const internalRepository = getSavedObjectsRepository(callCluster); return new SavedObjectsClient(internalRepository); } @@ -79,7 +85,7 @@ export function buildMapsTelemetry({ mapSavedObjects, indexPatternSavedObjects, // Total count of maps mapsTotalCount: mapsCount, // Time of capture - timeCaptured: new Date(), + timeCaptured: new Date().toISOString(), attributesPerMap: { // Count of data sources per map dataSourcesCount: { @@ -115,16 +121,16 @@ async function getIndexPatternSavedObjects(savedObjectsClient) { return _.get(indexPatternSavedObjects, 'saved_objects', []); } -export async function getMapsTelemetry(server, callCluster) { - const savedObjectsClient = getSavedObjectsClient(server, callCluster); +export async function getMapsTelemetry(server) { + const savedObjectsClient = getSavedObjectsClient(server); const mapSavedObjects = await getMapSavedObjects(savedObjectsClient); const indexPatternSavedObjects = await getIndexPatternSavedObjects(savedObjectsClient); const settings = { showMapVisualizationTypes: server.config().get('xpack.maps.showMapVisualizationTypes'), }; const mapsTelemetry = buildMapsTelemetry({ mapSavedObjects, indexPatternSavedObjects, settings }); - return await savedObjectsClient.create('maps-telemetry', mapsTelemetry, { - id: 'maps-telemetry', + return await savedObjectsClient.create(TELEMETRY_TYPE, mapsTelemetry, { + id: TELEMETRY_TYPE, overwrite: true, }); } diff --git a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_usage_collector.js b/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_usage_collector.js index d1011736e77f8..9c575e66f7556 100644 --- a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_usage_collector.js +++ b/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_usage_collector.js @@ -4,85 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; -import { TASK_ID, scheduleTask, registerMapsTelemetryTask } from './telemetry_task'; +import { getMapsTelemetry } from './maps_telemetry'; +import { TELEMETRY_TYPE } from '../../common/constants'; export function initTelemetryCollection(usageCollection, server) { - registerMapsTelemetryTask(server); - scheduleTask(server); - registerMapsUsageCollector(usageCollection, server); -} - -async function isTaskManagerReady(server) { - const result = await fetch(server); - return result !== null; -} - -async function fetch(server) { - let docs; - const taskManager = server.plugins.task_manager; - - if (!taskManager) { - return null; - } - - try { - ({ docs } = await taskManager.fetch({ - query: { - bool: { - filter: { - term: { - _id: `task:${TASK_ID}`, - }, - }, - }, - }, - })); - } catch (err) { - const errMessage = err && err.message ? err.message : err.toString(); - /* - * The usage service WILL to try to fetch from this collector before the task manager has been initialized, because the task manager - * has to wait for all plugins to initialize first. - * It's fine to ignore it as next time around it will be initialized (or it will throw a different type of error) - */ - if (errMessage.indexOf('NotInitialized') >= 0) { - return null; - } else { - throw err; - } + if (!usageCollection) { + return; } - return docs; -} - -export function buildCollectorObj(server) { - let isCollectorReady = false; - async function determineIfTaskManagerIsReady() { - let isReady = false; - try { - isReady = await isTaskManagerReady(server); - } catch (err) {} // eslint-disable-line - - if (isReady) { - isCollectorReady = true; - } else { - setTimeout(determineIfTaskManagerIsReady, 500); - } - } - determineIfTaskManagerIsReady(); - - return { - type: 'maps', - isReady: () => isCollectorReady, - fetch: async () => { - const docs = await fetch(server); - return _.get(docs, '[0].state.stats'); - }, - }; -} + const mapsUsageCollector = usageCollection.makeUsageCollector({ + type: TELEMETRY_TYPE, + isReady: () => true, + fetch: async () => await getMapsTelemetry(server), + }); -export function registerMapsUsageCollector(usageCollection, server) { - const collectorObj = buildCollectorObj(server); - const mapsUsageCollector = usageCollection.makeUsageCollector(collectorObj); usageCollection.registerCollector(mapsUsageCollector); } diff --git a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_usage_collector.test.js b/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_usage_collector.test.js index 727d60b5088aa..c5a3fca89b560 100644 --- a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_usage_collector.test.js +++ b/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_usage_collector.test.js @@ -4,60 +4,32 @@ * you may not use this file except in compliance with the Elastic License. */ -import sinon from 'sinon'; -import { getMockCallWithInternal, getMockKbnServer, getMockTaskFetch } from '../test_utils'; -import { buildCollectorObj } from './maps_usage_collector'; +import { initTelemetryCollection } from './maps_usage_collector'; describe('buildCollectorObj#fetch', () => { - let mockKbnServer; + let makeUsageCollectorStub; + let registerStub; + let usageCollection; beforeEach(() => { - mockKbnServer = getMockKbnServer(); + makeUsageCollectorStub = jest.fn(); + registerStub = jest.fn(); + usageCollection = { + makeUsageCollector: makeUsageCollectorStub, + registerCollector: registerStub, + }; }); - test('can return empty stats', async () => { - const { type, fetch } = buildCollectorObj(mockKbnServer); - expect(type).toBe('maps'); - const fetchResult = await fetch(); - expect(fetchResult).toEqual({}); - }); - - test('provides known stats', async () => { - const mockTaskFetch = getMockTaskFetch([ - { - state: { - runs: 2, - stats: { wombat_sightings: { total: 712, max: 84, min: 7, avg: 63 } }, - }, - }, - ]); - mockKbnServer = getMockKbnServer(getMockCallWithInternal(), mockTaskFetch); - - const { type, fetch } = buildCollectorObj(mockKbnServer); - expect(type).toBe('maps'); - const fetchResult = await fetch(); - expect(fetchResult).toEqual({ wombat_sightings: { total: 712, max: 84, min: 7, avg: 63 } }); - }); - - describe('Error handling', () => { - test('Silently handles Task Manager NotInitialized', async () => { - const mockTaskFetch = sinon.stub(); - mockTaskFetch.rejects( - new Error('NotInitialized taskManager is still waiting for plugins to load') - ); - mockKbnServer = getMockKbnServer(getMockCallWithInternal(), mockTaskFetch); - - const { fetch } = buildCollectorObj(mockKbnServer); - await expect(fetch()).resolves.toBe(undefined); - }); - // In real life, the CollectorSet calls fetch and handles errors - test('defers the errors', async () => { - const mockTaskFetch = sinon.stub(); - mockTaskFetch.rejects(new Error('Sad violin')); - mockKbnServer = getMockKbnServer(getMockCallWithInternal(), mockTaskFetch); + test('makes and registers maps usage collector', async () => { + const serverPlaceholder = {}; + initTelemetryCollection(usageCollection, serverPlaceholder); - const { fetch } = buildCollectorObj(mockKbnServer); - await expect(fetch()).rejects.toMatchObject(new Error('Sad violin')); + expect(registerStub).toHaveBeenCalledTimes(1); + expect(makeUsageCollectorStub).toHaveBeenCalledTimes(1); + expect(makeUsageCollectorStub).toHaveBeenCalledWith({ + type: expect.any(String), + isReady: expect.any(Function), + fetch: expect.any(Function), }); }); }); diff --git a/x-pack/legacy/plugins/maps/server/maps_telemetry/telemetry_task.js b/x-pack/legacy/plugins/maps/server/maps_telemetry/telemetry_task.js deleted file mode 100644 index db5df358abc39..0000000000000 --- a/x-pack/legacy/plugins/maps/server/maps_telemetry/telemetry_task.js +++ /dev/null @@ -1,125 +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 { getMapsTelemetry } from './maps_telemetry'; - -const TELEMETRY_TASK_TYPE = 'maps_telemetry'; - -export const TASK_ID = `Maps-${TELEMETRY_TASK_TYPE}`; - -export function scheduleTask(server) { - const taskManager = server.plugins.task_manager; - - if (!taskManager) { - server.log(['debug', 'telemetry'], `Task manager is not available`); - return; - } - - const { kbnServer } = server.plugins.xpack_main.status.plugin; - - kbnServer.afterPluginsInit(() => { - // The code block below can't await directly within "afterPluginsInit" - // callback due to circular dependency. The server isn't "ready" until - // this code block finishes. Migrations wait for server to be ready before - // executing. Saved objects repository waits for migrations to finish before - // finishing the request. To avoid this, we'll await within a separate - // function block. - (async () => { - try { - await taskManager.ensureScheduled({ - id: TASK_ID, - taskType: TELEMETRY_TASK_TYPE, - state: { stats: {}, runs: 0 }, - }); - } catch (e) { - server.log(['warning', 'maps'], `Error scheduling telemetry task, received ${e.message}`); - } - })(); - }); -} - -export function registerMapsTelemetryTask(server) { - const taskManager = server.plugins.task_manager; - - if (!taskManager) { - server.log(['debug', 'telemetry'], `Task manager is not available`); - return; - } - - taskManager.registerTaskDefinitions({ - [TELEMETRY_TASK_TYPE]: { - title: 'Maps telemetry fetch task', - type: TELEMETRY_TASK_TYPE, - timeout: '1m', - createTaskRunner: telemetryTaskRunner(server), - }, - }); -} - -export function telemetryTaskRunner(server) { - return ({ taskInstance }) => { - const { state } = taskInstance; - const prevState = state; - - const callCluster = server.plugins.elasticsearch.getCluster('admin').callWithInternalUser; - - let mapsTelemetryTask; - - return { - async run({ taskCanceled = false } = {}) { - try { - mapsTelemetryTask = makeCancelable(getMapsTelemetry(server, callCluster), taskCanceled); - } catch (err) { - server.log(['warning'], `Error loading maps telemetry: ${err}`); - } finally { - return mapsTelemetryTask.promise - .then((mapsTelemetry = {}) => { - return { - state: { - runs: state.runs || 0 + 1, - stats: mapsTelemetry.attributes || prevState.stats || {}, - }, - runAt: getNextMidnight(), - }; - }) - .catch(errMsg => - server.log(['warning'], `Error executing maps telemetry task: ${errMsg}`) - ); - } - }, - async cancel() { - if (mapsTelemetryTask) { - mapsTelemetryTask.cancel(); - } else { - server.log(['warning'], `Can not cancel "mapsTelemetryTask", it has not been defined`); - } - }, - }; - }; -} - -function makeCancelable(promise, isCanceled) { - const logMsg = 'Maps telemetry task has been cancelled'; - const wrappedPromise = new Promise((resolve, reject) => { - promise - .then(val => (isCanceled ? reject(logMsg) : resolve(val))) - .catch(err => (isCanceled ? reject(logMsg) : reject(err.message))); - }); - - return { - promise: wrappedPromise, - cancel() { - isCanceled = true; - }, - }; -} - -function getNextMidnight() { - const nextMidnight = new Date(); - nextMidnight.setHours(0, 0, 0, 0); - nextMidnight.setDate(nextMidnight.getDate() + 1); - return nextMidnight; -} diff --git a/x-pack/legacy/plugins/maps/server/maps_telemetry/telemetry_task.test.js b/x-pack/legacy/plugins/maps/server/maps_telemetry/telemetry_task.test.js deleted file mode 100644 index ad23ed1634204..0000000000000 --- a/x-pack/legacy/plugins/maps/server/maps_telemetry/telemetry_task.test.js +++ /dev/null @@ -1,68 +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 { getMockKbnServer, getMockTaskInstance } from '../test_utils'; -import { telemetryTaskRunner } from './telemetry_task'; -import * as mapsTelemetry from './maps_telemetry'; -jest.mock('./maps_telemetry'); - -const expectedAttributes = { - expect: 'values', - toBe: 'populated', -}; - -const generateTelemetry = ({ includeAttributes = true } = {}) => { - mapsTelemetry.getMapsTelemetry = async () => ({ // eslint-disable-line - attributes: includeAttributes ? expectedAttributes : {}, - }); -}; - -describe('telemetryTaskRunner', () => { - let mockTaskInstance; - let mockKbnServer; - let taskRunner; - - beforeEach(() => { - mockTaskInstance = getMockTaskInstance(); - mockKbnServer = getMockKbnServer(); - taskRunner = telemetryTaskRunner(mockKbnServer)({ taskInstance: mockTaskInstance }); - }); - - test('returns empty stats as default', async () => { - generateTelemetry({ includeAttributes: false }); - - const runResult = await taskRunner.run(); - - expect(runResult).toMatchObject({ - state: { - runs: 1, - stats: {}, - }, - }); - }); - - // Return stats when run normally - test('returns stats normally', async () => { - generateTelemetry(); - - const runResult = await taskRunner.run(); - - expect(runResult).toMatchObject({ - state: { - runs: 1, - stats: expectedAttributes, - }, - }); - }); - - test('cancels when cancel flag set to "true", returns undefined', async () => { - generateTelemetry(); - - const runResult = await taskRunner.run({ taskCanceled: true }); - - expect(runResult).toBe(undefined); - }); -}); diff --git a/x-pack/legacy/plugins/ml/common/util/job_utils.js b/x-pack/legacy/plugins/ml/common/util/job_utils.js index 757dfbd7a9a77..8982cebed522e 100644 --- a/x-pack/legacy/plugins/ml/common/util/job_utils.js +++ b/x-pack/legacy/plugins/ml/common/util/job_utils.js @@ -521,7 +521,7 @@ export function validateModelMemoryLimitUnits(modelMemoryLimit) { let valid = true; if (modelMemoryLimit !== undefined) { - const mml = modelMemoryLimit.toUpperCase(); + const mml = String(modelMemoryLimit).toUpperCase(); const mmlSplit = mml.match(/\d+(\w+)$/); const unit = mmlSplit && mmlSplit.length === 2 ? mmlSplit[1] : null; diff --git a/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.test.ts b/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.test.ts index fcb99ea83548d..7ea2f74908e0e 100644 --- a/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.test.ts +++ b/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.test.ts @@ -138,5 +138,11 @@ describe('useCreateAnalyticsForm', () => { validateAdvancedEditor(getMockState({ index: 'the-source-index', modelMemoryLimit: '' })) .isValid ).toBe(false); + // can still run validation check on model_memory_limit if number type + expect( + // @ts-ignore number is not assignable to type string - mml gets converted to string prior to creation + validateAdvancedEditor(getMockState({ index: 'the-source-index', modelMemoryLimit: 100 })) + .isValid + ).toBe(false); }); }); diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.test.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.test.ts index 9ba7cfbcac1d8..1b7ba3c90bab1 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.test.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.test.ts @@ -43,7 +43,7 @@ describe('headers', () => { }; const encryptedHeaders = await encryptHeaders(headers); - const { decryptedHeaders } = await decryptJobHeaders({ + const decryptedHeaders = await decryptJobHeaders({ job: { title: 'cool-job-bro', type: 'csv', diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.ts index 486181117bbfb..436b2c2dab1ad 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.ts @@ -17,22 +17,18 @@ export const decryptJobHeaders = async < JobParamsType, JobDocPayloadType extends HasEncryptedHeaders >({ - job, server, + job, logger, }: { - job: JobDocPayloadType; server: ServerFacade; - logger: Logger; -}): Promise<{ job: JobDocPayloadType; - server: ServerFacade; - decryptedHeaders: Record; -}> => { + logger: Logger; +}): Promise> => { const crypto: CryptoFactory = cryptoFactory(server); try { const decryptedHeaders: Record = await crypto.decrypt(job.headers); - return { job, decryptedHeaders, server }; + return decryptedHeaders; } catch (err) { logger.error(err); diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_conditional_headers.test.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_conditional_headers.test.ts index 66990c1f37df4..070bdb4314af9 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_conditional_headers.test.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_conditional_headers.test.ts @@ -27,7 +27,7 @@ describe('conditions', () => { baz: 'quix', }; - const { conditionalHeaders } = await getConditionalHeaders({ + const conditionalHeaders = await getConditionalHeaders({ job: {} as JobDocPayload, filteredHeaders: permittedHeaders, server: mockServer, @@ -44,7 +44,7 @@ describe('conditions', () => { baz: 'quix', }; - const { conditionalHeaders } = await getConditionalHeaders({ + const conditionalHeaders = await getConditionalHeaders({ job: {} as JobDocPayload, filteredHeaders: permittedHeaders, server: mockServer, @@ -65,7 +65,7 @@ describe('conditions', () => { baz: 'quix', }; - const { conditionalHeaders } = await getConditionalHeaders({ + const conditionalHeaders = await getConditionalHeaders({ job: {} as JobDocPayload, filteredHeaders: permittedHeaders, server: mockServer, @@ -82,7 +82,7 @@ describe('conditions', () => { baz: 'quix', }; - const { conditionalHeaders } = await getConditionalHeaders({ + const conditionalHeaders = await getConditionalHeaders({ job: {} as JobDocPayload, filteredHeaders: permittedHeaders, server: mockServer, @@ -97,7 +97,7 @@ describe('conditions', () => { baz: 'quix', }; - const { conditionalHeaders } = await getConditionalHeaders({ + const conditionalHeaders = await getConditionalHeaders({ job: {} as JobDocPayload, filteredHeaders: permittedHeaders, server: mockServer, @@ -120,7 +120,7 @@ describe('conditions', () => { baz: 'quix', }; - const { conditionalHeaders } = await getConditionalHeaders({ + const conditionalHeaders = await getConditionalHeaders({ job: {} as JobDocPayload, filteredHeaders: permittedHeaders, server: mockServer, @@ -137,7 +137,7 @@ describe('conditions', () => { baz: 'quix', }; - const { conditionalHeaders } = await getConditionalHeaders({ + const conditionalHeaders = await getConditionalHeaders({ job: {} as JobDocPayload, filteredHeaders: permittedHeaders, server: mockServer, @@ -153,7 +153,7 @@ test('uses basePath from job when creating saved object service', async () => { baz: 'quix', }; - const { conditionalHeaders } = await getConditionalHeaders({ + const conditionalHeaders = await getConditionalHeaders({ job: {} as JobDocPayload, filteredHeaders: permittedHeaders, server: mockServer, @@ -180,7 +180,7 @@ test(`uses basePath from server if job doesn't have a basePath when creating sav baz: 'quix', }; - const { conditionalHeaders } = await getConditionalHeaders({ + const conditionalHeaders = await getConditionalHeaders({ job: {} as JobDocPayload, filteredHeaders: permittedHeaders, server: mockServer, @@ -203,7 +203,7 @@ test(`uses basePath from server if job doesn't have a basePath when creating sav describe('config formatting', () => { test(`lowercases server.host`, async () => { mockServer = createMockServer({ settings: { 'server.host': 'COOL-HOSTNAME' } }); - const { conditionalHeaders } = await getConditionalHeaders({ + const conditionalHeaders = await getConditionalHeaders({ job: {} as JobDocPayload, filteredHeaders: {}, server: mockServer, @@ -215,7 +215,7 @@ describe('config formatting', () => { mockServer = createMockServer({ settings: { 'xpack.reporting.kibanaServer.hostname': 'GREAT-HOSTNAME' }, }); - const { conditionalHeaders } = await getConditionalHeaders({ + const conditionalHeaders = await getConditionalHeaders({ job: { title: 'cool-job-bro', type: 'csv', diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_conditional_headers.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_conditional_headers.ts index 14c092ccfb4a6..975060a8052f0 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_conditional_headers.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_conditional_headers.ts @@ -6,13 +6,13 @@ import { ConditionalHeaders, ServerFacade } from '../../../types'; export const getConditionalHeaders = ({ + server, job, filteredHeaders, - server, }: { + server: ServerFacade; job: JobDocPayloadType; filteredHeaders: Record; - server: ServerFacade; }) => { const config = server.config(); const [hostname, port, basePath, protocol] = [ @@ -32,5 +32,5 @@ export const getConditionalHeaders = ({ }, }; - return { job, conditionalHeaders, server }; + return conditionalHeaders; }; diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.test.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.test.ts index 4fca05337ea0c..ff2c44026315d 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.test.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.test.ts @@ -19,7 +19,7 @@ test(`gets logo from uiSettings`, async () => { baz: 'quix', }; - const { conditionalHeaders } = await getConditionalHeaders({ + const conditionalHeaders = await getConditionalHeaders({ job: {} as JobDocPayloadPDF, filteredHeaders: permittedHeaders, server: mockServer, diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.ts index 9b64e896dad18..0059276f6df71 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.ts @@ -9,13 +9,13 @@ import { ConditionalHeaders, ServerFacade } from '../../../types'; import { JobDocPayloadPDF } from '../../printable_pdf/types'; // Logo is PDF only export const getCustomLogo = async ({ + server, job, conditionalHeaders, - server, }: { + server: ServerFacade; job: JobDocPayloadPDF; conditionalHeaders: ConditionalHeaders; - server: ServerFacade; }) => { const serverBasePath: string = server.config().get('server.basePath'); @@ -38,12 +38,8 @@ export const getCustomLogo = async ({ }; const savedObjects = server.savedObjects; - const savedObjectsClient = savedObjects.getScopedSavedObjectsClient(fakeRequest); - const uiSettings = server.uiSettingsServiceFactory({ savedObjectsClient }); - - const logo = await uiSettings.get(UI_SETTINGS_CUSTOM_PDF_LOGO); - - return { job, conditionalHeaders, logo, server }; + const logo: string = await uiSettings.get(UI_SETTINGS_CUSTOM_PDF_LOGO); + return { conditionalHeaders, logo }; }; diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_full_urls.test.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_full_urls.test.ts index 60735b4abd446..9b2a065427f70 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_full_urls.test.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_full_urls.test.ts @@ -22,29 +22,26 @@ beforeEach(() => { }); test(`fails if no URL is passed`, async () => { - await expect( + const fn = () => getFullUrls({ job: {}, server: mockServer, - } as FullUrlsOpts) - ).rejects.toMatchInlineSnapshot( - `[Error: No valid URL fields found in Job Params! Expected \`job.relativeUrl\` or \`job.objects[{ relativeUrl }]\`]` + } as FullUrlsOpts); + expect(fn).toThrowErrorMatchingInlineSnapshot( + `"No valid URL fields found in Job Params! Expected \`job.relativeUrl: string\` or \`job.relativeUrls: string[]\`"` ); }); test(`fails if URLs are file-protocols for PNGs`, async () => { const forceNow = '2000-01-01T00:00:00.000Z'; const relativeUrl = 'file://etc/passwd/#/something'; - await expect( + const fn = () => getFullUrls({ - job: { - relativeUrl, - forceNow, - }, + job: { relativeUrl, forceNow }, server: mockServer, - } as FullUrlsOpts) - ).rejects.toMatchInlineSnapshot( - `[Error: Found invalid URL(s), all URLs must be relative: ${relativeUrl}]` + } as FullUrlsOpts); + expect(fn).toThrowErrorMatchingInlineSnapshot( + `"Found invalid URL(s), all URLs must be relative: file://etc/passwd/#/something"` ); }); @@ -52,36 +49,29 @@ test(`fails if URLs are absolute for PNGs`, async () => { const forceNow = '2000-01-01T00:00:00.000Z'; const relativeUrl = 'http://169.254.169.254/latest/meta-data/iam/security-credentials/profileName/#/something'; - await expect( + const fn = () => getFullUrls({ - job: { - relativeUrl, - forceNow, - }, + job: { relativeUrl, forceNow }, server: mockServer, - } as FullUrlsOpts) - ).rejects.toMatchInlineSnapshot( - `[Error: Found invalid URL(s), all URLs must be relative: ${relativeUrl}]` + } as FullUrlsOpts); + expect(fn).toThrowErrorMatchingInlineSnapshot( + `"Found invalid URL(s), all URLs must be relative: http://169.254.169.254/latest/meta-data/iam/security-credentials/profileName/#/something"` ); }); test(`fails if URLs are file-protocols for PDF`, async () => { const forceNow = '2000-01-01T00:00:00.000Z'; const relativeUrl = 'file://etc/passwd/#/something'; - await expect( + const fn = () => getFullUrls({ job: { - objects: [ - { - relativeUrl, - }, - ], + relativeUrls: [relativeUrl], forceNow, }, server: mockServer, - } as FullUrlsOpts) - ).rejects.toMatchInlineSnapshot( - `[Error: Found invalid URL(s), all URLs must be relative: ${relativeUrl}]` + } as FullUrlsOpts); + expect(fn).toThrowErrorMatchingInlineSnapshot( + `"Found invalid URL(s), all URLs must be relative: file://etc/passwd/#/something"` ); }); @@ -89,70 +79,52 @@ test(`fails if URLs are absolute for PDF`, async () => { const forceNow = '2000-01-01T00:00:00.000Z'; const relativeUrl = 'http://169.254.169.254/latest/meta-data/iam/security-credentials/profileName/#/something'; - await expect( + const fn = () => getFullUrls({ job: { - objects: [ - { - relativeUrl, - }, - ], + relativeUrls: [relativeUrl], forceNow, }, server: mockServer, - } as FullUrlsOpts) - ).rejects.toMatchInlineSnapshot( - `[Error: Found invalid URL(s), all URLs must be relative: ${relativeUrl}]` + } as FullUrlsOpts); + expect(fn).toThrowErrorMatchingInlineSnapshot( + `"Found invalid URL(s), all URLs must be relative: http://169.254.169.254/latest/meta-data/iam/security-credentials/profileName/#/something"` ); }); test(`fails if any URLs are absolute or file's for PDF`, async () => { const forceNow = '2000-01-01T00:00:00.000Z'; - const objects = [ - { - relativeUrl: '/app/kibana#/something_aaa', - }, - { - relativeUrl: - 'http://169.254.169.254/latest/meta-data/iam/security-credentials/profileName/#/something', - }, - { - relativeUrl: 'file://etc/passwd/#/something', - }, + const relativeUrls = [ + '/app/kibana#/something_aaa', + 'http://169.254.169.254/latest/meta-data/iam/security-credentials/profileName/#/something', + 'file://etc/passwd/#/something', ]; - await expect( + + const fn = () => getFullUrls({ - job: { - objects, - forceNow, - }, + job: { relativeUrls, forceNow }, server: mockServer, - } as FullUrlsOpts) - ).rejects.toMatchInlineSnapshot( - `[Error: Found invalid URL(s), all URLs must be relative: ${objects[1].relativeUrl} ${objects[2].relativeUrl}]` + } as FullUrlsOpts); + expect(fn).toThrowErrorMatchingInlineSnapshot( + `"Found invalid URL(s), all URLs must be relative: http://169.254.169.254/latest/meta-data/iam/security-credentials/profileName/#/something file://etc/passwd/#/something"` ); }); test(`fails if URL does not route to a visualization`, async () => { - await expect( + const fn = () => getFullUrls({ - job: { - relativeUrl: '/app/phoney', - }, + job: { relativeUrl: '/app/phoney' }, server: mockServer, - } as FullUrlsOpts) - ).rejects.toMatchInlineSnapshot( - `[Error: No valid hash in the URL! A hash is expected for the application to route to the intended visualization.]` + } as FullUrlsOpts); + expect(fn).toThrowErrorMatchingInlineSnapshot( + `"No valid hash in the URL! A hash is expected for the application to route to the intended visualization."` ); }); test(`adds forceNow to hash's query, if it exists`, async () => { const forceNow = '2000-01-01T00:00:00.000Z'; - const { urls } = await getFullUrls({ - job: { - relativeUrl: '/app/kibana#/something', - forceNow, - }, + const urls = await getFullUrls({ + job: { relativeUrl: '/app/kibana#/something', forceNow }, server: mockServer, } as FullUrlsOpts); @@ -164,11 +136,8 @@ test(`adds forceNow to hash's query, if it exists`, async () => { test(`appends forceNow to hash's query, if it exists`, async () => { const forceNow = '2000-01-01T00:00:00.000Z'; - const { urls } = await getFullUrls({ - job: { - relativeUrl: '/app/kibana#/something?_g=something', - forceNow, - }, + const urls = await getFullUrls({ + job: { relativeUrl: '/app/kibana#/something?_g=something', forceNow }, server: mockServer, } as FullUrlsOpts); @@ -178,10 +147,8 @@ test(`appends forceNow to hash's query, if it exists`, async () => { }); test(`doesn't append forceNow query to url, if it doesn't exists`, async () => { - const { urls } = await getFullUrls({ - job: { - relativeUrl: '/app/kibana#/something', - }, + const urls = await getFullUrls({ + job: { relativeUrl: '/app/kibana#/something' }, server: mockServer, } as FullUrlsOpts); @@ -190,13 +157,13 @@ test(`doesn't append forceNow query to url, if it doesn't exists`, async () => { test(`adds forceNow to each of multiple urls`, async () => { const forceNow = '2000-01-01T00:00:00.000Z'; - const { urls } = await getFullUrls({ + const urls = await getFullUrls({ job: { - objects: [ - { relativeUrl: '/app/kibana#/something_aaa' }, - { relativeUrl: '/app/kibana#/something_bbb' }, - { relativeUrl: '/app/kibana#/something_ccc' }, - { relativeUrl: '/app/kibana#/something_ddd' }, + relativeUrls: [ + '/app/kibana#/something_aaa', + '/app/kibana#/something_bbb', + '/app/kibana#/something_ccc', + '/app/kibana#/something_ddd', ], forceNow, }, diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_full_urls.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_full_urls.ts index 59c748aba9662..ca64d8632dbfe 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_full_urls.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_full_urls.ts @@ -12,7 +12,7 @@ import { } from 'url'; import { getAbsoluteUrlFactory } from '../../../common/get_absolute_url'; import { validateUrls } from '../../../common/validate_urls'; -import { ServerFacade, ConditionalHeaders } from '../../../types'; +import { ServerFacade } from '../../../types'; import { JobDocPayloadPNG } from '../../png/types'; import { JobDocPayloadPDF } from '../../printable_pdf/types'; @@ -20,18 +20,15 @@ function isPngJob(job: JobDocPayloadPNG | JobDocPayloadPDF): job is JobDocPayloa return (job as JobDocPayloadPNG).relativeUrl !== undefined; } function isPdfJob(job: JobDocPayloadPNG | JobDocPayloadPDF): job is JobDocPayloadPDF { - return (job as JobDocPayloadPDF).objects !== undefined; + return (job as JobDocPayloadPDF).relativeUrls !== undefined; } -export async function getFullUrls({ - job, +export function getFullUrls({ server, - ...mergeValues // pass-throughs + job, }: { - job: JobDocPayloadPDF | JobDocPayloadPNG; server: ServerFacade; - conditionalHeaders: ConditionalHeaders; - logo?: string; + job: JobDocPayloadPDF | JobDocPayloadPNG; }) { const config = server.config(); @@ -48,10 +45,10 @@ export async function getFullUrls({ if (isPngJob(job)) { relativeUrls = [job.relativeUrl]; } else if (isPdfJob(job)) { - relativeUrls = job.objects.map(obj => obj.relativeUrl); + relativeUrls = job.relativeUrls; } else { throw new Error( - `No valid URL fields found in Job Params! Expected \`job.relativeUrl\` or \`job.objects[{ relativeUrl }]\`` + `No valid URL fields found in Job Params! Expected \`job.relativeUrl: string\` or \`job.relativeUrls: string[]\`` ); } @@ -96,5 +93,5 @@ export async function getFullUrls({ }); }); - return { job, server, urls, ...mergeValues }; + return urls; } diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/omit_blacklisted_headers.test.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/omit_blacklisted_headers.test.ts index 4d4b0c8ade3f6..f446369fec78c 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/omit_blacklisted_headers.test.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/omit_blacklisted_headers.test.ts @@ -4,14 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createMockServer } from '../../../test_helpers/create_mock_server'; import { omitBlacklistedHeaders } from './index'; -let mockServer: any; -beforeEach(() => { - mockServer = createMockServer(''); -}); - test(`omits blacklisted headers`, async () => { const permittedHeaders = { foo: 'bar', @@ -27,7 +21,7 @@ test(`omits blacklisted headers`, async () => { 'transfer-encoding': '', }; - const { filteredHeaders } = await omitBlacklistedHeaders({ + const filteredHeaders = await omitBlacklistedHeaders({ job: { title: 'cool-job-bro', type: 'csv', @@ -41,7 +35,6 @@ test(`omits blacklisted headers`, async () => { ...permittedHeaders, ...blacklistedHeaders, }, - server: mockServer, }); expect(filteredHeaders).toEqual(permittedHeaders); diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/omit_blacklisted_headers.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/omit_blacklisted_headers.ts index 1bd52a0f1b2d2..cbebd6bc21b0e 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/omit_blacklisted_headers.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/omit_blacklisted_headers.ts @@ -5,20 +5,17 @@ */ import { omit } from 'lodash'; import { KBN_SCREENSHOT_HEADER_BLACKLIST } from '../../../common/constants'; -import { ServerFacade } from '../../../types'; export const omitBlacklistedHeaders = ({ job, decryptedHeaders, - server, }: { job: JobDocPayloadType; decryptedHeaders: Record; - server: ServerFacade; }) => { const filteredHeaders: Record = omit( decryptedHeaders, KBN_SCREENSHOT_HEADER_BLACKLIST ); - return { job, filteredHeaders, server }; + return filteredHeaders; }; diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts index 152ef32e331b9..b83021d5e38dd 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts @@ -5,21 +5,9 @@ */ import * as Rx from 'rxjs'; -import { first, mergeMap } from 'rxjs/operators'; -import { - ServerFacade, - CaptureConfig, - HeadlessChromiumDriverFactory, - HeadlessChromiumDriver as HeadlessBrowser, -} from '../../../../types'; -import { - ElementsPositionAndAttribute, - ScreenshotResults, - ScreenshotObservableOpts, - TimeRange, -} from './types'; - -import { checkForToastMessage } from './check_for_toast'; +import { first, mergeMap, toArray } from 'rxjs/operators'; +import { ServerFacade, CaptureConfig, HeadlessChromiumDriverFactory } from '../../../../types'; +import { ScreenshotResults, ScreenshotObservableOpts } from './types'; import { injectCustomCss } from './inject_css'; import { openUrl } from './open_url'; import { waitForRenderComplete } from './wait_for_render'; @@ -28,6 +16,7 @@ import { waitForElementsToBeInDOM } from './wait_for_dom_elements'; import { getTimeRange } from './get_time_range'; import { getElementPositionAndAttributes } from './get_element_position_data'; import { getScreenshots } from './get_screenshots'; +import { scanPage } from './scan_page'; import { skipTelemetry } from './skip_telemetry'; export function screenshotsObservableFactory( @@ -39,108 +28,68 @@ export function screenshotsObservableFactory( return function screenshotsObservable({ logger, - url, + urls, conditionalHeaders, layout, browserTimezone, - }: ScreenshotObservableOpts): Rx.Observable { - const create$ = browserDriverFactory.create({ - viewport: layout.getBrowserViewport(), - browserTimezone, - }); + }: ScreenshotObservableOpts): Rx.Observable { + const create$ = browserDriverFactory.createPage( + { viewport: layout.getBrowserViewport(), browserTimezone }, + logger + ); - // @ts-ignore this needs to be refactored to use less random type declaration and instead rely on structures that work with inference TODO - return create$.pipe( - mergeMap(({ driver$, exit$ }) => { - const screenshot$ = driver$.pipe( - mergeMap( - (browser: HeadlessBrowser) => openUrl(browser, url, conditionalHeaders, logger), - browser => browser - ), - mergeMap( - (browser: HeadlessBrowser) => skipTelemetry(browser, logger), - browser => browser - ), - mergeMap( - (browser: HeadlessBrowser) => { - logger.debug( - 'waiting for elements or items count attribute; or not found to interrupt' - ); + return Rx.from(urls).pipe( + mergeMap(url => { + return create$.pipe( + mergeMap(({ driver, exit$ }) => { + const screenshot$ = Rx.of(driver).pipe( + mergeMap(() => openUrl(driver, url, conditionalHeaders, logger)), + mergeMap(() => skipTelemetry(driver, logger)), + mergeMap(() => scanPage(driver, layout, logger)), + mergeMap(() => getNumberOfItems(driver, layout, logger)), + mergeMap(async itemsCount => { + const viewport = layout.getViewport(itemsCount); + await Promise.all([ + driver.setViewport(viewport, logger), + waitForElementsToBeInDOM(driver, itemsCount, layout, logger), + ]); + }), + mergeMap(async () => { + // Waiting till _after_ elements have rendered before injecting our CSS + // allows for them to be displayed properly in many cases + await injectCustomCss(driver, layout, logger); - // the dashboard is using the `itemsCountAttribute` attribute to let us - // know how many items to expect since gridster incrementally adds panels - // we have to use this hint to wait for all of them - const renderSuccess = browser.waitForSelector( - `${layout.selectors.renderComplete},[${layout.selectors.itemsCountAttribute}]`, - {}, - logger - ); - const renderError = checkForToastMessage(browser, layout, logger); - return Rx.race(Rx.from(renderSuccess), Rx.from(renderError)); - }, - browser => browser - ), - mergeMap( - (browser: HeadlessBrowser) => getNumberOfItems(browser, layout, logger), - (browser, itemsCount: number) => ({ browser, itemsCount }) - ), - mergeMap( - async ({ browser, itemsCount }) => { - logger.debug('setting viewport'); - const viewport = layout.getViewport(itemsCount); - return await browser.setViewport(viewport, logger); - }, - ({ browser, itemsCount }) => ({ browser, itemsCount }) - ), - mergeMap( - ({ browser, itemsCount }) => - waitForElementsToBeInDOM(browser, itemsCount, layout, logger), - ({ browser }) => browser - ), - mergeMap( - browser => { - // Waiting till _after_ elements have rendered before injecting our CSS - // allows for them to be displayed properly in many cases - return injectCustomCss(browser, layout, logger); - }, - browser => browser - ), - mergeMap( - async browser => { - if (layout.positionElements) { - // position panel elements for print layout - return await layout.positionElements(browser, logger); - } - }, - browser => browser - ), - mergeMap( - (browser: HeadlessBrowser) => { - return waitForRenderComplete(captureConfig, browser, layout, logger); - }, - browser => browser - ), - mergeMap( - browser => getTimeRange(browser, layout, logger), - (browser, timeRange: TimeRange | null) => ({ browser, timeRange }) - ), - mergeMap( - ({ browser }) => getElementPositionAndAttributes(browser, layout), - ({ browser, timeRange }, elementsPositionAndAttributes: ElementsPositionAndAttribute[]) => { - return { browser, timeRange, elementsPositionAndAttributes }; - } // prettier-ignore - ), - mergeMap( - ({ browser, elementsPositionAndAttributes }) => { - return getScreenshots({ browser, elementsPositionAndAttributes, logger }); - }, - ({ timeRange }, screenshots) => ({ timeRange, screenshots }) - ) - ); + if (layout.positionElements) { + // position panel elements for print layout + await layout.positionElements(driver, logger); + } - return Rx.race(screenshot$, exit$); + await waitForRenderComplete(captureConfig, driver, layout, logger); + }), + mergeMap(() => getTimeRange(driver, layout, logger)), + mergeMap( + async (timeRange): Promise => { + const elementsPositionAndAttributes = await getElementPositionAndAttributes( + driver, + layout + ); + const screenshots = await getScreenshots({ + browser: driver, + elementsPositionAndAttributes, + logger, + }); + + return { timeRange, screenshots }; + } + ) + ); + + return Rx.race(screenshot$, exit$); + }) + ); }), - first() + first(), + toArray() ); }; } diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/scan_page.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/scan_page.ts new file mode 100644 index 0000000000000..81ff01bb204b8 --- /dev/null +++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/scan_page.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as Rx from 'rxjs'; +import { HeadlessChromiumDriver } from '../../../../server/browsers/chromium/driver'; +import { LevelLogger } from '../../../../server/lib'; +import { LayoutInstance } from '../../layouts/layout'; +import { checkForToastMessage } from './check_for_toast'; + +export function scanPage( + browser: HeadlessChromiumDriver, + layout: LayoutInstance, + logger: LevelLogger +) { + logger.debug('waiting for elements or items count attribute; or not found to interrupt'); + + // the dashboard is using the `itemsCountAttribute` attribute to let us + // know how many items to expect since gridster incrementally adds panels + // we have to use this hint to wait for all of them + const renderSuccess = browser.waitForSelector( + `${layout.selectors.renderComplete},[${layout.selectors.itemsCountAttribute}]`, + {}, + logger + ); + const renderError = checkForToastMessage(browser, layout, logger); + return Rx.race(Rx.from(renderSuccess), Rx.from(renderError)); +} diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/types.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/types.ts index 4f461ef4ec5f9..78cd42f0cae2f 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/types.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/types.ts @@ -10,7 +10,7 @@ import { LayoutInstance } from '../../layouts/layout'; export interface ScreenshotObservableOpts { logger: LevelLogger; - url: string; + urls: string[]; conditionalHeaders: ConditionalHeaders; layout: LayoutInstance; browserTimezone: string; @@ -36,6 +36,6 @@ export interface Screenshot { } export interface ScreenshotResults { - timeRange: TimeRange; + timeRange: TimeRange | null; screenshots: Screenshot[]; } diff --git a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js index 0888d287af07c..1be65722fa668 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js +++ b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js @@ -27,7 +27,7 @@ beforeEach(() => { 'server.port': 5601, }; mockServer = { - expose: () => {}, + expose: () => {}, // NOTE: this is for oncePerServer config: memoize(() => ({ get: jest.fn() })), info: { protocol: 'http', diff --git a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts index b289ae45dde67..c2fda05fbe3e9 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts @@ -5,7 +5,7 @@ */ import * as Rx from 'rxjs'; -import { mergeMap, catchError, map, takeUntil } from 'rxjs/operators'; +import { catchError, map, mergeMap, takeUntil } from 'rxjs/operators'; import { PLUGIN_ID, PNG_JOB_TYPE } from '../../../../common/constants'; import { ServerFacade, @@ -32,18 +32,14 @@ export const executeJobFactory: QueuedPngExecutorFactory = function executeJobFa const generatePngObservable = generatePngObservableFactory(server, browserDriverFactory); const logger = LevelLogger.createForServer(server, [PLUGIN_ID, PNG_JOB_TYPE, 'execute']); - return function executeJob( - jobId: string, - jobToExecute: JobDocPayloadPNG, - cancellationToken: any - ) { + return function executeJob(jobId: string, job: JobDocPayloadPNG, cancellationToken: any) { const jobLogger = logger.clone([jobId]); - const process$ = Rx.of({ job: jobToExecute, server, logger }).pipe( - mergeMap(decryptJobHeaders), - map(omitBlacklistedHeaders), - map(getConditionalHeaders), - mergeMap(getFullUrls), - mergeMap(({ job, conditionalHeaders, urls }) => { + const process$ = Rx.of(1).pipe( + mergeMap(() => decryptJobHeaders({ server, job, logger })), + map(decryptedHeaders => omitBlacklistedHeaders({ job, decryptedHeaders })), + map(filteredHeaders => getConditionalHeaders({ server, job, filteredHeaders })), + mergeMap(conditionalHeaders => { + const urls = getFullUrls({ server, job }); const hashUrl = urls[0]; return generatePngObservable( jobLogger, diff --git a/x-pack/legacy/plugins/reporting/export_types/png/server/lib/generate_png.ts b/x-pack/legacy/plugins/reporting/export_types/png/server/lib/generate_png.ts index e2b1474515786..600762c451a79 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/server/lib/generate_png.ts +++ b/x-pack/legacy/plugins/reporting/export_types/png/server/lib/generate_png.ts @@ -32,19 +32,17 @@ export function generatePngObservableFactory( const layout = new PreserveLayout(layoutParams.dimensions); const screenshots$ = screenshotsObservable({ logger, - url, + urls: [url], conditionalHeaders, layout, browserTimezone, }).pipe( - map(urlScreenshots => { - if (urlScreenshots.screenshots.length !== 1) { - throw new Error( - `Expected there to be 1 screenshot, but there are ${urlScreenshots.screenshots.length}` - ); + map(([{ screenshots }]) => { + if (screenshots.length !== 1) { + throw new Error(`Expected there to be 1 screenshot, but there are ${screenshots.length}`); } - return urlScreenshots.screenshots[0].base64EncodedData; + return screenshots[0].base64EncodedData; }) ); diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/compatibility_shim.js b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/compatibility_shim.js deleted file mode 100644 index a3afd070603b0..0000000000000 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/compatibility_shim.js +++ /dev/null @@ -1,126 +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 { uriEncode } from '../lib/uri_encode'; - -/* - * TODO: Kibana 8.0: - * Remove support for parsing Saved Object details from objectType / savedObjectId - * Including support for determining the Report title from objectType / savedObjectId - * - * - `objectType` is optional, but helps differentiate the type of report in the job listing - * - `title` must be explicitly passed - * - `relativeUrls` array OR `relativeUrl` string must be passed - */ - -const getSavedObjectTitle = async (objectType, savedObjectId, savedObjectsClient) => { - const savedObject = await savedObjectsClient.get(objectType, savedObjectId); - return savedObject.attributes.title; -}; - -const getSavedObjectRelativeUrl = (objectType, savedObjectId, queryString) => { - const appPrefixes = { - dashboard: '/dashboard/', - visualization: '/visualize/edit/', - search: '/discover/', - }; - - const appPrefix = appPrefixes[objectType]; - if (!appPrefix) throw new Error('Unexpected app type: ' + objectType); - - const hash = appPrefix + uriEncode.string(savedObjectId, true); - - return `/app/kibana#${hash}?${queryString || ''}`; -}; - -export function compatibilityShimFactory(server, logger) { - return function compatibilityShimFactory(createJobFn) { - return async function( - { - savedObjectId, // deprecating - queryString, // deprecating - browserTimezone, - objectType, - title, - relativeUrls, - layout, - }, - headers, - request - ) { - // input validation and deprecation logging - if (savedObjectId) { - if (typeof savedObjectId !== 'string') { - throw new Error('Invalid savedObjectId (deprecated). String is expected.'); - } - if (relativeUrls) { - throw new Error(`savedObjectId should not be provided if relativeUrls are provided`); - } - } else { - if (!relativeUrls) { - throw new Error(`Either relativeUrls or savedObjectId must be provided`); - } - if (!Array.isArray(relativeUrls)) { - throw new Error('Invalid relativeUrls. String[] is expected.'); - } - relativeUrls.forEach(url => { - if (typeof url !== 'string') { - throw new Error('Invalid Relative URL in relativeUrls. String is expected.'); - } - }); - } - - let kibanaRelativeUrls; - if (relativeUrls) { - kibanaRelativeUrls = relativeUrls; - } else { - kibanaRelativeUrls = [getSavedObjectRelativeUrl(objectType, savedObjectId, queryString)]; - logger.warning( - `The relativeUrls have been derived from saved object parameters. ` + - `This functionality will be removed with the next major version.` - ); - } - - let reportTitle; - try { - if (title) { - reportTitle = title; - } else { - if (objectType && savedObjectId) { - reportTitle = await getSavedObjectTitle( - objectType, - savedObjectId, - request.getSavedObjectsClient() - ); - logger.warning( - `The title has been derived from saved object parameters. This ` + - `functionality will be removed with the next major version.` - ); - } else { - logger.warning( - `A title parameter should be provided with the job generation ` + - `request. Please use Kibana to regenerate your POST URL to have a ` + - `title included in the PDF.` - ); - } - } - } catch (err) { - logger.error(err); // 404 for the savedObjectId, etc - throw err; - } - - const transformedJobParams = { - objectType, - title: reportTitle, - relativeUrls: kibanaRelativeUrls, - browserTimezone, - layout, - }; - - return await createJobFn(transformedJobParams, headers, request); - }; - }; -} diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/compatibility_shim.test.js b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/compatibility_shim.test.js deleted file mode 100644 index b3b8bca1d8955..0000000000000 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/compatibility_shim.test.js +++ /dev/null @@ -1,297 +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 { once } from 'lodash'; -import { compatibilityShimFactory } from './compatibility_shim'; - -const createMockServer = () => { - return { - expose: jest.fn(), //fool once_per_server - log: jest.fn(), - }; -}; - -const createMockLogger = () => ({ - warning: jest.fn(), - error: jest.fn(), -}); - -const createMockRequest = () => { - return { - getSavedObjectsClient: once(function() { - return { - get: jest.fn(), - }; - }), - }; -}; - -test(`passes title through if provided`, async () => { - const mockLogger = createMockLogger(); - const compatibilityShim = compatibilityShimFactory(createMockServer(), mockLogger); - const title = 'test title'; - - const createJobMock = jest.fn(); - await compatibilityShim(createJobMock)( - { title, relativeUrls: ['/something'] }, - null, - createMockRequest() - ); - - expect(mockLogger.warning.mock.calls.length).toBe(0); - expect(mockLogger.error.mock.calls.length).toBe(0); - - expect(createJobMock.mock.calls.length).toBe(1); - expect(createJobMock.mock.calls[0][0].title).toBe(title); -}); - -test(`gets the title from the savedObject`, async () => { - const mockLogger = createMockLogger(); - const compatibilityShim = compatibilityShimFactory(createMockServer(), mockLogger); - - const createJobMock = jest.fn(); - const mockRequest = createMockRequest(); - const title = 'savedTitle'; - mockRequest.getSavedObjectsClient().get.mockReturnValue({ - attributes: { - title, - }, - }); - - await compatibilityShim(createJobMock)( - { objectType: 'search', savedObjectId: 'abc' }, - null, - mockRequest - ); - - expect(mockLogger.warning.mock.calls.length).toBe(2); - expect(mockLogger.warning.mock.calls[0][0]).toEqual( - 'The relativeUrls have been derived from saved object parameters. This functionality will be removed with the next major version.' - ); - expect(mockLogger.error.mock.calls.length).toBe(0); - - expect(createJobMock.mock.calls.length).toBe(1); - expect(createJobMock.mock.calls[0][0].title).toBe(title); -}); - -test(`passes the objectType and savedObjectId to the savedObjectsClient`, async () => { - const mockLogger = createMockLogger(); - const compatibilityShim = compatibilityShimFactory(createMockServer(), mockLogger); - - const createJobMock = jest.fn(); - const mockRequest = createMockRequest(); - mockRequest.getSavedObjectsClient().get.mockReturnValue({ - attributes: { - title: '', - }, - }); - - const objectType = 'search'; - const savedObjectId = 'abc'; - await compatibilityShim(createJobMock)({ objectType, savedObjectId }, null, mockRequest); - - expect(mockLogger.warning.mock.calls.length).toBe(2); - expect(mockLogger.warning.mock.calls[0][0]).toEqual( - 'The relativeUrls have been derived from saved object parameters. This functionality will be removed with the next major version.' - ); - expect(mockLogger.warning.mock.calls[1][0]).toEqual( - 'The title has been derived from saved object parameters. This functionality will be removed with the next major version.' - ); - expect(mockLogger.error.mock.calls.length).toBe(0); - - const getMock = mockRequest.getSavedObjectsClient().get.mock; - expect(getMock.calls.length).toBe(1); - expect(getMock.calls[0][0]).toBe(objectType); - expect(getMock.calls[0][1]).toBe(savedObjectId); -}); - -test(`logs no warnings when title and relativeUrls is passed`, async () => { - const mockLogger = createMockLogger(); - const compatibilityShim = compatibilityShimFactory(createMockServer(), mockLogger); - - const createJobMock = jest.fn(); - const mockRequest = createMockRequest(); - - await compatibilityShim(createJobMock)( - { title: 'Phenomenal Dashboard', relativeUrls: ['/abc', '/def'] }, - null, - mockRequest - ); - - expect(mockLogger.warning.mock.calls.length).toBe(0); - expect(mockLogger.error.mock.calls.length).toBe(0); -}); - -test(`logs warning if title can not be provided`, async () => { - const mockLogger = createMockLogger(); - const compatibilityShim = compatibilityShimFactory(createMockServer(), mockLogger); - - const createJobMock = jest.fn(); - const mockRequest = createMockRequest(); - await compatibilityShim(createJobMock)({ relativeUrls: ['/abc'] }, null, mockRequest); - - expect(mockLogger.warning.mock.calls.length).toBe(1); - expect(mockLogger.warning.mock.calls[0][0]).toEqual( - `A title parameter should be provided with the job generation request. Please ` + - `use Kibana to regenerate your POST URL to have a title included in the PDF.` - ); -}); - -test(`logs deprecations when generating the title/relativeUrl using the savedObject`, async () => { - const mockLogger = createMockLogger(); - const compatibilityShim = compatibilityShimFactory(createMockServer(), mockLogger); - - const createJobMock = jest.fn(); - const mockRequest = createMockRequest(); - mockRequest.getSavedObjectsClient().get.mockReturnValue({ - attributes: { - title: '', - }, - }); - - await compatibilityShim(createJobMock)( - { objectType: 'search', savedObjectId: 'abc' }, - null, - mockRequest - ); - - expect(mockLogger.warning.mock.calls.length).toBe(2); - expect(mockLogger.warning.mock.calls[0][0]).toEqual( - 'The relativeUrls have been derived from saved object parameters. This functionality will be removed with the next major version.' - ); - expect(mockLogger.warning.mock.calls[1][0]).toEqual( - 'The title has been derived from saved object parameters. This functionality will be removed with the next major version.' - ); -}); - -test(`passes objectType through`, async () => { - const mockLogger = createMockLogger(); - const compatibilityShim = compatibilityShimFactory(createMockServer(), mockLogger); - - const createJobMock = jest.fn(); - const mockRequest = createMockRequest(); - - const objectType = 'foo'; - await compatibilityShim(createJobMock)( - { title: 'test', relativeUrls: ['/something'], objectType }, - null, - mockRequest - ); - - expect(mockLogger.warning.mock.calls.length).toBe(0); - expect(mockLogger.error.mock.calls.length).toBe(0); - - expect(createJobMock.mock.calls.length).toBe(1); - expect(createJobMock.mock.calls[0][0].objectType).toBe(objectType); -}); - -test(`passes the relativeUrls through`, async () => { - const mockLogger = createMockLogger(); - const compatibilityShim = compatibilityShimFactory(createMockServer(), mockLogger); - - const createJobMock = jest.fn(); - - const relativeUrls = ['/app/kibana#something', '/app/kibana#something-else']; - await compatibilityShim(createJobMock)({ title: 'test', relativeUrls }, null, null); - - expect(mockLogger.warning.mock.calls.length).toBe(0); - expect(mockLogger.error.mock.calls.length).toBe(0); - - expect(createJobMock.mock.calls.length).toBe(1); - expect(createJobMock.mock.calls[0][0].relativeUrls).toBe(relativeUrls); -}); - -const testSavedObjectRelativeUrl = (objectType, expectedUrl) => { - test(`generates the saved object relativeUrl for ${objectType}`, async () => { - const mockLogger = createMockLogger(); - const compatibilityShim = compatibilityShimFactory(createMockServer(), mockLogger); - const createJobMock = jest.fn(); - - await compatibilityShim(createJobMock)( - { title: 'test', objectType, savedObjectId: 'abc' }, - null, - null - ); - - expect(mockLogger.warning.mock.calls.length).toBe(1); - expect(mockLogger.warning.mock.calls[0][0]).toEqual( - 'The relativeUrls have been derived from saved object parameters. This functionality will be removed with the next major version.' - ); - expect(mockLogger.error.mock.calls.length).toBe(0); - - expect(createJobMock.mock.calls.length).toBe(1); - expect(createJobMock.mock.calls[0][0].relativeUrls).toEqual([expectedUrl]); - }); -}; - -testSavedObjectRelativeUrl('search', '/app/kibana#/discover/abc?'); -testSavedObjectRelativeUrl('visualization', '/app/kibana#/visualize/edit/abc?'); -testSavedObjectRelativeUrl('dashboard', '/app/kibana#/dashboard/abc?'); - -test(`appends the queryString to the relativeUrl when generating from the savedObject`, async () => { - const mockLogger = createMockLogger(); - const compatibilityShim = compatibilityShimFactory(createMockServer(), mockLogger); - const createJobMock = jest.fn(); - - await compatibilityShim(createJobMock)( - { title: 'test', objectType: 'search', savedObjectId: 'abc', queryString: 'foo=bar' }, - null, - null - ); - - expect(mockLogger.warning.mock.calls.length).toBe(1); - expect(mockLogger.warning.mock.calls[0][0]).toEqual( - 'The relativeUrls have been derived from saved object parameters. This functionality will be removed with the next major version.' - ); - expect(mockLogger.error.mock.calls.length).toBe(0); - - expect(createJobMock.mock.calls.length).toBe(1); - expect(createJobMock.mock.calls[0][0].relativeUrls).toEqual([ - '/app/kibana#/discover/abc?foo=bar', - ]); -}); - -test(`throw an Error if the objectType, savedObjectId and relativeUrls are provided`, async () => { - const mockLogger = createMockLogger(); - const compatibilityShim = compatibilityShimFactory(createMockServer(), mockLogger); - const createJobMock = jest.fn(); - - const promise = compatibilityShim(createJobMock)( - { - title: 'test', - objectType: 'something', - relativeUrls: ['/something'], - savedObjectId: 'abc', - }, - null, - null - ); - - await expect(promise).rejects.toBeDefined(); -}); - -test(`passes headers and request through`, async () => { - const mockLogger = createMockLogger(); - const compatibilityShim = compatibilityShimFactory(createMockServer(), mockLogger); - - const createJobMock = jest.fn(); - - const headers = {}; - const request = createMockRequest(); - - await compatibilityShim(createJobMock)( - { title: 'test', relativeUrls: ['/something'] }, - headers, - request - ); - - expect(mockLogger.warning.mock.calls.length).toBe(0); - expect(mockLogger.error.mock.calls.length).toBe(0); - - expect(createJobMock.mock.calls.length).toBe(1); - expect(createJobMock.mock.calls[0][1]).toBe(headers); - expect(createJobMock.mock.calls[0][2]).toBe(request); -}); diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/index.ts index d17713057abc1..a8cc71175cffe 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/index.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PLUGIN_ID, PDF_JOB_TYPE } from '../../../../common/constants'; import { CreateJobFactory, ESQueueCreateJobFn, @@ -13,29 +12,16 @@ import { ConditionalHeaders, } from '../../../../types'; import { validateUrls } from '../../../../common/validate_urls'; -import { LevelLogger } from '../../../../server/lib'; import { cryptoFactory } from '../../../../server/lib/crypto'; import { JobParamsPDF } from '../../types'; -// @ts-ignore untyped module -import { compatibilityShimFactory } from './compatibility_shim'; - -interface CreateJobFnOpts { - objectType: any; - title: string; - relativeUrls: string[]; - browserTimezone: string; - layout: any; -} export const createJobFactory: CreateJobFactory> = function createJobFactoryFn(server: ServerFacade) { - const logger = LevelLogger.createForServer(server, [PLUGIN_ID, PDF_JOB_TYPE, 'create']); - const compatibilityShim = compatibilityShimFactory(server, logger); const crypto = cryptoFactory(server); - return compatibilityShim(async function createJobFn( - { objectType, title, relativeUrls, browserTimezone, layout }: CreateJobFnOpts, + return async function createJobFn( + { title, relativeUrls, browserTimezone, layout, objectType }: JobParamsPDF, headers: ConditionalHeaders['headers'], request: RequestFacade ) { @@ -44,14 +30,14 @@ export const createJobFactory: CreateJobFactory ({ relativeUrl: u })), - headers: serializedEncryptedHeaders, - browserTimezone, - layout, basePath: request.getBasePath(), + browserTimezone, forceNow: new Date().toISOString(), + headers: serializedEncryptedHeaders, + layout, + relativeUrls, + title, + objectType, }; - }); + }; }; diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.test.js b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.test.js index db7b599a1aaab..ddbee6e9d54a4 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.test.js +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.test.js @@ -27,7 +27,8 @@ beforeEach(() => { 'server.port': 5601, }; mockServer = { - expose: () => {}, + expose: jest.fn(), + log: jest.fn(), config: memoize(() => ({ get: jest.fn() })), info: { protocol: 'http', @@ -71,7 +72,7 @@ test(`passes browserTimezone to generatePdf`, async () => { const browserTimezone = 'UTC'; await executeJob( 'pdfJobId', - { objects: [], browserTimezone, headers: encryptedHeaders }, + { relativeUrls: [], browserTimezone, headers: encryptedHeaders }, cancellationToken ); @@ -96,7 +97,7 @@ test(`returns content_type of application/pdf`, async () => { const { content_type: contentType } = await executeJob( 'pdfJobId', - { objects: [], timeRange: {}, headers: encryptedHeaders }, + { relativeUrls: [], timeRange: {}, headers: encryptedHeaders }, cancellationToken ); expect(contentType).toBe('application/pdf'); @@ -112,7 +113,7 @@ test(`returns content of generatePdf getBuffer base64 encoded`, async () => { const encryptedHeaders = await encryptHeaders({}); const { content } = await executeJob( 'pdfJobId', - { objects: [], timeRange: {}, headers: encryptedHeaders }, + { relativeUrls: [], timeRange: {}, headers: encryptedHeaders }, cancellationToken ); diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts index e2b3183464cf2..d85207e671212 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts @@ -5,7 +5,7 @@ */ import * as Rx from 'rxjs'; -import { mergeMap, catchError, map, takeUntil } from 'rxjs/operators'; +import { catchError, map, mergeMap, takeUntil } from 'rxjs/operators'; import { ServerFacade, ExecuteJobFactory, @@ -33,33 +33,28 @@ export const executeJobFactory: QueuedPdfExecutorFactory = function executeJobFa const generatePdfObservable = generatePdfObservableFactory(server, browserDriverFactory); const logger = LevelLogger.createForServer(server, [PLUGIN_ID, PDF_JOB_TYPE, 'execute']); - return function executeJob( - jobId: string, - jobToExecute: JobDocPayloadPDF, - cancellationToken: any - ) { + return function executeJob(jobId: string, job: JobDocPayloadPDF, cancellationToken: any) { const jobLogger = logger.clone([jobId]); - const process$ = Rx.of({ job: jobToExecute, server, logger }).pipe( - mergeMap(decryptJobHeaders), - map(omitBlacklistedHeaders), - map(getConditionalHeaders), - mergeMap(getCustomLogo), - mergeMap(getFullUrls), - mergeMap( - ({ job, conditionalHeaders, logo, urls }): Rx.Observable => { - const { browserTimezone, layout } = jobToExecute; - return generatePdfObservable( - jobLogger, - job.title, - urls, - browserTimezone, - conditionalHeaders, - layout, - logo - ); - } - ), + const process$ = Rx.of(1).pipe( + mergeMap(() => decryptJobHeaders({ server, job, logger })), + map(decryptedHeaders => omitBlacklistedHeaders({ job, decryptedHeaders })), + map(filteredHeaders => getConditionalHeaders({ server, job, filteredHeaders })), + mergeMap(conditionalHeaders => getCustomLogo({ server, job, conditionalHeaders })), + mergeMap(({ logo, conditionalHeaders }) => { + const urls = getFullUrls({ server, job }); + + const { browserTimezone, layout, title } = job; + return generatePdfObservable( + jobLogger, + title, + urls, + browserTimezone, + conditionalHeaders, + layout, + logo + ); + }), map((buffer: Buffer) => ({ content_type: 'application/pdf', content: buffer.toString('base64'), @@ -72,7 +67,6 @@ export const executeJobFactory: QueuedPdfExecutorFactory = function executeJobFa ); const stop$ = Rx.fromEventPattern(cancellationToken.on); - return process$.pipe(takeUntil(stop$)).toPromise(); }; }; diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/generate_pdf.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/generate_pdf.ts index 898a13a2dfe80..9a8db308bea79 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/generate_pdf.ts +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/generate_pdf.ts @@ -5,7 +5,7 @@ */ import * as Rx from 'rxjs'; -import { toArray, mergeMap } from 'rxjs/operators'; +import { mergeMap } from 'rxjs/operators'; import { groupBy } from 'lodash'; import { LevelLogger } from '../../../../server/lib'; import { ServerFacade, HeadlessChromiumDriverFactory, ConditionalHeaders } from '../../../../types'; @@ -31,7 +31,6 @@ export function generatePdfObservableFactory( browserDriverFactory: HeadlessChromiumDriverFactory ) { const screenshotsObservable = screenshotsObservableFactory(server, browserDriverFactory); - const captureConcurrency = 1; return function generatePdfObservable( logger: LevelLogger, @@ -41,15 +40,16 @@ export function generatePdfObservableFactory( conditionalHeaders: ConditionalHeaders, layoutParams: LayoutParams, logo?: string - ) { + ): Rx.Observable { const layout = createLayout(server, layoutParams) as LayoutInstance; - const screenshots$ = Rx.from(urls).pipe( - mergeMap( - url => screenshotsObservable({ logger, url, conditionalHeaders, layout, browserTimezone }), - captureConcurrency - ), - toArray(), - mergeMap(async (urlScreenshots: ScreenshotResults[]) => { + const screenshots$ = screenshotsObservable({ + logger, + urls, + conditionalHeaders, + layout, + browserTimezone, + }).pipe( + mergeMap(async urlScreenshots => { const pdfOutput = pdf.create(layout, logo); if (title) { @@ -68,8 +68,7 @@ export function generatePdfObservableFactory( }); pdfOutput.generate(); - const buffer = await pdfOutput.getBuffer(); - return buffer; + return await pdfOutput.getBuffer(); }) ); diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/types.d.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/types.d.ts index b51a2d4d5711c..0a9dcfe986ca6 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/types.d.ts +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/types.d.ts @@ -9,7 +9,7 @@ import { JobDocPayload, ServerFacade, RequestFacade } from '../../types'; // Job params: structure of incoming user request data, after being parsed from RISON export interface JobParamsPDF { - objectType: string; + objectType: string; // visualization, dashboard, etc. Used for job info & telemetry title: string; relativeUrls: string[]; browserTimezone: string; @@ -22,7 +22,5 @@ export interface JobDocPayloadPDF extends JobDocPayload { browserTimezone: string; forceNow?: string; layout: LayoutParams; - objects: Array<{ - relativeUrl: string; - }>; + relativeUrls: string[]; } diff --git a/x-pack/legacy/plugins/reporting/index.ts b/x-pack/legacy/plugins/reporting/index.ts index c0c9e458132f0..faa27bfb2d6ea 100644 --- a/x-pack/legacy/plugins/reporting/index.ts +++ b/x-pack/legacy/plugins/reporting/index.ts @@ -94,7 +94,7 @@ export const reporting = (kibana: any) => { const { xpack_main: xpackMainPlugin } = server.plugins; mirrorPluginStatus(xpackMainPlugin, this); const checkLicense = checkLicenseFactory(exportTypesRegistry); - xpackMainPlugin.status.once('green', () => { + (xpackMainPlugin as any).status.once('green', () => { // Register a function that is called whenever the xpack info changes, // to re-compute the license check results for this plugin xpackMainPlugin.info.feature(this.id).registerLicenseCheckResultsGenerator(checkLicense); diff --git a/x-pack/legacy/plugins/reporting/public/components/report_listing.tsx b/x-pack/legacy/plugins/reporting/public/components/report_listing.tsx index 9783372aa29c4..320f6220aa996 100644 --- a/x-pack/legacy/plugins/reporting/public/components/report_listing.tsx +++ b/x-pack/legacy/plugins/reporting/public/components/report_listing.tsx @@ -425,7 +425,7 @@ class ReportListingUi extends Component { return { id: job._id, type: source.jobtype, - object_type: source.payload.type, + object_type: source.payload.objectType, object_title: source.payload.title, created_by: source.created_by, created_at: source.created_at, diff --git a/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver_factory/index.ts b/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver_factory/index.ts index daa7df343f8aa..6fa46b893de8c 100644 --- a/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver_factory/index.ts +++ b/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver_factory/index.ts @@ -31,10 +31,11 @@ type queueTimeout = number; export class HeadlessChromiumDriverFactory { private binaryPath: binaryPath; - private logger: Logger; private browserConfig: BrowserConfig; private queueTimeout: queueTimeout; private networkPolicy: NetworkPolicy; + private userDataDir: string; + private getChromiumArgs: (viewport: BrowserConfig['viewport']) => string[]; constructor( binaryPath: binaryPath, @@ -46,23 +47,30 @@ export class HeadlessChromiumDriverFactory { this.binaryPath = binaryPath; this.browserConfig = browserConfig; this.queueTimeout = queueTimeout; - this.logger = logger; this.networkPolicy = networkPolicy; + + this.userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chromium-')); + this.getChromiumArgs = (viewport: BrowserConfig['viewport']) => + args({ + userDataDir: this.userDataDir, + viewport, + disableSandbox: this.browserConfig.disableSandbox, + proxy: this.browserConfig.proxy, + }); } type = 'chromium'; - test({ viewport }: { viewport: BrowserConfig['viewport'] }, logger: Logger) { - const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chromium-')); + test(logger: Logger) { const chromiumArgs = args({ - userDataDir, - viewport, + userDataDir: this.userDataDir, + viewport: { width: 800, height: 600 }, disableSandbox: this.browserConfig.disableSandbox, proxy: this.browserConfig.proxy, }); return puppeteerLaunch({ - userDataDir, + userDataDir: this.userDataDir, executablePath: this.binaryPath, ignoreHTTPSErrors: true, args: chromiumArgs, @@ -76,33 +84,25 @@ export class HeadlessChromiumDriverFactory { }); } - create({ - viewport, - browserTimezone, - }: { - viewport: BrowserConfig['viewport']; - browserTimezone: string; - }): Rx.Observable<{ - driver$: Rx.Observable; - exit$: Rx.Observable; - }> { + /* + * Return an observable to objects which will drive screenshot capture for a page + */ + createPage( + { viewport, browserTimezone }: { viewport: BrowserConfig['viewport']; browserTimezone: string }, + pLogger: Logger + ): Rx.Observable<{ driver: HeadlessChromiumDriver; exit$: Rx.Observable }> { return Rx.Observable.create(async (observer: InnerSubscriber) => { - this.logger.debug(`Creating browser driver factory`); + const logger = pLogger.clone(['browser-driver']); + logger.info(`Creating browser page driver`); - const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chromium-')); - const chromiumArgs = args({ - userDataDir, - viewport, - disableSandbox: this.browserConfig.disableSandbox, - proxy: this.browserConfig.proxy, - }); + const chromiumArgs = this.getChromiumArgs(viewport); let browser: Browser; let page: Page; try { browser = await puppeteerLaunch({ pipe: !this.browserConfig.inspect, - userDataDir, + userDataDir: this.userDataDir, executablePath: this.binaryPath, ignoreHTTPSErrors: true, args: chromiumArgs, @@ -119,7 +119,7 @@ export class HeadlessChromiumDriverFactory { // "TimeoutError: waiting for selector ".application" failed: timeout 30000ms exceeded" page.setDefaultTimeout(this.queueTimeout); - this.logger.debug(`Browser driver factory created`); + logger.debug(`Browser page driver created`); } catch (err) { observer.error(new Error(`Error spawning Chromium browser: [${err}]`)); throw err; @@ -130,12 +130,12 @@ export class HeadlessChromiumDriverFactory { await browser.close(); }, }; - const { terminate$ } = safeChildProcess(this.logger, childProcess); + const { terminate$ } = safeChildProcess(logger, childProcess); // this is adding unsubscribe logic to our observer // so that if our observer unsubscribes, we terminate our child-process observer.add(() => { - this.logger.debug(`The browser process observer has unsubscribed. Closing the browser...`); + logger.debug(`The browser process observer has unsubscribed. Closing the browser...`); childProcess.kill(); // ignore async }); @@ -144,7 +144,7 @@ export class HeadlessChromiumDriverFactory { terminate$ .pipe( tap(signal => { - this.logger.debug(`Termination signal received: ${signal}`); + logger.debug(`Termination signal received: ${signal}`); }), ignoreElements() ) @@ -152,33 +152,40 @@ export class HeadlessChromiumDriverFactory { ); // taps the browser log streams and combine them to Kibana logs - this.getBrowserLogger(page).subscribe(); - this.getProcessLogger(browser).subscribe(); + this.getBrowserLogger(page, logger).subscribe(); + this.getProcessLogger(browser, logger).subscribe(); + + // HeadlessChromiumDriver: object to "drive" a browser page + const driver = new HeadlessChromiumDriver(page, { + inspect: this.browserConfig.inspect, + networkPolicy: this.networkPolicy, + }); - const driver$ = Rx.of(new HeadlessChromiumDriver(page, { inspect: this.browserConfig.inspect, networkPolicy: this.networkPolicy })); // prettier-ignore + // Rx.Observable: stream to interrupt page capture const exit$ = this.getPageExit(browser, page); - observer.next({ driver$, exit$ }); + observer.next({ driver, exit$ }); // unsubscribe logic makes a best-effort attempt to delete the user data directory used by chromium observer.add(() => { - this.logger.debug(`deleting chromium user data directory at [${userDataDir}]`); + const userDataDir = this.userDataDir; + logger.debug(`deleting chromium user data directory at [${userDataDir}]`); // the unsubscribe function isn't `async` so we're going to make our best effort at // deleting the userDataDir and if it fails log an error. del(userDataDir).catch(error => { - this.logger.error(`error deleting user data directory at [${userDataDir}]: [${error}]`); + logger.error(`error deleting user data directory at [${userDataDir}]: [${error}]`); }); }); }); } - getBrowserLogger(page: Page): Rx.Observable { + getBrowserLogger(page: Page, logger: Logger): Rx.Observable { const consoleMessages$ = Rx.fromEvent(page, 'console').pipe( map(line => { if (line.type() === 'error') { - this.logger.error(line.text(), ['headless-browser-console']); + logger.error(line.text(), ['headless-browser-console']); } else { - this.logger.debug(line.text(), [`headless-browser-console:${line.type()}`]); + logger.debug(line.text(), [`headless-browser-console:${line.type()}`]); } }) ); @@ -187,7 +194,7 @@ export class HeadlessChromiumDriverFactory { map(req => { const failure = req.failure && req.failure(); if (failure) { - this.logger.warning( + logger.warning( `Request to [${req.url()}] failed! [${failure.errorText}]. This error will be ignored.` ); } @@ -197,7 +204,7 @@ export class HeadlessChromiumDriverFactory { return Rx.merge(consoleMessages$, pageRequestFailed$); } - getProcessLogger(browser: Browser) { + getProcessLogger(browser: Browser, logger: Logger): Rx.Observable { const childProcess = browser.process(); // NOTE: The browser driver can not observe stdout and stderr of the child process // Puppeteer doesn't give a handle to the original ChildProcess object @@ -206,7 +213,7 @@ export class HeadlessChromiumDriverFactory { // just log closing of the process const processClose$ = Rx.fromEvent(childProcess, 'close').pipe( tap(() => { - this.logger.debug('child process closed', ['headless-browser-process']); + logger.debug('child process closed', ['headless-browser-process']); }) ); diff --git a/x-pack/legacy/plugins/reporting/server/browsers/index.ts b/x-pack/legacy/plugins/reporting/server/browsers/index.ts index a5ecc405bf9c5..402fabea56c84 100644 --- a/x-pack/legacy/plugins/reporting/server/browsers/index.ts +++ b/x-pack/legacy/plugins/reporting/server/browsers/index.ts @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + import * as chromiumDefinition from './chromium'; export { ensureAllBrowsersDownloaded } from './download'; diff --git a/x-pack/legacy/plugins/reporting/server/lib/check_license.js b/x-pack/legacy/plugins/reporting/server/lib/check_license.ts similarity index 69% rename from x-pack/legacy/plugins/reporting/server/lib/check_license.js rename to x-pack/legacy/plugins/reporting/server/lib/check_license.ts index 5a784f9913352..02e1196f1d00d 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/check_license.js +++ b/x-pack/legacy/plugins/reporting/server/lib/check_license.ts @@ -4,19 +4,31 @@ * you may not use this file except in compliance with the Elastic License. */ +import { XPackInfo } from '../../../xpack_main/server/lib/xpack_info'; +import { XPackInfoLicense } from '../../../xpack_main/server/lib/xpack_info_license'; +import { ExportTypesRegistry, ExportTypeDefinition } from '../../types'; + +interface LicenseCheckResult { + showLinks: boolean; + enableLinks: boolean; + message?: string; +} + const messages = { getUnavailable: () => { return 'You cannot use Reporting because license information is not available at this time.'; }, - getExpired: license => { + getExpired: (license: XPackInfoLicense) => { return `You cannot use Reporting because your ${license.getType()} license has expired.`; }, }; -const makeManagementFeature = exportTypes => { +const makeManagementFeature = ( + exportTypes: Array> +) => { return { id: 'management', - checkLicense: license => { + checkLicense: (license: XPackInfoLicense | null) => { if (!license) { return { showLinks: true, @@ -46,10 +58,12 @@ const makeManagementFeature = exportTypes => { }; }; -const makeExportTypeFeature = exportType => { +const makeExportTypeFeature = ( + exportType: ExportTypeDefinition +) => { return { id: exportType.id, - checkLicense: license => { + checkLicense: (license: XPackInfoLicense | null) => { if (!license) { return { showLinks: true, @@ -84,13 +98,9 @@ const makeExportTypeFeature = exportType => { }; }; -export function checkLicenseFactory(exportTypesRegistry) { - return function checkLicense(xpackLicenseInfo) { - const license = - xpackLicenseInfo === null || !xpackLicenseInfo.isAvailable() - ? null - : xpackLicenseInfo.license; - +export function checkLicenseFactory(exportTypesRegistry: ExportTypesRegistry) { + return function checkLicense(xpackInfo: XPackInfo) { + const license = xpackInfo === null || !xpackInfo.isAvailable() ? null : xpackInfo.license; const exportTypes = Array.from(exportTypesRegistry.getAll()); const reportingFeatures = [ ...exportTypes.map(makeExportTypeFeature), @@ -100,6 +110,6 @@ export function checkLicenseFactory(exportTypesRegistry) { return reportingFeatures.reduce((result, feature) => { result[feature.id] = feature.checkLicense(license); return result; - }, {}); + }, {} as Record); }; } diff --git a/x-pack/legacy/plugins/reporting/server/lib/esqueue/helpers/create_index.js b/x-pack/legacy/plugins/reporting/server/lib/esqueue/helpers/create_index.js index 9e00f0447e99e..670c2907fb832 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/esqueue/helpers/create_index.js +++ b/x-pack/legacy/plugins/reporting/server/lib/esqueue/helpers/create_index.js @@ -15,7 +15,7 @@ const schema = { properties: { /** * Type of object that is triggering this report. Should be either search, visualization or dashboard. - * Used for phone home stats only. + * Used for job listing and telemetry stats only. */ objectType: { type: 'text', diff --git a/x-pack/legacy/plugins/reporting/server/lib/esqueue/job.js b/x-pack/legacy/plugins/reporting/server/lib/esqueue/job.js index cded6d2ce89a8..a7d8f4df3fd54 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/esqueue/job.js +++ b/x-pack/legacy/plugins/reporting/server/lib/esqueue/job.js @@ -57,7 +57,7 @@ export class Job extends events.EventEmitter { meta: { // We are copying these values out of payload because these fields are indexed and can be aggregated on // for tracking stats, while payload contents are not. - objectType: payload.type, + objectType: payload.objectType, layout: payload.layout ? payload.layout.id : 'none', }, payload: this.payload, diff --git a/x-pack/legacy/plugins/reporting/server/lib/index.ts b/x-pack/legacy/plugins/reporting/server/lib/index.ts index 50d1a276b6b5d..0a2db749cb954 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/index.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/index.ts @@ -5,7 +5,6 @@ */ export { getExportTypesRegistry } from './export_types_registry'; -// @ts-ignore untyped module export { checkLicenseFactory } from './check_license'; export { LevelLogger } from './level_logger'; export { cryptoFactory } from './crypto'; diff --git a/x-pack/legacy/plugins/reporting/server/lib/validate/validate_browser.ts b/x-pack/legacy/plugins/reporting/server/lib/validate/validate_browser.ts index 031709c85284c..89c49123e85bf 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/validate/validate_browser.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/validate/validate_browser.ts @@ -18,14 +18,12 @@ export const validateBrowser = async ( logger: Logger ) => { if (browserFactory.type === BROWSER_TYPE) { - return browserFactory - .test({ viewport: { width: 800, height: 600 } }, logger) - .then((browser: Browser | null) => { - if (browser && browser.close) { - browser.close(); - } else { - throw new Error('Could not close browser client handle!'); - } - }); + return browserFactory.test(logger).then((browser: Browser | null) => { + if (browser && browser.close) { + browser.close(); + } else { + throw new Error('Could not close browser client handle!'); + } + }); } }; diff --git a/x-pack/legacy/plugins/reporting/server/routes/generation.ts b/x-pack/legacy/plugins/reporting/server/routes/generation.ts index 7bed7bc5773e4..73450b7641c8e 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/generation.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/generation.ts @@ -17,7 +17,6 @@ import { import { registerGenerateFromJobParams } from './generate_from_jobparams'; import { registerGenerateCsvFromSavedObject } from './generate_from_savedobject'; import { registerGenerateCsvFromSavedObjectImmediate } from './generate_from_savedobject_immediate'; -import { registerLegacy } from './legacy'; import { createQueueFactory, enqueueJobFactory } from '../lib'; export function registerJobGenerationRoutes( @@ -73,7 +72,6 @@ export function registerJobGenerationRoutes( } registerGenerateFromJobParams(server, handler, handleError); - registerLegacy(server, handler, handleError); // Register beta panel-action download-related API's if (config.get('xpack.reporting.csv.enablePanelActionDownload')) { diff --git a/x-pack/legacy/plugins/reporting/server/routes/legacy.ts b/x-pack/legacy/plugins/reporting/server/routes/legacy.ts deleted file mode 100644 index 011ac4a02bbf9..0000000000000 --- a/x-pack/legacy/plugins/reporting/server/routes/legacy.ts +++ /dev/null @@ -1,73 +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 querystring from 'querystring'; -import { API_BASE_URL } from '../../common/constants'; -import { ServerFacade, RequestFacade, ReportingResponseToolkit } from '../../types'; -import { - getRouteConfigFactoryReportingPre, - GetRouteConfigFactoryFn, -} from './lib/route_config_factories'; -import { HandlerErrorFunction, HandlerFunction } from './types'; - -const getStaticFeatureConfig = (getRouteConfig: GetRouteConfigFactoryFn, featureId: string) => - getRouteConfig(() => featureId); - -const BASE_GENERATE = `${API_BASE_URL}/generate`; - -export function registerLegacy( - server: ServerFacade, - handler: HandlerFunction, - handleError: HandlerErrorFunction -) { - const getRouteConfig = getRouteConfigFactoryReportingPre(server); - - function createLegacyPdfRoute({ path, objectType }: { path: string; objectType: string }) { - const exportTypeId = 'printablePdf'; - server.route({ - path, - method: 'POST', - options: getStaticFeatureConfig(getRouteConfig, exportTypeId), - handler: async (request: RequestFacade, h: ReportingResponseToolkit) => { - const message = `The following URL is deprecated and will stop working in the next major version: ${request.url.path}`; - server.log(['warning', 'reporting', 'deprecation'], message); - - try { - const savedObjectId = request.params.savedId; - const queryString = querystring.stringify(request.query); - - return await handler( - exportTypeId, - { - objectType, - savedObjectId, - queryString, - }, - request, - h - ); - } catch (err) { - throw handleError(exportTypeId, err); - } - }, - }); - } - - createLegacyPdfRoute({ - path: `${BASE_GENERATE}/visualization/{savedId}`, - objectType: 'visualization', - }); - - createLegacyPdfRoute({ - path: `${BASE_GENERATE}/search/{savedId}`, - objectType: 'search', - }); - - createLegacyPdfRoute({ - path: `${BASE_GENERATE}/dashboard/{savedId}`, - objectType: 'dashboard', - }); -} diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_matrix.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_matrix.tsx index 92dace65d466c..962487312c83d 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_matrix.tsx +++ b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_matrix.tsx @@ -23,7 +23,7 @@ import { import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react'; import React, { Component, Fragment } from 'react'; import { Space } from '../../../../../../../../../spaces/common/model/space'; -import { SpaceAvatar } from '../../../../../../../../../spaces/public/components'; +import { SpaceAvatar } from '../../../../../../../../../spaces/public/space_avatar'; import { Feature } from '../../../../../../../../../../../plugins/features/public'; import { FeaturesPrivileges, Role } from '../../../../../../../../common/model'; import { CalculatedPrivilege } from '../../../../../../../lib/kibana_privilege_calculator'; diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx index e54b5ff9c45da..65a3df9fb47a1 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx +++ b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx @@ -14,7 +14,7 @@ import { import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react'; import _ from 'lodash'; import React, { Component } from 'react'; -import { getSpaceColor } from '../../../../../../../../../spaces/public/lib/space_attributes'; +import { getSpaceColor } from '../../../../../../../../../spaces/public/space_avatar'; import { Space } from '../../../../../../../../../spaces/common/model/space'; import { FeaturesPrivileges, diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/space_selector.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/space_selector.tsx index e6e206e5fc7f4..0eb9cf0b0ee9d 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/space_selector.tsx +++ b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/space_selector.tsx @@ -8,7 +8,7 @@ import { EuiComboBox, EuiComboBoxOptionProps, EuiHealth, EuiHighlight } from '@e import { InjectedIntl } from '@kbn/i18n/react'; import React, { Component } from 'react'; import { Space } from '../../../../../../../../../spaces/common/model/space'; -import { getSpaceColor } from '../../../../../../../../../spaces/public/lib/space_attributes'; +import { getSpaceColor } from '../../../../../../../../../spaces/public/space_avatar'; const spaceToOption = (space?: Space, currentSelection?: 'global' | 'spaces') => { if (!space) { diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/spaces_popover_list/spaces_popover_list.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/spaces_popover_list/spaces_popover_list.tsx index 1ab2a27220eee..a99e389044eaa 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/spaces_popover_list/spaces_popover_list.tsx +++ b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/spaces_popover_list/spaces_popover_list.tsx @@ -14,9 +14,9 @@ import { } from '@elastic/eui'; import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react'; import React, { Component } from 'react'; -import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../../../../../../spaces/common/constants'; -import { Space } from '../../../../../../../spaces/common/model/space'; -import { SpaceAvatar } from '../../../../../../../spaces/public/components'; +import { SpaceAvatar } from '../../../../../../../spaces/public/space_avatar'; +import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../../../../../../../../plugins/spaces/common/constants'; +import { Space } from '../../../../../../../../../plugins/spaces/common/model/space'; interface Props { spaces: Space[]; diff --git a/x-pack/legacy/plugins/spaces/index.ts b/x-pack/legacy/plugins/spaces/index.ts index 0083847cfb441..934b44b4accaf 100644 --- a/x-pack/legacy/plugins/spaces/index.ts +++ b/x-pack/legacy/plugins/spaces/index.ts @@ -49,12 +49,12 @@ export const spaces = (kibana: Record) => uiExports: { styleSheetPaths: resolve(__dirname, 'public/index.scss'), - managementSections: ['plugins/spaces/views/management'], + managementSections: [], apps: [ { id: 'space_selector', title: 'Spaces', - main: 'plugins/spaces/views/space_selector', + main: 'plugins/spaces/space_selector', url: 'space_selector', hidden: true, }, diff --git a/x-pack/legacy/plugins/spaces/public/advanced_settings/advanced_settings_service.test.tsx b/x-pack/legacy/plugins/spaces/public/advanced_settings/advanced_settings_service.test.tsx new file mode 100644 index 0000000000000..aa3c6acf26236 --- /dev/null +++ b/x-pack/legacy/plugins/spaces/public/advanced_settings/advanced_settings_service.test.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AdvancedSettingsService } from './advanced_settings_service'; +jest.mock('ui/management', () => { + return { + PAGE_TITLE_COMPONENT: 'page_title_component', + PAGE_SUBTITLE_COMPONENT: 'page_subtitle_component', + }; +}); + +describe('Advanced Settings Service', () => { + describe('#setup', () => { + it('registers space-aware components to augment the advanced settings screen', () => { + const deps = { + getActiveSpace: jest.fn().mockResolvedValue({ id: 'foo', name: 'foo-space' }), + registerSettingsComponent: jest.fn(), + }; + + const advancedSettingsService = new AdvancedSettingsService(); + advancedSettingsService.setup(deps); + + expect(deps.registerSettingsComponent).toHaveBeenCalledTimes(2); + expect(deps.registerSettingsComponent).toHaveBeenCalledWith( + 'page_title_component', + expect.any(Function), + true + ); + + expect(deps.registerSettingsComponent).toHaveBeenCalledWith( + 'page_subtitle_component', + expect.any(Function), + true + ); + }); + }); +}); diff --git a/x-pack/legacy/plugins/spaces/public/advanced_settings/advanced_settings_service.tsx b/x-pack/legacy/plugins/spaces/public/advanced_settings/advanced_settings_service.tsx new file mode 100644 index 0000000000000..9c6c2fcc2cdda --- /dev/null +++ b/x-pack/legacy/plugins/spaces/public/advanced_settings/advanced_settings_service.tsx @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { PAGE_TITLE_COMPONENT, PAGE_SUBTITLE_COMPONENT } from 'ui/management'; +import { Space } from '../../common/model/space'; +import { AdvancedSettingsTitle, AdvancedSettingsSubtitle } from './components'; + +interface SetupDeps { + getActiveSpace: () => Promise; + registerSettingsComponent: ( + id: string, + component: string | React.FC, + allowOverride: boolean + ) => void; +} + +export class AdvancedSettingsService { + public setup({ getActiveSpace, registerSettingsComponent }: SetupDeps) { + const PageTitle = () => ; + const SubTitle = () => ; + + registerSettingsComponent(PAGE_TITLE_COMPONENT, PageTitle, true); + registerSettingsComponent(PAGE_SUBTITLE_COMPONENT, SubTitle, true); + } +} diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx b/x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx rename to x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx b/x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx similarity index 95% rename from x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx rename to x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx index 433f8a8ccf0a2..e35d67c7214cf 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx +++ b/x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx @@ -7,7 +7,7 @@ import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { Fragment, useState, useEffect } from 'react'; -import { Space } from '../../../../../common/model/space'; +import { Space } from '../../../../common/model/space'; interface Props { getActiveSpace: () => Promise; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/index.ts b/x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_subtitle/index.ts rename to x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/advanced_settings_title.test.tsx b/x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.test.tsx similarity index 94% rename from x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/advanced_settings_title.test.tsx rename to x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.test.tsx index bf792ca2cdacf..b772ff433abec 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/advanced_settings_title.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.test.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; import { AdvancedSettingsTitle } from './advanced_settings_title'; -import { SpaceAvatar } from '../../../../components'; +import { SpaceAvatar } from '../../../space_avatar'; import { act } from '@testing-library/react'; describe('AdvancedSettingsTitle', () => { diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/advanced_settings_title.tsx b/x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.tsx similarity index 90% rename from x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/advanced_settings_title.tsx rename to x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.tsx index af6fa42cce07b..b74524db81d81 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/advanced_settings_title.tsx +++ b/x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.tsx @@ -7,8 +7,8 @@ import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { useState, useEffect } from 'react'; -import { Space } from '../../../../../common/model/space'; -import { SpaceAvatar } from '../../../../components'; +import { Space } from '../../../../../../../plugins/spaces/common/model/space'; +import { SpaceAvatar } from '../../../space_avatar'; interface Props { getActiveSpace: () => Promise; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/index.ts b/x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_title/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/components/advanced_settings_title/index.ts rename to x-pack/legacy/plugins/spaces/public/advanced_settings/components/advanced_settings_title/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/lib/index.ts b/x-pack/legacy/plugins/spaces/public/advanced_settings/components/index.ts similarity index 63% rename from x-pack/legacy/plugins/spaces/public/lib/index.ts rename to x-pack/legacy/plugins/spaces/public/advanced_settings/components/index.ts index 56ac7b8ff37f4..6678be7fa34e4 100644 --- a/x-pack/legacy/plugins/spaces/public/lib/index.ts +++ b/x-pack/legacy/plugins/spaces/public/advanced_settings/components/index.ts @@ -4,5 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export { SpacesManager } from './spaces_manager'; -export { getSpaceInitials, getSpaceColor, getSpaceImageUrl } from './space_attributes'; +export { AdvancedSettingsSubtitle } from './advanced_settings_subtitle'; +export { AdvancedSettingsTitle } from './advanced_settings_title'; diff --git a/x-pack/legacy/plugins/uptime/public/lib/adapters/telemetry/index.ts b/x-pack/legacy/plugins/spaces/public/advanced_settings/index.ts similarity index 65% rename from x-pack/legacy/plugins/uptime/public/lib/adapters/telemetry/index.ts rename to x-pack/legacy/plugins/spaces/public/advanced_settings/index.ts index 08d8d9a5d4069..546831a84fa82 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/adapters/telemetry/index.ts +++ b/x-pack/legacy/plugins/spaces/public/advanced_settings/index.ts @@ -4,5 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { getTelemetryMonitorPageLogger } from './log_monitor'; -export { getTelemetryOverviewPageLogger } from './log_overview'; +export { AdvancedSettingsService } from './advanced_settings_service'; diff --git a/x-pack/legacy/plugins/spaces/public/lib/constants.ts b/x-pack/legacy/plugins/spaces/public/constants.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/lib/constants.ts rename to x-pack/legacy/plugins/spaces/public/constants.ts diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/_index.scss b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/_index.scss new file mode 100644 index 0000000000000..30ac0c9fe9b27 --- /dev/null +++ b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/_index.scss @@ -0,0 +1 @@ +@import './components/index'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/_index.scss b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/_copy_to_space.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/_index.scss rename to x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/_copy_to_space.scss diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/_index.scss b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/_index.scss new file mode 100644 index 0000000000000..92b19a8c8c6a3 --- /dev/null +++ b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/_index.scss @@ -0,0 +1 @@ +@import './copy_to_space'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_status_indicator.tsx b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_indicator.tsx similarity index 96% rename from x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_status_indicator.tsx rename to x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_indicator.tsx index f9da25409d60e..ff9035ff02be5 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_status_indicator.tsx +++ b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_indicator.tsx @@ -7,10 +7,7 @@ import React from 'react'; import { EuiLoadingSpinner, EuiText, EuiIconTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { - SummarizedCopyToSpaceResult, - SummarizedSavedObjectResult, -} from '../../../../lib/copy_saved_objects_to_space'; +import { SummarizedCopyToSpaceResult, SummarizedSavedObjectResult } from '..'; interface Props { summarizedCopyResult: SummarizedCopyToSpaceResult; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_status_summary_indicator.tsx b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_summary_indicator.tsx similarity index 94% rename from x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_status_summary_indicator.tsx rename to x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_summary_indicator.tsx index 0ad5f72ba3e45..9d73c216c73ce 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_status_summary_indicator.tsx +++ b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_summary_indicator.tsx @@ -7,8 +7,8 @@ import React from 'react'; import { EuiLoadingSpinner, EuiIconTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { Space } from '../../../../../common/model/space'; -import { SummarizedCopyToSpaceResult } from '../../../../lib/copy_saved_objects_to_space'; +import { Space } from '../../../common/model/space'; +import { SummarizedCopyToSpaceResult } from '..'; interface Props { space: Space; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_flyout.test.tsx b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx similarity index 97% rename from x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_flyout.test.tsx rename to x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx index b3fd345b1d2b4..28011911e212e 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_flyout.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx @@ -6,20 +6,20 @@ import React from 'react'; import Boom from 'boom'; import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; -import { mockManagementPlugin } from '../../../../../../../../../src/legacy/core_plugins/management/public/np_ready/mocks'; +import { mockManagementPlugin } from '../../../../../../../src/legacy/core_plugins/management/public/np_ready/mocks'; import { CopySavedObjectsToSpaceFlyout } from './copy_to_space_flyout'; import { CopyToSpaceForm } from './copy_to_space_form'; import { EuiLoadingSpinner, EuiEmptyPrompt } from '@elastic/eui'; -import { Space } from '../../../../../common/model/space'; +import { Space } from '../../../common/model/space'; import { findTestSubject } from 'test_utils/find_test_subject'; import { SelectableSpacesControl } from './selectable_spaces_control'; import { act } from '@testing-library/react'; import { ProcessingCopyToSpace } from './processing_copy_to_space'; -import { spacesManagerMock } from '../../../../lib/mocks'; -import { SpacesManager } from '../../../../lib'; +import { spacesManagerMock } from '../../spaces_manager/mocks'; +import { SpacesManager } from '../../spaces_manager'; import { ToastNotifications } from 'ui/notify/toasts/toast_notifications'; -jest.mock('../../../../../../../../../src/legacy/core_plugins/management/public/legacy', () => ({ +jest.mock('../../../../../../../src/legacy/core_plugins/management/public/legacy', () => ({ setup: mockManagementPlugin.createSetupContract(), start: mockManagementPlugin.createStartContract(), })); @@ -404,6 +404,7 @@ describe('CopyToSpaceFlyout', () => { id: 'my-viz', error: { type: 'missing_references', + blocking: [], references: [{ type: 'index-pattern', id: 'missing-index-pattern' }], }, }, diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_flyout.tsx b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx similarity index 95% rename from x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_flyout.tsx rename to x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx index 5a43e5878ab83..f486f2f24f13d 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_flyout.tsx +++ b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx @@ -22,17 +22,17 @@ import { mapValues } from 'lodash'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { ToastNotifications } from 'ui/notify/toasts/toast_notifications'; -import { SavedObjectsManagementRecord } from '../../../../../../../../../src/legacy/core_plugins/management/public'; +import { SavedObjectsManagementRecord } from '../../../../../../../src/legacy/core_plugins/management/public'; import { ProcessedImportResponse, processImportResponse, -} from '../../../../../../../../../src/legacy/core_plugins/management/public'; -import { Space } from '../../../../../common/model/space'; -import { SpacesManager } from '../../../../lib'; +} from '../../../../../../../src/legacy/core_plugins/management/public'; +import { Space } from '../../../common/model/space'; +import { SpacesManager } from '../../spaces_manager'; import { ProcessingCopyToSpace } from './processing_copy_to_space'; import { CopyToSpaceFlyoutFooter } from './copy_to_space_flyout_footer'; import { CopyToSpaceForm } from './copy_to_space_form'; -import { CopyOptions, ImportRetry } from '../../../../lib/copy_saved_objects_to_space/types'; +import { CopyOptions, ImportRetry } from '../types'; interface Props { onClose: () => void; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_flyout_footer.tsx b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx similarity index 97% rename from x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_flyout_footer.tsx rename to x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx index 5853bebe3c669..56f39ce3ed4fb 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_flyout_footer.tsx +++ b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx @@ -8,8 +8,8 @@ import React, { Fragment } from 'react'; import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiStat, EuiHorizontalRule } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { ProcessedImportResponse } from '../../../../../../../../../src/legacy/core_plugins/management/public'; -import { ImportRetry } from '../../../../lib/copy_saved_objects_to_space/types'; +import { ProcessedImportResponse } from '../../../../../../../src/legacy/core_plugins/management/public'; +import { ImportRetry } from '../types'; interface Props { copyInProgress: boolean; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_form.tsx b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_form.tsx similarity index 94% rename from x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_form.tsx rename to x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_form.tsx index 2a7e17c253f0b..f680793e27fe0 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/copy_to_space_form.tsx +++ b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_form.tsx @@ -14,8 +14,8 @@ import { EuiListGroupItem, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { CopyOptions } from '../../../../lib/copy_saved_objects_to_space/types'; -import { Space } from '../../../../../common/model/space'; +import { CopyOptions } from '../types'; +import { Space } from '../../../common/model/space'; import { SelectableSpacesControl } from './selectable_spaces_control'; interface Props { diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/index.ts b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/index.ts rename to x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/processing_copy_to_space.tsx b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx similarity index 90% rename from x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/processing_copy_to_space.tsx rename to x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx index b04c9598559b3..285abb828a011 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/processing_copy_to_space.tsx +++ b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx @@ -13,12 +13,12 @@ import { EuiHorizontalRule, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SavedObjectsManagementRecord } from '../../../../../../../../../src/legacy/core_plugins/management/public'; -import { ProcessedImportResponse } from '../../../../../../../../../src/legacy/core_plugins/management/public'; -import { summarizeCopyResult } from '../../../../lib/copy_saved_objects_to_space'; -import { Space } from '../../../../../common/model/space'; -import { CopyOptions, ImportRetry } from '../../../../lib/copy_saved_objects_to_space/types'; +import { SavedObjectsManagementRecord } from '../../../../../../../src/legacy/core_plugins/management/public'; +import { ProcessedImportResponse } from '../../../../../../../src/legacy/core_plugins/management/public'; +import { Space } from '../../../common/model/space'; +import { CopyOptions, ImportRetry } from '../types'; import { SpaceResult } from './space_result'; +import { summarizeCopyResult } from '..'; interface Props { savedObject: SavedObjectsManagementRecord; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/selectable_spaces_control.tsx b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/selectable_spaces_control.tsx similarity index 95% rename from x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/selectable_spaces_control.tsx rename to x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/selectable_spaces_control.tsx index 42d5707531380..9cf81b1cc4486 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/selectable_spaces_control.tsx +++ b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/selectable_spaces_control.tsx @@ -6,8 +6,8 @@ import React, { Fragment, useState } from 'react'; import { EuiSelectable, EuiLoadingSpinner } from '@elastic/eui'; -import { SpaceAvatar } from '../../../../components'; -import { Space } from '../../../../../common/model/space'; +import { SpaceAvatar } from '../../space_avatar'; +import { Space } from '../../../common/model/space'; interface Props { spaces: Space[]; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/space_result.tsx b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/space_result.tsx similarity index 86% rename from x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/space_result.tsx rename to x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/space_result.tsx index f71be12276be5..22f0767ba196e 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/space_result.tsx +++ b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/space_result.tsx @@ -6,13 +6,13 @@ import React from 'react'; import { EuiAccordion, EuiFlexGroup, EuiFlexItem, EuiText, EuiSpacer } from '@elastic/eui'; -import { SavedObjectsManagementRecord } from '../../../../../../../../../src/legacy/core_plugins/management/public'; -import { SummarizedCopyToSpaceResult } from '../../../../lib/copy_saved_objects_to_space'; -import { SpaceAvatar } from '../../../../components'; -import { Space } from '../../../../../common/model/space'; +import { SavedObjectsManagementRecord } from '../../../../../../../src/legacy/core_plugins/management/public'; +import { SummarizedCopyToSpaceResult } from '../index'; +import { SpaceAvatar } from '../../space_avatar'; +import { Space } from '../../../common/model/space'; import { CopyStatusSummaryIndicator } from './copy_status_summary_indicator'; import { SpaceCopyResultDetails } from './space_result_details'; -import { ImportRetry } from '../../../../lib/copy_saved_objects_to_space/types'; +import { ImportRetry } from '../types'; interface Props { savedObject: SavedObjectsManagementRecord; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/space_result_details.tsx b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/space_result_details.tsx similarity index 93% rename from x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/space_result_details.tsx rename to x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/space_result_details.tsx index 66ec38331c89a..d3ab406b87c3e 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/copy_saved_objects_to_space/space_result_details.tsx +++ b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/components/space_result_details.tsx @@ -5,13 +5,13 @@ */ import React from 'react'; -import { SummarizedCopyToSpaceResult } from 'plugins/spaces/lib/copy_saved_objects_to_space'; import { EuiText, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SavedObjectsManagementRecord } from '../../../../../../../../../src/legacy/core_plugins/management/public'; -import { Space } from '../../../../../common/model/space'; +import { SummarizedCopyToSpaceResult } from '../index'; +import { SavedObjectsManagementRecord } from '../../../../../../../src/legacy/core_plugins/management/public'; +import { Space } from '../../../common/model/space'; import { CopyStatusIndicator } from './copy_status_indicator'; -import { ImportRetry } from '../../../../lib/copy_saved_objects_to_space/types'; +import { ImportRetry } from '../types'; interface Props { savedObject: SavedObjectsManagementRecord; diff --git a/x-pack/legacy/plugins/spaces/public/lib/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx similarity index 89% rename from x-pack/legacy/plugins/spaces/public/lib/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx rename to x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx index 3b0fffa38e785..c016494a4cdf9 100644 --- a/x-pack/legacy/plugins/spaces/public/lib/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx +++ b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx @@ -9,8 +9,8 @@ import { toastNotifications } from 'ui/notify'; import { SavedObjectsManagementAction, SavedObjectsManagementRecord, -} from '../../../../../../../src/legacy/core_plugins/management/public'; -import { CopySavedObjectsToSpaceFlyout } from '../../views/management/components/copy_saved_objects_to_space'; +} from '../../../../../../src/legacy/core_plugins/management/public'; +import { CopySavedObjectsToSpaceFlyout } from './components'; import { SpacesManager } from '../spaces_manager'; export class CopyToSpaceSavedObjectsManagementAction extends SavedObjectsManagementAction { diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.test.ts b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.test.ts new file mode 100644 index 0000000000000..63a59344dfe5d --- /dev/null +++ b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.test.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ManagementSetup } from 'src/legacy/core_plugins/management/public'; +import { CopyToSpaceSavedObjectsManagementAction } from './copy_saved_objects_to_space_action'; +import { spacesManagerMock } from '../spaces_manager/mocks'; +import { CopySavedObjectsToSpaceService } from '.'; + +describe('CopySavedObjectsToSpaceService', () => { + describe('#setup', () => { + it('registers the CopyToSpaceSavedObjectsManagementAction', () => { + const deps = { + spacesManager: spacesManagerMock.create(), + // we don't have a proper NP mock for this yet + managementSetup: ({ + savedObjects: { + registry: { + has: jest.fn().mockReturnValue(false), + register: jest.fn(), + }, + }, + } as unknown) as ManagementSetup, + }; + + const service = new CopySavedObjectsToSpaceService(); + service.setup(deps); + + expect(deps.managementSetup.savedObjects.registry.register).toHaveBeenCalledTimes(1); + expect(deps.managementSetup.savedObjects.registry.register).toHaveBeenCalledWith( + expect.any(CopyToSpaceSavedObjectsManagementAction) + ); + }); + }); +}); diff --git a/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.ts b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.ts new file mode 100644 index 0000000000000..37354f985a2fc --- /dev/null +++ b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ManagementSetup } from 'src/legacy/core_plugins/management/public'; +import { CopyToSpaceSavedObjectsManagementAction } from './copy_saved_objects_to_space_action'; +import { SpacesManager } from '../spaces_manager'; + +interface SetupDeps { + spacesManager: SpacesManager; + managementSetup: ManagementSetup; +} + +export class CopySavedObjectsToSpaceService { + public setup({ spacesManager, managementSetup }: SetupDeps) { + const action = new CopyToSpaceSavedObjectsManagementAction(spacesManager); + managementSetup.savedObjects.registry.register(action); + } +} diff --git a/x-pack/legacy/plugins/spaces/public/lib/copy_saved_objects_to_space/index.ts b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/index.ts similarity index 74% rename from x-pack/legacy/plugins/spaces/public/lib/copy_saved_objects_to_space/index.ts rename to x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/index.ts index be23d90cc242a..06969a52a3d8d 100644 --- a/x-pack/legacy/plugins/spaces/public/lib/copy_saved_objects_to_space/index.ts +++ b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/index.ts @@ -5,4 +5,4 @@ */ export * from './summarize_copy_result'; -export { CopyToSpaceSavedObjectsManagementAction } from './copy_saved_objects_to_space_action'; +export { CopySavedObjectsToSpaceService } from './copy_saved_objects_to_space_service'; diff --git a/x-pack/legacy/plugins/spaces/public/lib/copy_saved_objects_to_space/summarize_copy_result.test.ts b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.test.ts similarity index 98% rename from x-pack/legacy/plugins/spaces/public/lib/copy_saved_objects_to_space/summarize_copy_result.test.ts rename to x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.test.ts index 0352902072790..0244a35711e6f 100644 --- a/x-pack/legacy/plugins/spaces/public/lib/copy_saved_objects_to_space/summarize_copy_result.test.ts +++ b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.test.ts @@ -5,7 +5,7 @@ */ import { summarizeCopyResult } from './summarize_copy_result'; -import { ProcessedImportResponse } from '../../../../../../../src/legacy/core_plugins/management/public'; +import { ProcessedImportResponse } from '../../../../../../src/legacy/core_plugins/management/public'; const createSavedObjectsManagementRecord = () => ({ type: 'dashboard', diff --git a/x-pack/legacy/plugins/spaces/public/lib/copy_saved_objects_to_space/summarize_copy_result.ts b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.ts similarity index 96% rename from x-pack/legacy/plugins/spaces/public/lib/copy_saved_objects_to_space/summarize_copy_result.ts rename to x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.ts index 8807489157d71..7bc47d35efc6c 100644 --- a/x-pack/legacy/plugins/spaces/public/lib/copy_saved_objects_to_space/summarize_copy_result.ts +++ b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsManagementRecord } from '../../../../../../../src/legacy/core_plugins/management/public'; -import { ProcessedImportResponse } from '../../../../../../../src/legacy/core_plugins/management/public'; +import { SavedObjectsManagementRecord } from '../../../../../../src/legacy/core_plugins/management/public'; +import { ProcessedImportResponse } from '../../../../../../src/legacy/core_plugins/management/public'; export interface SummarizedSavedObjectResult { type: string; diff --git a/x-pack/legacy/plugins/spaces/public/lib/copy_saved_objects_to_space/types.ts b/x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/types.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/lib/copy_saved_objects_to_space/types.ts rename to x-pack/legacy/plugins/spaces/public/copy_saved_objects_to_space/types.ts diff --git a/x-pack/legacy/plugins/spaces/public/create_feature_catalogue_entry.ts b/x-pack/legacy/plugins/spaces/public/create_feature_catalogue_entry.ts index 1f41bb89d7707..464066d2221de 100644 --- a/x-pack/legacy/plugins/spaces/public/create_feature_catalogue_entry.ts +++ b/x-pack/legacy/plugins/spaces/public/create_feature_catalogue_entry.ts @@ -9,7 +9,7 @@ import { FeatureCatalogueEntry, FeatureCatalogueCategory, } from '../../../../../src/plugins/home/public'; -import { getSpacesFeatureDescription } from './lib/constants'; +import { getSpacesFeatureDescription } from './constants'; export const createSpacesFeatureCatalogueEntry = (): FeatureCatalogueEntry => { return { diff --git a/x-pack/legacy/plugins/spaces/public/index.scss b/x-pack/legacy/plugins/spaces/public/index.scss index 7a40872b760cb..26269f1d31aa3 100644 --- a/x-pack/legacy/plugins/spaces/public/index.scss +++ b/x-pack/legacy/plugins/spaces/public/index.scss @@ -10,4 +10,7 @@ // spcChart__legend--small // spcChart__legend-isLoading -@import './views/index'; +@import './management/index'; +@import './nav_control/index'; +@import './space_selector/index'; +@import './copy_saved_objects_to_space/index'; diff --git a/x-pack/legacy/plugins/spaces/public/index.ts b/x-pack/legacy/plugins/spaces/public/index.ts index 9233aae9fb12f..53cb906a619d3 100644 --- a/x-pack/legacy/plugins/spaces/public/index.ts +++ b/x-pack/legacy/plugins/spaces/public/index.ts @@ -5,6 +5,8 @@ */ import { SpacesPlugin } from './plugin'; +export { SpaceAvatar } from './space_avatar'; + export const plugin = () => { return new SpacesPlugin(); }; diff --git a/x-pack/legacy/plugins/spaces/public/legacy.ts b/x-pack/legacy/plugins/spaces/public/legacy.ts index 99419206093e9..1dffbd2661714 100644 --- a/x-pack/legacy/plugins/spaces/public/legacy.ts +++ b/x-pack/legacy/plugins/spaces/public/legacy.ts @@ -4,15 +4,26 @@ * you may not use this file except in compliance with the Elastic License. */ +import { registerSettingsComponent } from 'ui/management'; import { npSetup, npStart } from 'ui/new_platform'; +import { setup as managementSetup } from '../../../../../src/legacy/core_plugins/management/public/legacy'; import { plugin } from '.'; -import { SpacesPlugin, PluginsSetup } from './plugin'; +import { SpacesPlugin, PluginsSetup, PluginsStart } from './plugin'; +import './management/legacy_page_routes'; const spacesPlugin: SpacesPlugin = plugin(); -const plugins: PluginsSetup = { +const pluginsSetup: PluginsSetup = { home: npSetup.plugins.home, + management: managementSetup, + __managementLegacyCompat: { + registerSettingsComponent, + }, }; -export const setup = spacesPlugin.setup(npSetup.core, plugins); -export const start = spacesPlugin.start(npStart.core); +const pluginsStart: PluginsStart = { + management: npStart.plugins.management, +}; + +export const setup = spacesPlugin.setup(npSetup.core, pluginsSetup); +export const start = spacesPlugin.start(npStart.core, pluginsStart); diff --git a/x-pack/legacy/plugins/spaces/public/management/_index.scss b/x-pack/legacy/plugins/spaces/public/management/_index.scss new file mode 100644 index 0000000000000..72deb1f1cde8d --- /dev/null +++ b/x-pack/legacy/plugins/spaces/public/management/_index.scss @@ -0,0 +1,4 @@ +@import './components/confirm_delete_modal/confirm_delete_modal'; +@import './edit_space/enabled_features/index'; +@import './edit_space/section_panel/section_panel'; + diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/__snapshots__/confirm_delete_modal.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/__snapshots__/confirm_delete_modal.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/components/__snapshots__/confirm_delete_modal.test.tsx.snap rename to x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/__snapshots__/confirm_delete_modal.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/_confirm_delete_modal.scss b/x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/_confirm_delete_modal.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/components/_confirm_delete_modal.scss rename to x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/_confirm_delete_modal.scss diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/confirm_delete_modal.test.tsx b/x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.test.tsx similarity index 94% rename from x-pack/legacy/plugins/spaces/public/views/management/components/confirm_delete_modal.test.tsx rename to x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.test.tsx index f0ab2c99ac2e2..331435b54edb7 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/confirm_delete_modal.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.test.tsx @@ -7,8 +7,8 @@ import React from 'react'; import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; import { ConfirmDeleteModal } from './confirm_delete_modal'; -import { spacesManagerMock } from '../../../lib/mocks'; -import { SpacesManager } from '../../../lib'; +import { spacesManagerMock } from '../../../spaces_manager/mocks'; +import { SpacesManager } from '../../../spaces_manager'; describe('ConfirmDeleteModal', () => { it('renders as expected', () => { diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/confirm_delete_modal.tsx b/x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx similarity index 98% rename from x-pack/legacy/plugins/spaces/public/views/management/components/confirm_delete_modal.tsx rename to x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx index 0c76cb4a828fe..6eed58a784212 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/confirm_delete_modal.tsx +++ b/x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx @@ -25,8 +25,8 @@ import { } from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React, { ChangeEvent, Component } from 'react'; -import { Space } from '../../../../common/model/space'; -import { SpacesManager } from '../../../lib'; +import { SpacesManager } from '../../../spaces_manager'; +import { Space } from '../../../../../../plugins/spaces/common/model/space'; interface Props { space: Space; diff --git a/x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/index.ts b/x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/index.ts new file mode 100644 index 0000000000000..651455d00e9f2 --- /dev/null +++ b/x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { ConfirmDeleteModal } from './confirm_delete_modal'; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/index.ts b/x-pack/legacy/plugins/spaces/public/management/components/index.ts similarity index 85% rename from x-pack/legacy/plugins/spaces/public/views/management/components/index.ts rename to x-pack/legacy/plugins/spaces/public/management/components/index.ts index 91f4964e1da06..7f9f80f470d12 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/index.ts +++ b/x-pack/legacy/plugins/spaces/public/management/components/index.ts @@ -6,3 +6,4 @@ export { ConfirmDeleteModal } from './confirm_delete_modal'; export { UnauthorizedPrompt } from './unauthorized_prompt'; +export { SecureSpaceMessage } from './secure_space_message'; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/secure_space_message/__snapshots__/secure_space_message.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/__snapshots__/secure_space_message.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/components/secure_space_message/__snapshots__/secure_space_message.test.tsx.snap rename to x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/__snapshots__/secure_space_message.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/secure_space_message/index.ts b/x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/components/secure_space_message/index.ts rename to x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/secure_space_message/secure_space_message.test.tsx b/x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/secure_space_message.test.tsx similarity index 93% rename from x-pack/legacy/plugins/spaces/public/views/management/components/secure_space_message/secure_space_message.test.tsx rename to x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/secure_space_message.test.tsx index 1cc6f6c1f72be..b43010fe5f326 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/secure_space_message/secure_space_message.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/secure_space_message.test.tsx @@ -8,7 +8,7 @@ import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import { SecureSpaceMessage } from './secure_space_message'; let mockShowLinks: boolean = true; -jest.mock('../../../../../../xpack_main/public/services/xpack_info', () => { +jest.mock('../../../../../xpack_main/public/services/xpack_info', () => { return { xpackInfo: { get: jest.fn().mockImplementation((key: string) => { diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/secure_space_message/secure_space_message.tsx b/x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/secure_space_message.tsx similarity index 94% rename from x-pack/legacy/plugins/spaces/public/views/management/components/secure_space_message/secure_space_message.tsx rename to x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/secure_space_message.tsx index 6bbc423968b4b..746b7e2ac4c98 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/components/secure_space_message/secure_space_message.tsx +++ b/x-pack/legacy/plugins/spaces/public/management/components/secure_space_message/secure_space_message.tsx @@ -9,7 +9,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import React, { Fragment } from 'react'; // @ts-ignore -import { xpackInfo } from '../../../../../../xpack_main/public/services/xpack_info'; +import { xpackInfo } from '../../../../../xpack_main/public/services/xpack_info'; export const SecureSpaceMessage = ({}) => { const showSecurityLinks = xpackInfo.get('features.security.showLinks'); diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/__snapshots__/unauthorized_prompt.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/management/components/unauthorized_prompt/__snapshots__/unauthorized_prompt.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/components/__snapshots__/unauthorized_prompt.test.tsx.snap rename to x-pack/legacy/plugins/spaces/public/management/components/unauthorized_prompt/__snapshots__/unauthorized_prompt.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/management/components/unauthorized_prompt/index.ts b/x-pack/legacy/plugins/spaces/public/management/components/unauthorized_prompt/index.ts new file mode 100644 index 0000000000000..5a8120a77804b --- /dev/null +++ b/x-pack/legacy/plugins/spaces/public/management/components/unauthorized_prompt/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { UnauthorizedPrompt } from './unauthorized_prompt'; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/unauthorized_prompt.test.tsx b/x-pack/legacy/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/components/unauthorized_prompt.test.tsx rename to x-pack/legacy/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/views/management/components/unauthorized_prompt.tsx b/x-pack/legacy/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/components/unauthorized_prompt.tsx rename to x-pack/legacy/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.tsx diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/__snapshots__/delete_spaces_button.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/management/edit_space/__snapshots__/delete_spaces_button.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/__snapshots__/delete_spaces_button.test.tsx.snap rename to x-pack/legacy/plugins/spaces/public/management/edit_space/__snapshots__/delete_spaces_button.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/confirm_alter_active_space_modal/__snapshots__/confirm_alter_active_space_modal.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/__snapshots__/confirm_alter_active_space_modal.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/confirm_alter_active_space_modal/__snapshots__/confirm_alter_active_space_modal.test.tsx.snap rename to x-pack/legacy/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/__snapshots__/confirm_alter_active_space_modal.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.test.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.test.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.tsx diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/confirm_alter_active_space_modal/index.ts b/x-pack/legacy/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/confirm_alter_active_space_modal/index.ts rename to x-pack/legacy/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/__snapshots__/customize_space_avatar.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/__snapshots__/customize_space_avatar.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/__snapshots__/customize_space_avatar.test.tsx.snap rename to x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/__snapshots__/customize_space_avatar.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/__snapshots__/space_identifier.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/__snapshots__/space_identifier.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/__snapshots__/space_identifier.test.tsx.snap rename to x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/__snapshots__/space_identifier.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/customize_space.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx similarity index 97% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/customize_space.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx index 8a7e384d44b35..b0d74afaa90aa 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/customize_space.tsx +++ b/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx @@ -18,9 +18,9 @@ import { } from '@elastic/eui'; import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react'; import React, { ChangeEvent, Component, Fragment } from 'react'; -import { isReservedSpace } from '../../../../../common'; -import { Space } from '../../../../../common/model/space'; -import { SpaceAvatar } from '../../../../components'; +import { isReservedSpace } from '../../../../common'; +import { Space } from '../../../../common/model/space'; +import { SpaceAvatar } from '../../../space_avatar'; import { SpaceValidator, toSpaceIdentifier } from '../../lib'; import { SectionPanel } from '../section_panel'; import { CustomizeSpaceAvatar } from './customize_space_avatar'; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/customize_space_avatar.test.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/customize_space_avatar.test.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/customize_space_avatar.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.tsx similarity index 95% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/customize_space_avatar.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.tsx index 12fa0193b59a4..c3207c82bf95e 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/customize_space_avatar.tsx +++ b/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.tsx @@ -17,12 +17,10 @@ import { isValidHex, } from '@elastic/eui'; import { InjectedIntl, injectI18n } from '@kbn/i18n/react'; - -import { getSpaceColor, getSpaceInitials } from '../../../../lib/space_attributes'; -import { encode, imageTypes } from '../../../../../common/lib/dataurl'; - -import { MAX_SPACE_INITIALS } from '../../../../../common/constants'; -import { Space } from '../../../../../common/model/space'; +import { imageTypes, encode } from '../../../../common/lib/dataurl'; +import { getSpaceColor, getSpaceInitials } from '../../../space_avatar'; +import { Space } from '../../../../../../../plugins/spaces/common/model/space'; +import { MAX_SPACE_INITIALS } from '../../../../../../../plugins/spaces/common'; interface Props { space: Partial; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/index.ts b/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/index.ts rename to x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/space_identifier.test.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/space_identifier.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/space_identifier.test.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/space_identifier.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/space_identifier.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/space_identifier.tsx similarity index 98% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/space_identifier.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/space_identifier.tsx index a717570b19c5d..1d6664273d21e 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/customize_space/space_identifier.tsx +++ b/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/space_identifier.tsx @@ -7,7 +7,7 @@ import { EuiFieldText, EuiFormRow, EuiLink } from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React, { ChangeEvent, Component, Fragment } from 'react'; -import { Space } from '../../../../../common/model/space'; +import { Space } from '../../../../common/model/space'; import { SpaceValidator, toSpaceIdentifier } from '../../lib'; interface Props { diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/delete_spaces_button.test.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/delete_spaces_button.test.tsx similarity index 88% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/delete_spaces_button.test.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/delete_spaces_button.test.tsx index e7c7dfc5eb1b0..364145d6495b8 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/delete_spaces_button.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/management/edit_space/delete_spaces_button.test.tsx @@ -7,8 +7,8 @@ import React from 'react'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import { DeleteSpacesButton } from './delete_spaces_button'; -import { spacesManagerMock } from '../../../lib/mocks'; -import { SpacesManager } from '../../../lib'; +import { spacesManagerMock } from '../../spaces_manager/mocks'; +import { SpacesManager } from '../../spaces_manager'; const space = { id: 'my-space', diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/delete_spaces_button.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx similarity index 96% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/delete_spaces_button.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx index 216dd7c41f124..56a858eb4ccf6 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/delete_spaces_button.tsx +++ b/x-pack/legacy/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx @@ -7,10 +7,9 @@ import { EuiButton, EuiButtonIcon, EuiButtonIconProps } from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React, { Component, Fragment } from 'react'; -// @ts-ignore import { toastNotifications } from 'ui/notify'; -import { Space } from '../../../../common/model/space'; -import { SpacesManager } from '../../../lib/spaces_manager'; +import { Space } from '../../../common/model/space'; +import { SpacesManager } from '../../spaces_manager'; import { ConfirmDeleteModal } from '../components/confirm_delete_modal'; interface Props { diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap rename to x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/_index.scss b/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/_index.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/_index.scss rename to x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/_index.scss diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/enabled_features.test.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx similarity index 95% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/enabled_features.test.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx index f8bd4b889394a..f770857d9313d 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/enabled_features.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx @@ -7,8 +7,8 @@ import { EuiLink } from '@elastic/eui'; import React from 'react'; import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { Feature } from '../../../../../../../../plugins/features/public'; -import { Space } from '../../../../../common/model/space'; +import { Feature } from '../../../../../../../plugins/features/public'; +import { Space } from '../../../../common/model/space'; import { SectionPanel } from '../section_panel'; import { EnabledFeatures } from './enabled_features'; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/enabled_features.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx similarity index 97% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/enabled_features.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx index 628be759b7c5c..70312296f757b 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/enabled_features.tsx +++ b/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx @@ -8,8 +8,8 @@ import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer, EuiText, EuiTitle } from import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react'; import React, { Component, Fragment, ReactNode } from 'react'; import { Capabilities } from 'src/core/public'; -import { Feature } from '../../../../../../../../plugins/features/public'; -import { Space } from '../../../../../common/model/space'; +import { Feature } from '../../../../../../../plugins/features/public'; +import { Space } from '../../../../common/model/space'; import { getEnabledFeatures } from '../../lib/feature_utils'; import { SectionPanel } from '../section_panel'; import { FeatureTable } from './feature_table'; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/feature_table.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/feature_table.tsx similarity index 92% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/feature_table.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/feature_table.tsx index b3654b4d35bd3..2866d0bfa8cf3 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/feature_table.tsx +++ b/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/feature_table.tsx @@ -3,13 +3,13 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -// @ts-ignore -import { EuiCheckbox, EuiIcon, EuiInMemoryTable, EuiSwitch, EuiText, IconType } from '@elastic/eui'; + +import { EuiIcon, EuiInMemoryTable, EuiSwitch, EuiText, IconType } from '@elastic/eui'; import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react'; import _ from 'lodash'; import React, { ChangeEvent, Component } from 'react'; -import { Feature } from '../../../../../../../../plugins/features/public'; -import { Space } from '../../../../../common/model/space'; +import { Feature } from '../../../../../../../plugins/features/public'; +import { Space } from '../../../../common/model/space'; import { ToggleAllFeatures } from './toggle_all_features'; interface Props { diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/index.ts b/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/index.ts rename to x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/toggle_all_features.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/toggle_all_features.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/enabled_features/toggle_all_features.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/enabled_features/toggle_all_features.tsx diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/index.ts b/x-pack/legacy/plugins/spaces/public/management/edit_space/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/index.ts rename to x-pack/legacy/plugins/spaces/public/management/edit_space/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.test.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx similarity index 95% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.test.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx index c69a885ae0587..d24e932bce112 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx @@ -6,7 +6,7 @@ jest.mock('ui/kfetch', () => ({ kfetch: () => Promise.resolve([{ id: 'feature-1', name: 'feature 1', icon: 'spacesApp' }]), })); -import '../../../__mocks__/xpack_info'; +import '../../__mocks__/xpack_info'; import { EuiButton, EuiLink, EuiSwitch } from '@elastic/eui'; import { ReactWrapper } from 'enzyme'; import React from 'react'; @@ -14,8 +14,8 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { ConfirmAlterActiveSpaceModal } from './confirm_alter_active_space_modal'; import { ManageSpacePage } from './manage_space_page'; import { SectionPanel } from './section_panel'; -import { spacesManagerMock } from '../../../lib/mocks'; -import { SpacesManager } from '../../../lib'; +import { spacesManagerMock } from '../../spaces_manager/mocks'; +import { SpacesManager } from '../../spaces_manager'; const space = { id: 'my-space', @@ -65,21 +65,28 @@ describe('ManageSpacePage', () => { }); it('allows a space to be updated', async () => { - const spacesManager = spacesManagerMock.create(); - spacesManager.getSpace = jest.fn().mockResolvedValue({ + const spaceToUpdate = { id: 'existing-space', name: 'Existing Space', description: 'hey an existing space', color: '#aabbcc', initials: 'AB', disabledFeatures: [], + }; + + const spacesManager = spacesManagerMock.create(); + spacesManager.getSpace = jest.fn().mockResolvedValue({ + ...spaceToUpdate, }); spacesManager.getActiveSpace = jest.fn().mockResolvedValue(space); + const onLoadSpace = jest.fn(); + const wrapper = mountWithIntl( { await waitForDataLoad(wrapper); expect(spacesManager.getSpace).toHaveBeenCalledWith('existing-space'); + expect(onLoadSpace).toHaveBeenCalledWith({ + ...spaceToUpdate, + }); await Promise.resolve(); diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/manage_space_page.tsx similarity index 93% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/manage_space_page.tsx index a5d60d1a731ba..6bbb32ccd654f 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/manage_space_page.tsx +++ b/x-pack/legacy/plugins/spaces/public/management/edit_space/manage_space_page.tsx @@ -17,17 +17,15 @@ import { import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import _ from 'lodash'; import React, { Component, Fragment } from 'react'; -import { Breadcrumb } from 'ui/chrome'; import { kfetch } from 'ui/kfetch'; import { toastNotifications } from 'ui/notify'; import { Capabilities } from 'src/core/public'; -import { Feature } from '../../../../../../../plugins/features/public'; -import { isReservedSpace } from '../../../../common'; -import { Space } from '../../../../common/model/space'; -import { SpacesManager } from '../../../lib'; -import { SecureSpaceMessage } from '../components/secure_space_message'; -import { UnauthorizedPrompt } from '../components/unauthorized_prompt'; -import { getEditBreadcrumbs, toSpaceIdentifier } from '../lib'; +import { Feature } from '../../../../../../plugins/features/public'; +import { isReservedSpace } from '../../../common'; +import { Space } from '../../../common/model/space'; +import { SpacesManager } from '../../spaces_manager'; +import { SecureSpaceMessage, UnauthorizedPrompt } from '../components'; +import { toSpaceIdentifier } from '../lib'; import { SpaceValidator } from '../lib/validate_space'; import { ConfirmAlterActiveSpaceModal } from './confirm_alter_active_space_modal'; import { CustomizeSpace } from './customize_space'; @@ -39,7 +37,7 @@ interface Props { spacesManager: SpacesManager; spaceId?: string; intl: InjectedIntl; - setBreadcrumbs?: (breadcrumbs: Breadcrumb[]) => void; + onLoadSpace?: (space: Space) => void; capabilities: Capabilities; } @@ -76,7 +74,7 @@ class ManageSpacePageUI extends Component { return; } - const { spaceId, spacesManager, intl, setBreadcrumbs } = this.props; + const { spaceId, spacesManager, intl, onLoadSpace } = this.props; const getFeatures = kfetch({ method: 'get', pathname: '/api/features' }); @@ -84,8 +82,8 @@ class ManageSpacePageUI extends Component { try { const [space, features] = await Promise.all([spacesManager.getSpace(spaceId), getFeatures]); if (space) { - if (setBreadcrumbs) { - setBreadcrumbs(getEditBreadcrumbs(space)); + if (onLoadSpace) { + onLoadSpace(space); } this.setState({ diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/reserved_space_badge.test.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/reserved_space_badge.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/reserved_space_badge.test.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/reserved_space_badge.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/reserved_space_badge.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/reserved_space_badge.tsx similarity index 89% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/reserved_space_badge.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/reserved_space_badge.tsx index e4b2dda3a668b..38bf351902096 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/reserved_space_badge.tsx +++ b/x-pack/legacy/plugins/spaces/public/management/edit_space/reserved_space_badge.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { EuiIcon, EuiToolTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { isReservedSpace } from '../../../../common'; -import { Space } from '../../../../common/model/space'; +import { isReservedSpace } from '../../../common'; +import { Space } from '../../../common/model/space'; interface Props { space?: Space; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/section_panel/__snapshots__/section_panel.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/__snapshots__/section_panel.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/section_panel/__snapshots__/section_panel.test.tsx.snap rename to x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/__snapshots__/section_panel.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/section_panel/_section_panel.scss b/x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/_section_panel.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/section_panel/_section_panel.scss rename to x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/_section_panel.scss diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/section_panel/index.ts b/x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/section_panel/index.ts rename to x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/section_panel/section_panel.test.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/section_panel/section_panel.test.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/views/management/edit_space/section_panel/section_panel.tsx b/x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/edit_space/section_panel/section_panel.tsx rename to x-pack/legacy/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx diff --git a/x-pack/legacy/plugins/spaces/public/management/index.ts b/x-pack/legacy/plugins/spaces/public/management/index.ts new file mode 100644 index 0000000000000..ad3cc6b245619 --- /dev/null +++ b/x-pack/legacy/plugins/spaces/public/management/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { ManagementService } from './management_service'; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/page_routes.tsx b/x-pack/legacy/plugins/spaces/public/management/legacy_page_routes.tsx similarity index 75% rename from x-pack/legacy/plugins/spaces/public/views/management/page_routes.tsx rename to x-pack/legacy/plugins/spaces/public/management/legacy_page_routes.tsx index d8fd0298df2fc..8cf4a129e5b8f 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/page_routes.tsx +++ b/x-pack/legacy/plugins/spaces/public/management/legacy_page_routes.tsx @@ -4,30 +4,59 @@ * you may not use this file except in compliance with the Elastic License. */ // @ts-ignore -import template from 'plugins/spaces/views/management/template.html'; +import template from 'plugins/spaces/management/template.html'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { I18nContext } from 'ui/i18n'; // @ts-ignore import routes from 'ui/routes'; +import { MANAGEMENT_BREADCRUMB } from 'ui/management/breadcrumbs'; import { npStart } from 'ui/new_platform'; import { ManageSpacePage } from './edit_space'; -import { getCreateBreadcrumbs, getEditBreadcrumbs, getListBreadcrumbs } from './lib'; import { SpacesGridPage } from './spaces_grid'; -import { start as spacesNPStart } from '../../legacy'; +import { start as spacesNPStart } from '../legacy'; +import { Space } from '../../common/model/space'; const reactRootNodeId = 'manageSpacesReactRoot'; +function getListBreadcrumbs() { + return [ + MANAGEMENT_BREADCRUMB, + { + text: 'Spaces', + href: '#/management/spaces/list', + }, + ]; +} + +function getCreateBreadcrumbs() { + return [ + ...getListBreadcrumbs(), + { + text: 'Create', + }, + ]; +} + +function getEditBreadcrumbs(space?: Space) { + return [ + ...getListBreadcrumbs(), + { + text: space ? space.name : '...', + }, + ]; +} + routes.when('/management/spaces/list', { template, k7Breadcrumbs: getListBreadcrumbs, requireUICapability: 'management.kibana.spaces', controller($scope: any) { - $scope.$$postDigest(async () => { + $scope.$$postDigest(() => { const domNode = document.getElementById(reactRootNodeId); - const { spacesManager } = await spacesNPStart; + const { spacesManager } = spacesNPStart; render( @@ -54,10 +83,10 @@ routes.when('/management/spaces/create', { k7Breadcrumbs: getCreateBreadcrumbs, requireUICapability: 'management.kibana.spaces', controller($scope: any) { - $scope.$$postDigest(async () => { + $scope.$$postDigest(() => { const domNode = document.getElementById(reactRootNodeId); - const { spacesManager } = await spacesNPStart; + const { spacesManager } = spacesNPStart; render( @@ -100,7 +129,9 @@ routes.when('/management/spaces/edit/:spaceId', { { + npStart.core.chrome.setBreadcrumbs(getEditBreadcrumbs(space)); + }} capabilities={npStart.core.application.capabilities} /> , diff --git a/x-pack/legacy/plugins/spaces/public/views/management/lib/feature_utils.test.ts b/x-pack/legacy/plugins/spaces/public/management/lib/feature_utils.test.ts similarity index 94% rename from x-pack/legacy/plugins/spaces/public/views/management/lib/feature_utils.test.ts rename to x-pack/legacy/plugins/spaces/public/management/lib/feature_utils.test.ts index 8621ec5614368..ce874956d0ef2 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/lib/feature_utils.test.ts +++ b/x-pack/legacy/plugins/spaces/public/management/lib/feature_utils.test.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature } from '../../../../../../../plugins/features/public'; import { getEnabledFeatures } from './feature_utils'; +import { Feature } from '../../../../../../plugins/features/public'; const buildFeatures = () => [ diff --git a/x-pack/legacy/plugins/spaces/public/views/management/lib/feature_utils.ts b/x-pack/legacy/plugins/spaces/public/management/lib/feature_utils.ts similarity index 74% rename from x-pack/legacy/plugins/spaces/public/views/management/lib/feature_utils.ts rename to x-pack/legacy/plugins/spaces/public/management/lib/feature_utils.ts index ef46a53967744..ff1688637ef73 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/lib/feature_utils.ts +++ b/x-pack/legacy/plugins/spaces/public/management/lib/feature_utils.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature } from '../../../../../../../plugins/features/public'; +import { Feature } from '../../../../../../plugins/features/common'; -import { Space } from '../../../../common/model/space'; +import { Space } from '../../../../../../plugins/spaces/common/model/space'; export function getEnabledFeatures(features: Feature[], space: Partial) { return features.filter(feature => !(space.disabledFeatures || []).includes(feature.id)); diff --git a/x-pack/legacy/plugins/spaces/public/views/management/lib/index.ts b/x-pack/legacy/plugins/spaces/public/management/lib/index.ts similarity index 80% rename from x-pack/legacy/plugins/spaces/public/views/management/lib/index.ts rename to x-pack/legacy/plugins/spaces/public/management/lib/index.ts index f65757f5dba26..4a158168febd8 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/lib/index.ts +++ b/x-pack/legacy/plugins/spaces/public/management/lib/index.ts @@ -7,5 +7,3 @@ export { toSpaceIdentifier, isValidSpaceIdentifier } from './space_identifier_utils'; export { SpaceValidator } from './validate_space'; - -export { getCreateBreadcrumbs, getEditBreadcrumbs, getListBreadcrumbs } from './breadcrumbs'; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/lib/space_identifier_utils.test.ts b/x-pack/legacy/plugins/spaces/public/management/lib/space_identifier_utils.test.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/lib/space_identifier_utils.test.ts rename to x-pack/legacy/plugins/spaces/public/management/lib/space_identifier_utils.test.ts diff --git a/x-pack/legacy/plugins/spaces/public/views/management/lib/space_identifier_utils.ts b/x-pack/legacy/plugins/spaces/public/management/lib/space_identifier_utils.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/lib/space_identifier_utils.ts rename to x-pack/legacy/plugins/spaces/public/management/lib/space_identifier_utils.ts diff --git a/x-pack/legacy/plugins/spaces/public/views/management/lib/validate_space.test.ts b/x-pack/legacy/plugins/spaces/public/management/lib/validate_space.test.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/lib/validate_space.test.ts rename to x-pack/legacy/plugins/spaces/public/management/lib/validate_space.test.ts diff --git a/x-pack/legacy/plugins/spaces/public/views/management/lib/validate_space.ts b/x-pack/legacy/plugins/spaces/public/management/lib/validate_space.ts similarity index 96% rename from x-pack/legacy/plugins/spaces/public/views/management/lib/validate_space.ts rename to x-pack/legacy/plugins/spaces/public/management/lib/validate_space.ts index e7b9116131431..43d42dacdd36d 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/lib/validate_space.ts +++ b/x-pack/legacy/plugins/spaces/public/management/lib/validate_space.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { i18n } from '@kbn/i18n'; -import { isReservedSpace } from '../../../../common/is_reserved_space'; -import { Space } from '../../../../common/model/space'; +import { isReservedSpace } from '../../../common/is_reserved_space'; +import { Space } from '../../../common/model/space'; import { isValidSpaceIdentifier } from './space_identifier_utils'; interface SpaceValidatorOptions { diff --git a/x-pack/legacy/plugins/spaces/public/management/management_service.test.ts b/x-pack/legacy/plugins/spaces/public/management/management_service.test.ts new file mode 100644 index 0000000000000..fa8ae64168673 --- /dev/null +++ b/x-pack/legacy/plugins/spaces/public/management/management_service.test.ts @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ManagementService } from '.'; + +describe('ManagementService', () => { + describe('#start', () => { + it('registers the spaces management page under the kibana section', () => { + const mockKibanaSection = { + hasItem: jest.fn().mockReturnValue(false), + register: jest.fn(), + }; + + const managementStart = { + legacy: { + getSection: jest.fn().mockReturnValue(mockKibanaSection), + }, + }; + + const deps = { + managementStart, + }; + + const service = new ManagementService(); + service.start(deps); + + expect(deps.managementStart.legacy.getSection).toHaveBeenCalledTimes(1); + expect(deps.managementStart.legacy.getSection).toHaveBeenCalledWith('kibana'); + + expect(mockKibanaSection.register).toHaveBeenCalledTimes(1); + expect(mockKibanaSection.register).toHaveBeenCalledWith('spaces', { + name: 'spacesManagementLink', + order: 10, + display: 'Spaces', + url: `#/management/spaces/list`, + }); + }); + + it('will not register the spaces management page twice', () => { + const mockKibanaSection = { + hasItem: jest.fn().mockReturnValue(true), + register: jest.fn(), + }; + + const managementStart = { + legacy: { + getSection: jest.fn().mockReturnValue(mockKibanaSection), + }, + }; + + const deps = { + managementStart, + }; + + const service = new ManagementService(); + service.start(deps); + + expect(mockKibanaSection.register).toHaveBeenCalledTimes(0); + }); + + it('will not register the spaces management page if the kibana section is missing', () => { + const managementStart = { + legacy: { + getSection: jest.fn().mockReturnValue(undefined), + }, + }; + + const deps = { + managementStart, + }; + + const service = new ManagementService(); + service.start(deps); + + expect(deps.managementStart.legacy.getSection).toHaveBeenCalledTimes(1); + }); + }); + + describe('#stop', () => { + it('deregisters the spaces management page', () => { + const mockKibanaSection = { + hasItem: jest + .fn() + .mockReturnValueOnce(false) + .mockReturnValueOnce(true), + register: jest.fn(), + deregister: jest.fn(), + }; + + const managementStart = { + legacy: { + getSection: jest.fn().mockReturnValue(mockKibanaSection), + }, + }; + + const deps = { + managementStart, + }; + + const service = new ManagementService(); + service.start(deps); + + service.stop(); + + expect(mockKibanaSection.register).toHaveBeenCalledTimes(1); + expect(mockKibanaSection.deregister).toHaveBeenCalledTimes(1); + expect(mockKibanaSection.deregister).toHaveBeenCalledWith('spaces'); + }); + }); +}); diff --git a/x-pack/legacy/plugins/spaces/public/management/management_service.ts b/x-pack/legacy/plugins/spaces/public/management/management_service.ts new file mode 100644 index 0000000000000..ada38f5cf3387 --- /dev/null +++ b/x-pack/legacy/plugins/spaces/public/management/management_service.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { i18n } from '@kbn/i18n'; +import { ManagementStart } from 'src/plugins/management/public'; + +interface StartDeps { + managementStart: ManagementStart; +} + +const MANAGE_SPACES_KEY = 'spaces'; + +export class ManagementService { + private kibanaSection!: any; + + public start({ managementStart }: StartDeps) { + this.kibanaSection = managementStart.legacy.getSection('kibana'); + if (this.kibanaSection && !this.kibanaSection.hasItem(MANAGE_SPACES_KEY)) { + this.kibanaSection.register(MANAGE_SPACES_KEY, { + name: 'spacesManagementLink', + order: 10, + display: i18n.translate('xpack.spaces.displayName', { + defaultMessage: 'Spaces', + }), + url: `#/management/spaces/list`, + }); + } + } + + public stop() { + if (this.kibanaSection && this.kibanaSection.hasItem(MANAGE_SPACES_KEY)) { + this.kibanaSection.deregister(MANAGE_SPACES_KEY); + } + } +} diff --git a/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap rename to x-pack/legacy/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/index.ts b/x-pack/legacy/plugins/spaces/public/management/spaces_grid/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/index.ts rename to x-pack/legacy/plugins/spaces/public/management/spaces_grid/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_page.tsx b/x-pack/legacy/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx similarity index 96% rename from x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_page.tsx rename to x-pack/legacy/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx index 9fa03b1a9b74a..6ca1877642bdc 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_page.tsx +++ b/x-pack/legacy/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx @@ -22,13 +22,13 @@ import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import { kfetch } from 'ui/kfetch'; import { toastNotifications } from 'ui/notify'; import { Capabilities } from 'src/core/public'; -import { Feature } from '../../../../../../../plugins/features/public'; -import { isReservedSpace } from '../../../../common'; -import { DEFAULT_SPACE_ID } from '../../../../common/constants'; -import { Space } from '../../../../common/model/space'; -import { SpaceAvatar } from '../../../components'; -import { getSpacesFeatureDescription } from '../../../lib/constants'; -import { SpacesManager } from '../../../lib/spaces_manager'; +import { Feature } from '../../../../../../plugins/features/public'; +import { isReservedSpace } from '../../../common'; +import { DEFAULT_SPACE_ID } from '../../../common/constants'; +import { Space } from '../../../common/model/space'; +import { SpaceAvatar } from '../../space_avatar'; +import { getSpacesFeatureDescription } from '../../constants'; +import { SpacesManager } from '../..//spaces_manager'; import { ConfirmDeleteModal } from '../components/confirm_delete_modal'; import { SecureSpaceMessage } from '../components/secure_space_message'; import { UnauthorizedPrompt } from '../components/unauthorized_prompt'; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_pages.test.tsx b/x-pack/legacy/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx similarity index 90% rename from x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_pages.test.tsx rename to x-pack/legacy/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx index 4add607707b24..7856d2e7bee01 100644 --- a/x-pack/legacy/plugins/spaces/public/views/management/spaces_grid/spaces_grid_pages.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx @@ -6,12 +6,12 @@ jest.mock('ui/kfetch', () => ({ kfetch: () => Promise.resolve([]), })); -import '../../../__mocks__/xpack_info'; +import '../../__mocks__/xpack_info'; import React from 'react'; import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { SpaceAvatar } from '../../../components'; -import { spacesManagerMock } from '../../../lib/mocks'; -import { SpacesManager } from '../../../lib'; +import { SpaceAvatar } from '../../space_avatar'; +import { spacesManagerMock } from '../../spaces_manager/mocks'; +import { SpacesManager } from '../../spaces_manager'; import { SpacesGridPage } from './spaces_grid_page'; const spaces = [ diff --git a/x-pack/legacy/plugins/spaces/public/views/management/template.html b/x-pack/legacy/plugins/spaces/public/management/template.html similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/management/template.html rename to x-pack/legacy/plugins/spaces/public/management/template.html diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/__snapshots__/nav_control_popover.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/nav_control/__snapshots__/nav_control_popover.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/nav_control/__snapshots__/nav_control_popover.test.tsx.snap rename to x-pack/legacy/plugins/spaces/public/nav_control/__snapshots__/nav_control_popover.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/_index.scss b/x-pack/legacy/plugins/spaces/public/nav_control/_index.scss similarity index 55% rename from x-pack/legacy/plugins/spaces/public/views/nav_control/_index.scss rename to x-pack/legacy/plugins/spaces/public/nav_control/_index.scss index 192091fb04e3c..d0471da325cec 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/_index.scss +++ b/x-pack/legacy/plugins/spaces/public/nav_control/_index.scss @@ -1 +1,2 @@ @import './components/index'; +@import './nav_control'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/_nav_control.scss b/x-pack/legacy/plugins/spaces/public/nav_control/_nav_control.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/nav_control/_nav_control.scss rename to x-pack/legacy/plugins/spaces/public/nav_control/_nav_control.scss diff --git a/x-pack/legacy/plugins/spaces/public/components/__snapshots__/manage_spaces_button.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/nav_control/components/__snapshots__/manage_spaces_button.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/components/__snapshots__/manage_spaces_button.test.tsx.snap rename to x-pack/legacy/plugins/spaces/public/nav_control/components/__snapshots__/manage_spaces_button.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/__snapshots__/spaces_description.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/nav_control/components/__snapshots__/spaces_description.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/nav_control/components/__snapshots__/spaces_description.test.tsx.snap rename to x-pack/legacy/plugins/spaces/public/nav_control/components/__snapshots__/spaces_description.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/_index.scss b/x-pack/legacy/plugins/spaces/public/nav_control/components/_index.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/nav_control/components/_index.scss rename to x-pack/legacy/plugins/spaces/public/nav_control/components/_index.scss diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/_spaces_description.scss b/x-pack/legacy/plugins/spaces/public/nav_control/components/_spaces_description.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/nav_control/components/_spaces_description.scss rename to x-pack/legacy/plugins/spaces/public/nav_control/components/_spaces_description.scss diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/_spaces_menu.scss b/x-pack/legacy/plugins/spaces/public/nav_control/components/_spaces_menu.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/nav_control/components/_spaces_menu.scss rename to x-pack/legacy/plugins/spaces/public/nav_control/components/_spaces_menu.scss diff --git a/x-pack/legacy/plugins/spaces/public/components/manage_spaces_button.test.tsx b/x-pack/legacy/plugins/spaces/public/nav_control/components/manage_spaces_button.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/components/manage_spaces_button.test.tsx rename to x-pack/legacy/plugins/spaces/public/nav_control/components/manage_spaces_button.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/components/manage_spaces_button.tsx b/x-pack/legacy/plugins/spaces/public/nav_control/components/manage_spaces_button.tsx similarity index 96% rename from x-pack/legacy/plugins/spaces/public/components/manage_spaces_button.tsx rename to x-pack/legacy/plugins/spaces/public/nav_control/components/manage_spaces_button.tsx index 91a0803c20bc9..857d0c1f828a6 100644 --- a/x-pack/legacy/plugins/spaces/public/components/manage_spaces_button.tsx +++ b/x-pack/legacy/plugins/spaces/public/nav_control/components/manage_spaces_button.tsx @@ -8,7 +8,7 @@ import { EuiButton } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component, CSSProperties } from 'react'; import { Capabilities } from 'src/core/public'; -import { getManageSpacesUrl } from '../lib/constants'; +import { getManageSpacesUrl } from '../../constants'; interface Props { isDisabled?: boolean; diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_description.test.tsx b/x-pack/legacy/plugins/spaces/public/nav_control/components/spaces_description.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_description.test.tsx rename to x-pack/legacy/plugins/spaces/public/nav_control/components/spaces_description.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_description.tsx b/x-pack/legacy/plugins/spaces/public/nav_control/components/spaces_description.tsx similarity index 89% rename from x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_description.tsx rename to x-pack/legacy/plugins/spaces/public/nav_control/components/spaces_description.tsx index 043fc656a571e..abf3c636b839e 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_description.tsx +++ b/x-pack/legacy/plugins/spaces/public/nav_control/components/spaces_description.tsx @@ -7,8 +7,8 @@ import { EuiContextMenuPanel, EuiText } from '@elastic/eui'; import React, { FC } from 'react'; import { Capabilities } from 'src/core/public'; -import { ManageSpacesButton } from '../../../components'; -import { getSpacesFeatureDescription } from '../../../lib/constants'; +import { ManageSpacesButton } from './manage_spaces_button'; +import { getSpacesFeatureDescription } from '../../constants'; interface Props { onManageSpacesClick: () => void; diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_menu.tsx b/x-pack/legacy/plugins/spaces/public/nav_control/components/spaces_menu.tsx similarity index 95% rename from x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_menu.tsx rename to x-pack/legacy/plugins/spaces/public/nav_control/components/spaces_menu.tsx index 9a26f6802abdf..96ce18896b426 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/components/spaces_menu.tsx +++ b/x-pack/legacy/plugins/spaces/public/nav_control/components/spaces_menu.tsx @@ -14,9 +14,10 @@ import { import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React, { Component, ReactElement } from 'react'; import { Capabilities } from 'src/core/public'; -import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../../../common/constants'; -import { Space } from '../../../../common/model/space'; -import { ManageSpacesButton, SpaceAvatar } from '../../../components'; +import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../../common/constants'; +import { Space } from '../../../common/model/space'; +import { ManageSpacesButton } from './manage_spaces_button'; +import { SpaceAvatar } from '../../space_avatar'; interface Props { spaces: Space[]; diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/index.ts b/x-pack/legacy/plugins/spaces/public/nav_control/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/nav_control/index.ts rename to x-pack/legacy/plugins/spaces/public/nav_control/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control.tsx b/x-pack/legacy/plugins/spaces/public/nav_control/nav_control.tsx similarity index 94% rename from x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control.tsx rename to x-pack/legacy/plugins/spaces/public/nav_control/nav_control.tsx index 0df077e0d2da0..9ec070eff3fed 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control.tsx +++ b/x-pack/legacy/plugins/spaces/public/nav_control/nav_control.tsx @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SpacesManager } from 'plugins/spaces/lib/spaces_manager'; import React from 'react'; import ReactDOM from 'react-dom'; import { CoreStart } from 'src/core/public'; +import { SpacesManager } from '../spaces_manager'; import { NavControlPopover } from './nav_control_popover'; export function initSpacesNavControl(spacesManager: SpacesManager, core: CoreStart) { diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.test.tsx b/x-pack/legacy/plugins/spaces/public/nav_control/nav_control_popover.test.tsx similarity index 91% rename from x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.test.tsx rename to x-pack/legacy/plugins/spaces/public/nav_control/nav_control_popover.test.tsx index a04f28242f984..5ce141abb713e 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/nav_control/nav_control_popover.test.tsx @@ -7,9 +7,9 @@ import * as Rx from 'rxjs'; import { shallow } from 'enzyme'; import React from 'react'; -import { SpaceAvatar } from '../../components'; -import { spacesManagerMock } from '../../lib/mocks'; -import { SpacesManager } from '../../lib'; +import { SpaceAvatar } from '../space_avatar'; +import { spacesManagerMock } from '../spaces_manager/mocks'; +import { SpacesManager } from '../spaces_manager'; import { NavControlPopover } from './nav_control_popover'; import { EuiHeaderSectionItemButton } from '@elastic/eui'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; @@ -42,6 +42,7 @@ describe('NavControlPopover', () => { disabledFeatures: [], }, ]); + // @ts-ignore readonly check spacesManager.onActiveSpaceChange$ = Rx.of({ id: 'foo-space', name: 'foo', diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx b/x-pack/legacy/plugins/spaces/public/nav_control/nav_control_popover.tsx similarity index 96% rename from x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx rename to x-pack/legacy/plugins/spaces/public/nav_control/nav_control_popover.tsx index b37458aace2a2..f291027e15232 100644 --- a/x-pack/legacy/plugins/spaces/public/views/nav_control/nav_control_popover.tsx +++ b/x-pack/legacy/plugins/spaces/public/nav_control/nav_control_popover.tsx @@ -13,9 +13,9 @@ import { import React, { Component } from 'react'; import { Capabilities } from 'src/core/public'; import { Subscription } from 'rxjs'; -import { Space } from '../../../common/model/space'; -import { SpaceAvatar } from '../../components'; -import { SpacesManager } from '../../lib/spaces_manager'; +import { Space } from '../../common/model/space'; +import { SpaceAvatar } from '../space_avatar'; +import { SpacesManager } from '../spaces_manager'; import { SpacesDescription } from './components/spaces_description'; import { SpacesMenu } from './components/spaces_menu'; diff --git a/x-pack/legacy/plugins/spaces/public/views/nav_control/types.tsx b/x-pack/legacy/plugins/spaces/public/nav_control/types.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/nav_control/types.tsx rename to x-pack/legacy/plugins/spaces/public/nav_control/types.tsx diff --git a/x-pack/legacy/plugins/spaces/public/plugin.tsx b/x-pack/legacy/plugins/spaces/public/plugin.tsx index 4e070c3cee3df..1ddb69a5b595c 100644 --- a/x-pack/legacy/plugins/spaces/public/plugin.tsx +++ b/x-pack/legacy/plugins/spaces/public/plugin.tsx @@ -6,9 +6,14 @@ import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; import { HomePublicPluginSetup } from 'src/plugins/home/public'; -import { SpacesManager } from './lib'; -import { initSpacesNavControl } from './views/nav_control'; +import { ManagementSetup } from 'src/legacy/core_plugins/management/public'; +import { ManagementStart } from 'src/plugins/management/public'; +import { SpacesManager } from './spaces_manager'; +import { initSpacesNavControl } from './nav_control'; import { createSpacesFeatureCatalogueEntry } from './create_feature_catalogue_entry'; +import { CopySavedObjectsToSpaceService } from './copy_saved_objects_to_space'; +import { AdvancedSettingsService } from './advanced_settings'; +import { ManagementService } from './management'; export interface SpacesPluginStart { spacesManager: SpacesManager | null; @@ -16,25 +21,61 @@ export interface SpacesPluginStart { export interface PluginsSetup { home?: HomePublicPluginSetup; + management: ManagementSetup; + __managementLegacyCompat: { + registerSettingsComponent: ( + id: string, + component: string | React.FC, + allowOverride: boolean + ) => void; + }; +} + +export interface PluginsStart { + management: ManagementStart; } export class SpacesPlugin implements Plugin { - private spacesManager: SpacesManager | null = null; + private spacesManager!: SpacesManager; - public async start(core: CoreStart) { - const serverBasePath = core.injectedMetadata.getInjectedVar('serverBasePath') as string; + private managementService?: ManagementService; + public setup(core: CoreSetup, plugins: PluginsSetup) { + const serverBasePath = core.injectedMetadata.getInjectedVar('serverBasePath') as string; this.spacesManager = new SpacesManager(serverBasePath, core.http); + + const copySavedObjectsToSpaceService = new CopySavedObjectsToSpaceService(); + copySavedObjectsToSpaceService.setup({ + spacesManager: this.spacesManager, + managementSetup: plugins.management, + }); + + const advancedSettingsService = new AdvancedSettingsService(); + advancedSettingsService.setup({ + getActiveSpace: () => this.spacesManager.getActiveSpace(), + registerSettingsComponent: plugins.__managementLegacyCompat.registerSettingsComponent, + }); + + if (plugins.home) { + plugins.home.featureCatalogue.register(createSpacesFeatureCatalogueEntry()); + } + } + + public start(core: CoreStart, plugins: PluginsStart) { initSpacesNavControl(this.spacesManager, core); + this.managementService = new ManagementService(); + this.managementService.start({ managementStart: plugins.management }); + return { spacesManager: this.spacesManager, }; } - public async setup(core: CoreSetup, plugins: PluginsSetup) { - if (plugins.home) { - plugins.home.featureCatalogue.register(createSpacesFeatureCatalogueEntry()); + public stop() { + if (this.managementService) { + this.managementService.stop(); + this.managementService = undefined; } } } diff --git a/x-pack/legacy/plugins/spaces/public/components/__snapshots__/space_avatar.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/space_avatar/__snapshots__/space_avatar.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/components/__snapshots__/space_avatar.test.tsx.snap rename to x-pack/legacy/plugins/spaces/public/space_avatar/__snapshots__/space_avatar.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/components/index.ts b/x-pack/legacy/plugins/spaces/public/space_avatar/index.ts similarity index 82% rename from x-pack/legacy/plugins/spaces/public/components/index.ts rename to x-pack/legacy/plugins/spaces/public/space_avatar/index.ts index 2e73f0c704f8c..1525f2c8c6186 100644 --- a/x-pack/legacy/plugins/spaces/public/components/index.ts +++ b/x-pack/legacy/plugins/spaces/public/space_avatar/index.ts @@ -5,4 +5,4 @@ */ export { SpaceAvatar } from './space_avatar'; -export { ManageSpacesButton } from './manage_spaces_button'; +export * from './space_attributes'; diff --git a/x-pack/legacy/plugins/spaces/public/lib/space_attributes.test.ts b/x-pack/legacy/plugins/spaces/public/space_avatar/space_attributes.test.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/lib/space_attributes.test.ts rename to x-pack/legacy/plugins/spaces/public/space_avatar/space_attributes.test.ts diff --git a/x-pack/legacy/plugins/spaces/public/lib/space_attributes.ts b/x-pack/legacy/plugins/spaces/public/space_avatar/space_attributes.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/lib/space_attributes.ts rename to x-pack/legacy/plugins/spaces/public/space_avatar/space_attributes.ts diff --git a/x-pack/legacy/plugins/spaces/public/components/space_avatar.test.tsx b/x-pack/legacy/plugins/spaces/public/space_avatar/space_avatar.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/components/space_avatar.test.tsx rename to x-pack/legacy/plugins/spaces/public/space_avatar/space_avatar.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/components/space_avatar.tsx b/x-pack/legacy/plugins/spaces/public/space_avatar/space_avatar.tsx similarity index 98% rename from x-pack/legacy/plugins/spaces/public/components/space_avatar.tsx rename to x-pack/legacy/plugins/spaces/public/space_avatar/space_avatar.tsx index 0d9751ca43db9..c89f492a8fc99 100644 --- a/x-pack/legacy/plugins/spaces/public/components/space_avatar.tsx +++ b/x-pack/legacy/plugins/spaces/public/space_avatar/space_avatar.tsx @@ -8,7 +8,7 @@ import { EuiAvatar, isValidHex } from '@elastic/eui'; import React, { FC } from 'react'; import { MAX_SPACE_INITIALS } from '../../common'; import { Space } from '../../common/model/space'; -import { getSpaceColor, getSpaceInitials, getSpaceImageUrl } from '../lib/space_attributes'; +import { getSpaceColor, getSpaceInitials, getSpaceImageUrl } from './space_attributes'; interface Props { space: Partial; diff --git a/x-pack/legacy/plugins/spaces/public/views/space_selector/__snapshots__/space_selector.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/space_selector/__snapshots__/space_selector.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/space_selector/__snapshots__/space_selector.test.tsx.snap rename to x-pack/legacy/plugins/spaces/public/space_selector/__snapshots__/space_selector.test.tsx.snap diff --git a/x-pack/legacy/plugins/spaces/public/space_selector/_index.scss b/x-pack/legacy/plugins/spaces/public/space_selector/_index.scss new file mode 100644 index 0000000000000..0621aa2a3efd7 --- /dev/null +++ b/x-pack/legacy/plugins/spaces/public/space_selector/_index.scss @@ -0,0 +1,2 @@ +@import './space_selector'; +@import './components/index'; diff --git a/x-pack/legacy/plugins/spaces/public/views/space_selector/_space_selector.scss b/x-pack/legacy/plugins/spaces/public/space_selector/_space_selector.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/space_selector/_space_selector.scss rename to x-pack/legacy/plugins/spaces/public/space_selector/_space_selector.scss diff --git a/x-pack/legacy/plugins/spaces/public/views/components/_index.scss b/x-pack/legacy/plugins/spaces/public/space_selector/components/_index.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/components/_index.scss rename to x-pack/legacy/plugins/spaces/public/space_selector/components/_index.scss diff --git a/x-pack/legacy/plugins/spaces/public/views/components/_space_card.scss b/x-pack/legacy/plugins/spaces/public/space_selector/components/_space_card.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/components/_space_card.scss rename to x-pack/legacy/plugins/spaces/public/space_selector/components/_space_card.scss diff --git a/x-pack/legacy/plugins/spaces/public/views/components/_space_cards.scss b/x-pack/legacy/plugins/spaces/public/space_selector/components/_space_cards.scss similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/components/_space_cards.scss rename to x-pack/legacy/plugins/spaces/public/space_selector/components/_space_cards.scss diff --git a/x-pack/legacy/plugins/spaces/public/views/components/index.ts b/x-pack/legacy/plugins/spaces/public/space_selector/components/index.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/components/index.ts rename to x-pack/legacy/plugins/spaces/public/space_selector/components/index.ts diff --git a/x-pack/legacy/plugins/spaces/public/views/components/space_card.test.tsx b/x-pack/legacy/plugins/spaces/public/space_selector/components/space_card.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/components/space_card.test.tsx rename to x-pack/legacy/plugins/spaces/public/space_selector/components/space_card.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/views/components/space_card.tsx b/x-pack/legacy/plugins/spaces/public/space_selector/components/space_card.tsx similarity index 90% rename from x-pack/legacy/plugins/spaces/public/views/components/space_card.tsx rename to x-pack/legacy/plugins/spaces/public/space_selector/components/space_card.tsx index 2386f6a6fe9d0..f898ba87c60bd 100644 --- a/x-pack/legacy/plugins/spaces/public/views/components/space_card.tsx +++ b/x-pack/legacy/plugins/spaces/public/space_selector/components/space_card.tsx @@ -4,14 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - // FIXME: need updated typedefs - // @ts-ignore - EuiCard, -} from '@elastic/eui'; +import { EuiCard } from '@elastic/eui'; import React from 'react'; import { Space } from '../../../common/model/space'; -import { SpaceAvatar } from '../../components'; +import { SpaceAvatar } from '../../space_avatar'; interface Props { space: Space; diff --git a/x-pack/legacy/plugins/spaces/public/views/components/space_cards.test.tsx b/x-pack/legacy/plugins/spaces/public/space_selector/components/space_cards.test.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/components/space_cards.test.tsx rename to x-pack/legacy/plugins/spaces/public/space_selector/components/space_cards.test.tsx diff --git a/x-pack/legacy/plugins/spaces/public/views/components/space_cards.tsx b/x-pack/legacy/plugins/spaces/public/space_selector/components/space_cards.tsx similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/components/space_cards.tsx rename to x-pack/legacy/plugins/spaces/public/space_selector/components/space_cards.tsx diff --git a/x-pack/legacy/plugins/spaces/public/views/space_selector/index.tsx b/x-pack/legacy/plugins/spaces/public/space_selector/index.tsx similarity index 82% rename from x-pack/legacy/plugins/spaces/public/views/space_selector/index.tsx rename to x-pack/legacy/plugins/spaces/public/space_selector/index.tsx index c520c2683c965..c1c1b6dc3a2f3 100644 --- a/x-pack/legacy/plugins/spaces/public/views/space_selector/index.tsx +++ b/x-pack/legacy/plugins/spaces/public/space_selector/index.tsx @@ -5,7 +5,7 @@ */ // @ts-ignore -import template from 'plugins/spaces/views/space_selector/space_selector.html'; +import template from 'plugins/spaces/space_selector/space_selector.html'; import chrome from 'ui/chrome'; import { I18nContext } from 'ui/i18n'; // @ts-ignore @@ -15,14 +15,14 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { SpaceSelector } from './space_selector'; -import { start as spacesNPStart } from '../../legacy'; +import { start as spacesNPStart } from '../legacy'; const module = uiModules.get('spaces_selector', []); module.controller('spacesSelectorController', ($scope: any) => { - $scope.$$postDigest(async () => { + $scope.$$postDigest(() => { const domNode = document.getElementById('spaceSelectorRoot'); - const { spacesManager } = await spacesNPStart; + const { spacesManager } = spacesNPStart; render( diff --git a/x-pack/legacy/plugins/spaces/public/views/space_selector/space_selector.html b/x-pack/legacy/plugins/spaces/public/space_selector/space_selector.html similarity index 100% rename from x-pack/legacy/plugins/spaces/public/views/space_selector/space_selector.html rename to x-pack/legacy/plugins/spaces/public/space_selector/space_selector.html diff --git a/x-pack/legacy/plugins/spaces/public/views/space_selector/space_selector.test.tsx b/x-pack/legacy/plugins/spaces/public/space_selector/space_selector.test.tsx similarity index 92% rename from x-pack/legacy/plugins/spaces/public/views/space_selector/space_selector.test.tsx rename to x-pack/legacy/plugins/spaces/public/space_selector/space_selector.test.tsx index 829312061ca98..b4d0f96307500 100644 --- a/x-pack/legacy/plugins/spaces/public/views/space_selector/space_selector.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/space_selector/space_selector.test.tsx @@ -6,8 +6,8 @@ import React from 'react'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { Space } from '../../../common/model/space'; -import { spacesManagerMock } from '../../lib/mocks'; +import { Space } from '../../common/model/space'; +import { spacesManagerMock } from '../spaces_manager/mocks'; import { SpaceSelector } from './space_selector'; function getSpacesManager(spaces: Space[] = []) { diff --git a/x-pack/legacy/plugins/spaces/public/views/space_selector/space_selector.tsx b/x-pack/legacy/plugins/spaces/public/space_selector/space_selector.tsx similarity index 95% rename from x-pack/legacy/plugins/spaces/public/views/space_selector/space_selector.tsx rename to x-pack/legacy/plugins/spaces/public/space_selector/space_selector.tsx index d665752b3c8a6..206d38454fa8c 100644 --- a/x-pack/legacy/plugins/spaces/public/views/space_selector/space_selector.tsx +++ b/x-pack/legacy/plugins/spaces/public/space_selector/space_selector.tsx @@ -19,11 +19,11 @@ import { EuiLoadingSpinner, } from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; -import { SpacesManager } from 'plugins/spaces/lib'; import React, { Component, Fragment } from 'react'; -import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../../common/constants'; -import { Space } from '../../../common/model/space'; -import { SpaceCards } from '../components/space_cards'; +import { SpacesManager } from '../spaces_manager'; +import { Space } from '../../common/model/space'; +import { SpaceCards } from './components'; +import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../common/constants'; interface Props { spacesManager: SpacesManager; diff --git a/x-pack/legacy/plugins/uptime/public/lib/adapters/index_pattern/index.ts b/x-pack/legacy/plugins/spaces/public/spaces_manager/index.ts similarity index 81% rename from x-pack/legacy/plugins/uptime/public/lib/adapters/index_pattern/index.ts rename to x-pack/legacy/plugins/spaces/public/spaces_manager/index.ts index 1c84a7bc3b727..538dd77e053f5 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/adapters/index_pattern/index.ts +++ b/x-pack/legacy/plugins/spaces/public/spaces_manager/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { getIndexPattern } from './get_index_pattern'; +export { SpacesManager } from './spaces_manager'; diff --git a/x-pack/legacy/plugins/spaces/public/lib/mocks.ts b/x-pack/legacy/plugins/spaces/public/spaces_manager/mocks.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/lib/mocks.ts rename to x-pack/legacy/plugins/spaces/public/spaces_manager/mocks.ts diff --git a/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.mock.ts b/x-pack/legacy/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts similarity index 87% rename from x-pack/legacy/plugins/spaces/public/lib/spaces_manager.mock.ts rename to x-pack/legacy/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts index 69c6f7a452fdd..56879af33916f 100644 --- a/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.mock.ts +++ b/x-pack/legacy/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts @@ -6,9 +6,10 @@ import { of, Observable } from 'rxjs'; import { Space } from '../../common/model/space'; +import { SpacesManager } from './spaces_manager'; function createSpacesManagerMock() { - return { + return ({ onActiveSpaceChange$: (of(undefined) as unknown) as Observable, getSpaces: jest.fn().mockResolvedValue([]), getSpace: jest.fn().mockResolvedValue(undefined), @@ -19,7 +20,8 @@ function createSpacesManagerMock() { copySavedObjects: jest.fn().mockResolvedValue(undefined), resolveCopySavedObjectsErrors: jest.fn().mockResolvedValue(undefined), redirectToSpaceSelector: jest.fn().mockResolvedValue(undefined), - }; + changeSelectedSpace: jest.fn(), + } as unknown) as jest.Mocked; } export const spacesManagerMock = { diff --git a/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.test.ts b/x-pack/legacy/plugins/spaces/public/spaces_manager/spaces_manager.test.ts similarity index 100% rename from x-pack/legacy/plugins/spaces/public/lib/spaces_manager.test.ts rename to x-pack/legacy/plugins/spaces/public/spaces_manager/spaces_manager.test.ts diff --git a/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.ts b/x-pack/legacy/plugins/spaces/public/spaces_manager/spaces_manager.ts similarity index 97% rename from x-pack/legacy/plugins/spaces/public/lib/spaces_manager.ts rename to x-pack/legacy/plugins/spaces/public/spaces_manager/spaces_manager.ts index ccc1b00dabb29..e9c738cf40c69 100644 --- a/x-pack/legacy/plugins/spaces/public/lib/spaces_manager.ts +++ b/x-pack/legacy/plugins/spaces/public/spaces_manager/spaces_manager.ts @@ -9,9 +9,9 @@ import { HttpSetup } from 'src/core/public'; import { SavedObjectsManagementRecord } from '../../../../../../src/legacy/core_plugins/management/public'; import { Space } from '../../common/model/space'; import { GetSpacePurpose } from '../../common/model/types'; -import { CopySavedObjectsToSpaceResponse } from './copy_saved_objects_to_space/types'; import { ENTER_SPACE_PATH } from '../../common/constants'; import { addSpaceIdToPath } from '../../../../../plugins/spaces/common'; +import { CopySavedObjectsToSpaceResponse } from '../copy_saved_objects_to_space/types'; export class SpacesManager { private activeSpace$: BehaviorSubject = new BehaviorSubject(null); diff --git a/x-pack/legacy/plugins/spaces/public/views/_index.scss b/x-pack/legacy/plugins/spaces/public/views/_index.scss deleted file mode 100644 index 0cc8ccb10246b..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/views/_index.scss +++ /dev/null @@ -1,4 +0,0 @@ -@import './components/index'; -@import './management/index'; -@import './nav_control/index'; -@import './space_selector/index' diff --git a/x-pack/legacy/plugins/spaces/public/views/management/_index.scss b/x-pack/legacy/plugins/spaces/public/views/management/_index.scss deleted file mode 100644 index e7cbdfe2de7e8..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/views/management/_index.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import './components/confirm_delete_modal'; -@import './edit_space/enabled_features/index'; -@import './components/copy_saved_objects_to_space/index'; diff --git a/x-pack/legacy/plugins/spaces/public/views/management/index.tsx b/x-pack/legacy/plugins/spaces/public/views/management/index.tsx deleted file mode 100644 index bf33273c614d6..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/views/management/index.tsx +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import { i18n } from '@kbn/i18n'; -import 'plugins/spaces/views/management/page_routes'; -import React from 'react'; -import { - management, - PAGE_SUBTITLE_COMPONENT, - PAGE_TITLE_COMPONENT, - registerSettingsComponent, -} from 'ui/management'; -// @ts-ignore -import routes from 'ui/routes'; -import { setup as managementSetup } from '../../../../../../../src/legacy/core_plugins/management/public/legacy'; -import { AdvancedSettingsSubtitle } from './components/advanced_settings_subtitle'; -import { AdvancedSettingsTitle } from './components/advanced_settings_title'; -import { start as spacesNPStart } from '../../legacy'; -import { CopyToSpaceSavedObjectsManagementAction } from '../../lib/copy_saved_objects_to_space'; - -const MANAGE_SPACES_KEY = 'spaces'; - -routes.defaults(/\/management/, { - resolve: { - spacesManagementSection() { - function getKibanaSection() { - return management.getSection('kibana'); - } - - function deregisterSpaces() { - getKibanaSection().deregister(MANAGE_SPACES_KEY); - } - - function ensureSpagesRegistered() { - const kibanaSection = getKibanaSection(); - - if (!kibanaSection.hasItem(MANAGE_SPACES_KEY)) { - kibanaSection.register(MANAGE_SPACES_KEY, { - name: 'spacesManagementLink', - order: 10, - display: i18n.translate('xpack.spaces.displayName', { - defaultMessage: 'Spaces', - }), - url: `#/management/spaces/list`, - }); - } - - // Customize Saved Objects Management - spacesNPStart.then(({ spacesManager }) => { - const action = new CopyToSpaceSavedObjectsManagementAction(spacesManager!); - // This route resolve function executes any time the management screen is loaded, and we want to ensure - // that this action is only registered once. - if (!managementSetup.savedObjects.registry.has(action.id)) { - managementSetup.savedObjects.registry.register(action); - } - }); - - const getActiveSpace = async () => { - const { spacesManager } = await spacesNPStart; - return spacesManager!.getActiveSpace(); - }; - - const PageTitle = () => ; - registerSettingsComponent(PAGE_TITLE_COMPONENT, PageTitle, true); - - const SubTitle = () => ; - registerSettingsComponent(PAGE_SUBTITLE_COMPONENT, SubTitle, true); - } - - deregisterSpaces(); - - ensureSpagesRegistered(); - }, - }, -}); diff --git a/x-pack/legacy/plugins/spaces/public/views/management/lib/breadcrumbs.ts b/x-pack/legacy/plugins/spaces/public/views/management/lib/breadcrumbs.ts deleted file mode 100644 index a4e8ba508b617..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/views/management/lib/breadcrumbs.ts +++ /dev/null @@ -1,36 +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 { MANAGEMENT_BREADCRUMB } from 'ui/management/breadcrumbs'; -import { Space } from '../../../../common/model/space'; - -export function getListBreadcrumbs() { - return [ - MANAGEMENT_BREADCRUMB, - { - text: 'Spaces', - href: '#/management/spaces/list', - }, - ]; -} - -export function getCreateBreadcrumbs() { - return [ - ...getListBreadcrumbs(), - { - text: 'Create', - }, - ]; -} - -export function getEditBreadcrumbs(space?: Space) { - return [ - ...getListBreadcrumbs(), - { - text: space ? space.name : '...', - }, - ]; -} diff --git a/x-pack/legacy/plugins/spaces/public/views/space_selector/_index.scss b/x-pack/legacy/plugins/spaces/public/views/space_selector/_index.scss deleted file mode 100644 index f23ac662dce1d..0000000000000 --- a/x-pack/legacy/plugins/spaces/public/views/space_selector/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './space_selector'; diff --git a/x-pack/legacy/plugins/uptime/public/apps/plugin.ts b/x-pack/legacy/plugins/uptime/public/apps/plugin.ts index bc4e30b79cb15..c09fdf116e790 100644 --- a/x-pack/legacy/plugins/uptime/public/apps/plugin.ts +++ b/x-pack/legacy/plugins/uptime/public/apps/plugin.ts @@ -30,14 +30,8 @@ export class Plugin { } public start(start: StartObject): void { - const { - core, - plugins: { - data: { autocomplete }, - }, - } = start; const libs: UMFrontendLibs = { - framework: getKibanaFrameworkAdapter(core, autocomplete), + framework: getKibanaFrameworkAdapter(start.core, start.plugins), }; // @ts-ignore improper type description this.chrome.setRootTemplate(template); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/index.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/index.tsx index 72e88d2824073..731f560d315d6 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/index.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/index.tsx @@ -4,14 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, useEffect, useContext } from 'react'; +import React, { useState, useEffect } from 'react'; import { uniqueId, startsWith } from 'lodash'; import { EuiCallOut } from '@elastic/eui'; import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n/react'; import { Typeahead } from './typeahead'; -import { getIndexPattern } from '../../../lib/adapters/index_pattern'; -import { UptimeSettingsContext } from '../../../contexts'; import { useUrlParams } from '../../../hooks'; import { toStaticIndexPattern } from '../../../lib/helper'; import { @@ -20,6 +18,7 @@ import { esKuery, IIndexPattern, } from '../../../../../../../../src/plugins/data/public'; +import { useIndexPattern } from '../../../hooks'; const Container = styled.div` margin-bottom: 10px; @@ -71,16 +70,18 @@ export function KueryBar({ autocomplete }: Props) { suggestions: [], isLoadingIndexPattern: true, }); - const { basePath } = useContext(UptimeSettingsContext); const [indexPattern, setIndexPattern] = useState(undefined); const [isLoadingIndexPattern, setIsLoadingIndexPattern] = useState(true); const [isLoadingSuggestions, setIsLoadingSuggestions] = useState(false); let currentRequestCheck: string; + useIndexPattern((result: any) => setIndexPattern(toStaticIndexPattern(result))); + useEffect(() => { - getIndexPattern(basePath, (result: any) => setIndexPattern(toStaticIndexPattern(result))); - setIsLoadingIndexPattern(false); - }, [basePath]); + if (indexPattern !== undefined) { + setIsLoadingIndexPattern(false); + } + }, [indexPattern]); const [getUrlParams, updateUrlParams] = useUrlParams(); const { search: kuery } = getUrlParams(); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__mocks__/mock.ts b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__tests__/__mocks__/mock.ts similarity index 98% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__mocks__/mock.ts rename to x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__tests__/__mocks__/mock.ts index 9b902651690bf..291ab555fbdc6 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__mocks__/mock.ts +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__tests__/__mocks__/mock.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import lowPolyLayerFeatures from '../low_poly_layer.json'; +import lowPolyLayerFeatures from '../../low_poly_layer.json'; export const mockDownPointsLayer = { id: 'down_points', diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/map_config.test.ts b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__tests__/map_config.test.ts similarity index 83% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/map_config.test.ts rename to x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__tests__/map_config.test.ts index 1e8e5b6012a79..263c3fc787da9 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/map_config.test.ts +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__tests__/map_config.test.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getLayerList } from './map_config'; +import { getLayerList } from '../map_config'; import { mockLayerList } from './__mocks__/mock'; -import { LocationPoint } from './embedded_map'; +import { LocationPoint } from '../embedded_map'; jest.mock('uuid', () => { return { @@ -33,7 +33,7 @@ describe('map_config', () => { describe('#getLayerList', () => { test('it returns the low poly layer', () => { - const layerList = getLayerList(upPoints, downPoints); + const layerList = getLayerList(upPoints, downPoints, { danger: '#BC261E', gray: '#000' }); expect(layerList).toStrictEqual(mockLayerList); }); }); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx index 93de1d478fb83..fe8a1a0bad7ec 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useContext } from 'react'; import uuid from 'uuid'; import styled from 'styled-components'; @@ -15,6 +15,7 @@ import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../maps/common/constants'; import { MapEmbeddable } from './types'; import { getLayerList } from './map_config'; +import { UptimeSettingsContext } from '../../../../contexts'; export interface EmbeddedMapProps { upPoints: LocationPoint[]; @@ -45,6 +46,7 @@ const EmbeddedPanel = styled.div` `; export const EmbeddedMap = ({ upPoints, downPoints }: EmbeddedMapProps) => { + const { colors } = useContext(UptimeSettingsContext); const [embeddable, setEmbeddable] = useState(); const embeddableRoot: React.RefObject = React.createRef(); const factory = start.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE); @@ -58,16 +60,18 @@ export const EmbeddedMap = ({ upPoints, downPoints }: EmbeddedMapProps) => { viewMode: 'view', isLayerTOCOpen: false, hideFilterActions: true, - mapCenter: { lon: 11, lat: 47, zoom: 0 }, + mapCenter: { lon: 11, lat: 20, zoom: 0 }, disableInteractive: true, disableTooltipControl: true, hideToolbarOverlay: true, + hideLayerControl: true, + hideViewControl: true, }; useEffect(() => { async function setupEmbeddable() { const mapState = { - layerList: getLayerList(upPoints, downPoints), + layerList: getLayerList(upPoints, downPoints, colors), title: i18n.MAP_TITLE, }; // @ts-ignore @@ -82,9 +86,9 @@ export const EmbeddedMap = ({ upPoints, downPoints }: EmbeddedMapProps) => { useEffect(() => { if (embeddable) { - embeddable.setLayerList(getLayerList(upPoints, downPoints)); + embeddable.setLayerList(getLayerList(upPoints, downPoints, colors)); } - }, [upPoints, downPoints, embeddable]); + }, [upPoints, downPoints, embeddable, colors]); useEffect(() => { if (embeddableRoot.current && embeddable) { diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/map_config.ts b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/map_config.ts index 608df8b235f00..b423b8baf41bf 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/map_config.ts +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/map_config.ts @@ -12,8 +12,12 @@ import { LocationPoint } from './embedded_map'; * destination, and line layer for each of the provided indexPatterns * */ -export const getLayerList = (upPoints: LocationPoint[], downPoints: LocationPoint[]) => { - return [getLowPolyLayer(), getDownPointsLayer(downPoints), getUpPointsLayer(upPoints)]; +export const getLayerList = ( + upPoints: LocationPoint[], + downPoints: LocationPoint[], + { gray, danger }: { gray: string; danger: string } +) => { + return [getLowPolyLayer(), getDownPointsLayer(downPoints, danger), getUpPointsLayer(upPoints)]; }; export const getLowPolyLayer = () => { @@ -62,7 +66,7 @@ export const getLowPolyLayer = () => { }; }; -export const getDownPointsLayer = (downPoints: LocationPoint[]) => { +export const getDownPointsLayer = (downPoints: LocationPoint[], dangerColor: string) => { const features = downPoints?.map(point => ({ type: 'feature', geometry: { @@ -87,7 +91,7 @@ export const getDownPointsLayer = (downPoints: LocationPoint[]) => { fillColor: { type: 'STATIC', options: { - color: '#BC261E', + color: dangerColor, }, }, lineColor: { diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_map.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_map.tsx index b271632cb631f..f70d145ec05c3 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_map.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_map.tsx @@ -6,15 +6,19 @@ import React from 'react'; import styled from 'styled-components'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { LocationStatusTags } from './location_status_tags'; import { EmbeddedMap, LocationPoint } from './embeddables/embedded_map'; +import { MonitorLocations } from '../../../../common/runtime_types'; const MapPanel = styled.div` - height: 400px; + height: 240px; width: 520px; + margin-right: 10px; `; interface LocationMapProps { - monitorLocations: any; + monitorLocations: MonitorLocations; } export const LocationMap = ({ monitorLocations }: LocationMapProps) => { @@ -31,8 +35,15 @@ export const LocationMap = ({ monitorLocations }: LocationMapProps) => { }); } return ( - - - + + + + + + + + + + ); }; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_status_tags.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_status_tags.tsx new file mode 100644 index 0000000000000..a10d8e02e6863 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_status_tags.tsx @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useContext } from 'react'; +import styled from 'styled-components'; +import { EuiBadge, EuiText } from '@elastic/eui'; +import { UptimeSettingsContext } from '../../../contexts'; +import { MonitorLocation } from '../../../../common/runtime_types'; + +const TextStyle = styled.div` + font-weight: 600; +`; + +const BadgeItem = styled.div` + margin-bottom: 5px; +`; + +const TagContainer = styled.div` + padding: 10px; + max-height: 200px; + overflow: hidden; +`; + +interface Props { + locations: MonitorLocation[]; +} + +export const LocationStatusTags = ({ locations }: Props) => { + const { + colors: { gray, danger }, + } = useContext(UptimeSettingsContext); + + const upLocs: string[] = []; + const downLocs: string[] = []; + + locations.forEach((item: any) => { + if (item.summary.down === 0) { + upLocs.push(item.geo.name); + } else { + downLocs.push(item.geo.name); + } + }); + + return ( + + + {downLocs.map((item, ind) => ( + + + + {item} + + + + ))} + + + {upLocs.map((item, ind) => ( + + + + {item} + + + + ))} + + + ); +}; diff --git a/x-pack/legacy/plugins/uptime/public/hooks/index.ts b/x-pack/legacy/plugins/uptime/public/hooks/index.ts index 22de59833b08d..aa7bb0a220357 100644 --- a/x-pack/legacy/plugins/uptime/public/hooks/index.ts +++ b/x-pack/legacy/plugins/uptime/public/hooks/index.ts @@ -5,3 +5,5 @@ */ export { useUrlParams } from './use_url_params'; +export { useIndexPattern } from './use_index_pattern'; +export * from './use_telemetry'; diff --git a/x-pack/legacy/plugins/uptime/public/hooks/use_index_pattern.ts b/x-pack/legacy/plugins/uptime/public/hooks/use_index_pattern.ts new file mode 100644 index 0000000000000..eb9b475a35716 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/hooks/use_index_pattern.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useEffect, Dispatch } from 'react'; +import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; + +export const useIndexPattern = (setIndexPattern: Dispatch) => { + const core = useKibana(); + useEffect(() => { + const fetch = core.services.http?.fetch; + async function getIndexPattern() { + if (!fetch) throw new Error('Http core services are not defined'); + setIndexPattern(await fetch('/api/uptime/index_pattern', { method: 'GET' })); + } + getIndexPattern(); + }, [core.services.http, setIndexPattern]); +}; diff --git a/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts b/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts new file mode 100644 index 0000000000000..15f276174e2cf --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useEffect } from 'react'; +import { HttpHandler } from 'kibana/public'; +import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; + +export enum UptimePage { + Overview = '/api/uptime/logOverview', + Monitor = '/api/uptime/logMonitor', + NotFound = '__not-found__', +} + +const getApiPath = (page?: UptimePage) => { + if (!page) throw new Error('Telemetry logging for this page not yet implemented'); + if (page === '__not-found__') + throw new Error('Telemetry logging for 404 page not yet implemented'); + return page.valueOf(); +}; + +const logPageLoad = async (fetch: HttpHandler, page?: UptimePage) => { + try { + await fetch(getApiPath(page), { + method: 'POST', + }); + } catch (e) { + throw e; + } +}; + +export const useUptimeTelemetry = (page?: UptimePage) => { + const kibana = useKibana(); + const fetch = kibana.services.http?.fetch; + useEffect(() => { + if (!fetch) throw new Error('Core http services are not defined'); + logPageLoad(fetch, page); + }, [fetch, page]); +}; diff --git a/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/new_platform_adapter.tsx b/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/new_platform_adapter.tsx index b7ff3b2aa6264..28179c229013b 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/new_platform_adapter.tsx +++ b/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/new_platform_adapter.tsx @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ChromeBreadcrumb, CoreStart } from 'src/core/public'; +import { ChromeBreadcrumb, LegacyCoreStart } from 'src/core/public'; import React from 'react'; import ReactDOM from 'react-dom'; import { get } from 'lodash'; -import { AutocompleteProviderRegister } from 'src/plugins/data/public'; import { i18n as i18nFormatter } from '@kbn/i18n'; +import { PluginsStart } from 'ui/new_platform/new_platform'; import { CreateGraphQLClient } from './framework_adapter_types'; import { UptimeApp, UptimeAppProps } from '../../../uptime_app'; import { getIntegratedAppAvailability } from './capabilities_adapter'; @@ -19,13 +19,12 @@ import { DEFAULT_DARK_MODE, DEFAULT_TIMEPICKER_QUICK_RANGES, } from '../../../../common/constants'; -import { getTelemetryMonitorPageLogger, getTelemetryOverviewPageLogger } from '../telemetry'; import { UMFrameworkAdapter, BootstrapUptimeApp } from '../../lib'; import { createApolloClient } from './apollo_client_adapter'; export const getKibanaFrameworkAdapter = ( - core: CoreStart, - autocomplete: Pick + core: LegacyCoreStart, + plugins: PluginsStart ): UMFrameworkAdapter => { const { application: { capabilities }, @@ -44,10 +43,10 @@ export const getKibanaFrameworkAdapter = ( ); const canSave = get(capabilities, 'uptime.save', false); const props: UptimeAppProps = { - autocomplete, basePath: basePath.get(), canSave, client: createApolloClient(`${basePath.get()}/api/uptime/graphql`, 'true'), + core, darkMode: core.uiSettings.get(DEFAULT_DARK_MODE), commonlyUsedRanges: core.uiSettings.get(DEFAULT_TIMEPICKER_QUICK_RANGES), i18n, @@ -55,8 +54,7 @@ export const getKibanaFrameworkAdapter = ( isInfraAvailable: infrastructure, isLogsAvailable: logs, kibanaBreadcrumbs: breadcrumbs, - logMonitorPageLoad: getTelemetryMonitorPageLogger('true', basePath.get()), - logOverviewPageLoad: getTelemetryOverviewPageLogger('true', basePath.get()), + plugins, renderGlobalHelpControls: () => setHelpExtension({ appName: i18nFormatter.translate('xpack.uptime.header.appName', { diff --git a/x-pack/legacy/plugins/uptime/public/lib/adapters/index_pattern/__tests__/get_index_pattern.test.ts b/x-pack/legacy/plugins/uptime/public/lib/adapters/index_pattern/__tests__/get_index_pattern.test.ts deleted file mode 100644 index 6654def2f944b..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/lib/adapters/index_pattern/__tests__/get_index_pattern.test.ts +++ /dev/null @@ -1,50 +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 axios, { AxiosRequestConfig } from 'axios'; -import { getIndexPattern } from '../get_index_pattern'; - -describe('getIndexPattern', () => { - let axiosSpy: jest.SpyInstance, [string, (AxiosRequestConfig | undefined)?]>; - beforeEach(() => { - axiosSpy = jest.spyOn(axios, 'get'); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('returns expected data', async () => { - expect.assertions(3); - axiosSpy.mockReturnValue(new Promise(r => r({ data: { foo: 'bar' } }))); - expect(await getIndexPattern()).toEqual({ foo: 'bar' }); - expect(axiosSpy.mock.calls).toHaveLength(1); - expect(axiosSpy.mock.calls[0]).toEqual(['/api/uptime/index_pattern']); - }); - - it('handles the supplied basePath', async () => { - expect.assertions(2); - await getIndexPattern('foo'); - expect(axiosSpy.mock.calls).toHaveLength(1); - expect(axiosSpy.mock.calls[0]).toEqual(['foo/api/uptime/index_pattern']); - }); - - it('supplies the returned data to the given setter function', async () => { - const mockSetter = jest.fn(); - axiosSpy.mockReturnValue(new Promise(r => r({ data: { foo: 'bar' } }))); - await getIndexPattern(undefined, mockSetter); - expect(mockSetter).toHaveBeenCalled(); - expect(mockSetter).toHaveBeenCalledWith({ foo: 'bar' }); - }); - - it('returns undefined when there is an error fetching', async () => { - expect.assertions(1); - axiosSpy.mockReturnValue( - new Promise((resolve, reject) => reject('Request timeout, server could not be reached')) - ); - expect(await getIndexPattern()).toBeUndefined(); - }); -}); diff --git a/x-pack/legacy/plugins/uptime/public/lib/adapters/index_pattern/get_index_pattern.ts b/x-pack/legacy/plugins/uptime/public/lib/adapters/index_pattern/get_index_pattern.ts deleted file mode 100644 index fd4161b35f7dd..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/lib/adapters/index_pattern/get_index_pattern.ts +++ /dev/null @@ -1,26 +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 axios from 'axios'; -import { getApiPath } from '../../helper'; - -/** - * Fetches and returns the uptime index pattern, optionally provides it to - * a given setter function. - * @param basePath - the base path, if any - * @param setter - a callback for use with non-async functions like `useEffect` - */ -export const getIndexPattern = async (basePath?: string, setter?: (data: unknown) => void) => { - try { - const { data } = await axios.get(getApiPath('/api/uptime/index_pattern', basePath)); - if (setter) { - setter(data); - } - return data; - } catch { - return undefined; - } -}; diff --git a/x-pack/legacy/plugins/uptime/public/lib/adapters/telemetry/log_monitor.ts b/x-pack/legacy/plugins/uptime/public/lib/adapters/telemetry/log_monitor.ts deleted file mode 100644 index 20328497d69a8..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/lib/adapters/telemetry/log_monitor.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import axios from 'axios'; -import { getApiPath } from '../../helper'; - -/** - * Generates a function to log a page load of the monitor page for Kibana telemetry. - * @returns a function that can log page loads - */ -export const getTelemetryMonitorPageLogger = (xsrf: string, basePath?: string) => async () => { - await axios.post(getApiPath('/api/uptime/logMonitor', basePath), undefined, { - headers: { 'kbn-xsrf': xsrf }, - }); -}; diff --git a/x-pack/legacy/plugins/uptime/public/lib/adapters/telemetry/log_overview.ts b/x-pack/legacy/plugins/uptime/public/lib/adapters/telemetry/log_overview.ts deleted file mode 100644 index fd9fd773a18b9..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/lib/adapters/telemetry/log_overview.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import axios from 'axios'; -import { getApiPath } from '../../helper'; - -/** - * Generates a function to log a page load of the overview page for Kibana telemtry. - * @returns a function that can log page loads - */ -export const getTelemetryOverviewPageLogger = (xsrf: string, basePath?: string) => async () => { - await axios.post(getApiPath('/api/uptime/logOverview', basePath), undefined, { - headers: { 'kbn-xsrf': xsrf }, - }); -}; diff --git a/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx b/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx index 8c5649f680fcb..c8334b2376bc1 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx +++ b/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx @@ -12,14 +12,13 @@ import { getMonitorPageBreadcrumb } from '../breadcrumbs'; import { MonitorCharts, MonitorPageTitle, PingList } from '../components/functional'; import { UMUpdateBreadcrumbs } from '../lib/lib'; import { UptimeSettingsContext } from '../contexts'; -import { useUrlParams } from '../hooks'; +import { useUptimeTelemetry, useUrlParams, UptimePage } from '../hooks'; import { stringifyUrlParams } from '../lib/helper/stringify_url_params'; import { useTrackPageview } from '../../../infra/public'; import { getTitle } from '../lib/helper/get_title'; import { MonitorStatusDetails } from '../components/functional/monitor_status_details'; interface MonitorPageProps { - logMonitorPageLoad: () => void; match: { params: { monitorId: string } }; // this is the query function provided by Apollo's Client API query: ( @@ -28,12 +27,7 @@ interface MonitorPageProps { setBreadcrumbs: UMUpdateBreadcrumbs; } -export const MonitorPage = ({ - logMonitorPageLoad, - query, - setBreadcrumbs, - match, -}: MonitorPageProps) => { +export const MonitorPage = ({ query, setBreadcrumbs, match }: MonitorPageProps) => { // decode 64 base string, it was decoded to make it a valid url, since monitor id can be a url const monitorId = atob(match.params.monitorId); const [pingListPageCount, setPingListPageCount] = useState(10); @@ -74,9 +68,7 @@ export const MonitorPage = ({ monitorId, }; - useEffect(() => { - logMonitorPageLoad(); - }, [logMonitorPageLoad]); + useUptimeTelemetry(UptimePage.Monitor); useTrackPageview({ app: 'uptime', path: 'monitor' }); useTrackPageview({ app: 'uptime', path: 'monitor', delay: 15000 }); diff --git a/x-pack/legacy/plugins/uptime/public/pages/overview.tsx b/x-pack/legacy/plugins/uptime/public/pages/overview.tsx index 8e72f964ed128..34bcfb994cd48 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/overview.tsx +++ b/x-pack/legacy/plugins/uptime/public/pages/overview.tsx @@ -19,10 +19,9 @@ import { } from '../components/functional'; import { UMUpdateBreadcrumbs } from '../lib/lib'; import { UptimeSettingsContext } from '../contexts'; -import { useUrlParams } from '../hooks'; +import { useIndexPattern, useUrlParams, useUptimeTelemetry, UptimePage } from '../hooks'; import { stringifyUrlParams } from '../lib/helper/stringify_url_params'; import { useTrackPageview } from '../../../infra/public'; -import { getIndexPattern } from '../lib/adapters/index_pattern'; import { combineFiltersAndUserSearch, stringifyKueries, toStaticIndexPattern } from '../lib/helper'; import { AutocompleteProviderRegister, esKuery } from '../../../../../../src/plugins/data/public'; @@ -34,7 +33,6 @@ interface OverviewPageProps { pathname: string; search: string; }; - logOverviewPageLoad: () => void; setBreadcrumbs: UMUpdateBreadcrumbs; } @@ -54,12 +52,7 @@ const EuiFlexItemStyled = styled(EuiFlexItem)` } `; -export const OverviewPage = ({ - basePath, - autocomplete, - logOverviewPageLoad, - setBreadcrumbs, -}: Props) => { +export const OverviewPage = ({ basePath, autocomplete, setBreadcrumbs }: Props) => { const { colors, setHeadingText } = useContext(UptimeSettingsContext); const [getUrlParams, updateUrl] = useUrlParams(); const { absoluteDateRangeStart, absoluteDateRangeEnd, ...params } = getUrlParams(); @@ -72,11 +65,11 @@ export const OverviewPage = ({ filters: urlFilters, } = params; const [indexPattern, setIndexPattern] = useState(undefined); + useUptimeTelemetry(UptimePage.Overview); + useIndexPattern(setIndexPattern); useEffect(() => { - getIndexPattern(basePath, setIndexPattern); setBreadcrumbs(getOverviewPageBreadcrumbs()); - logOverviewPageLoad(); if (setHeadingText) { setHeadingText( i18n.translate('xpack.uptime.overviewPage.headerText', { @@ -85,7 +78,7 @@ export const OverviewPage = ({ }) ); } - }, [basePath, logOverviewPageLoad, setBreadcrumbs, setHeadingText]); + }, [basePath, setBreadcrumbs, setHeadingText]); useTrackPageview({ app: 'uptime', path: 'overview' }); useTrackPageview({ app: 'uptime', path: 'overview', delay: 15000 }); diff --git a/x-pack/legacy/plugins/uptime/public/uptime_app.tsx b/x-pack/legacy/plugins/uptime/public/uptime_app.tsx index f72055c52255d..cecbfe375f5fe 100644 --- a/x-pack/legacy/plugins/uptime/public/uptime_app.tsx +++ b/x-pack/legacy/plugins/uptime/public/uptime_app.tsx @@ -13,8 +13,9 @@ import React, { useEffect, useState } from 'react'; import { ApolloProvider } from 'react-apollo'; import { Provider as ReduxProvider } from 'react-redux'; import { BrowserRouter as Router, Route, RouteComponentProps, Switch } from 'react-router-dom'; -import { I18nStart, ChromeBreadcrumb } from 'src/core/public'; -import { AutocompleteProviderRegister } from 'src/plugins/data/public'; +import { I18nStart, ChromeBreadcrumb, LegacyCoreStart } from 'src/core/public'; +import { PluginsStart } from 'ui/new_platform/new_platform'; +import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; import { UMGraphQLClient, UMUpdateBreadcrumbs, UMUpdateBadge } from './lib/lib'; import { MonitorPage, OverviewPage, NotFoundPage } from './pages'; import { UptimeRefreshContext, UptimeSettingsContext, UMSettingsContextValues } from './contexts'; @@ -37,15 +38,14 @@ export interface UptimeAppProps { basePath: string; canSave: boolean; client: UMGraphQLClient; + core: LegacyCoreStart; darkMode: boolean; - autocomplete: Pick; i18n: I18nStart; isApmAvailable: boolean; isInfraAvailable: boolean; isLogsAvailable: boolean; kibanaBreadcrumbs: ChromeBreadcrumb[]; - logMonitorPageLoad: () => void; - logOverviewPageLoad: () => void; + plugins: PluginsStart; routerBasename: string; setBreadcrumbs: UMUpdateBreadcrumbs; setBadge: UMUpdateBadge; @@ -55,18 +55,17 @@ export interface UptimeAppProps { const Application = (props: UptimeAppProps) => { const { - autocomplete, basePath, canSave, client, + core, darkMode, commonlyUsedRanges, i18n: i18nCore, isApmAvailable, isInfraAvailable, isLogsAvailable, - logMonitorPageLoad, - logOverviewPageLoad, + plugins, renderGlobalHelpControls, routerBasename, setBreadcrumbs, @@ -156,70 +155,70 @@ const Application = (props: UptimeAppProps) => { return ( - - { - return ( - - - - -
- - - -

{headingText}

-
-
- - - -
- - - ( - - )} - /> - ( - + + { + return ( + + + + +
+ + + +

{headingText}

+
+
+ + - )} - /> - - -
-
-
-
-
- ); - }} - /> -
+ + + + + ( + + )} + /> + ( + + )} + /> + + +
+
+
+
+
+ ); + }} + /> +
+
); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 83ef497e50649..af43110a8ba5e 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -6588,8 +6588,6 @@ "xpack.maps.source.wmsTitle": "ウェブマップサービス", "xpack.maps.style.heatmap.displayNameLabel": "ヒートマップスタイル", "xpack.maps.style.heatmap.resolutionStyleErrorMessage": "解像度パラメーターが認識されません: {resolution}", - "xpack.maps.styles.staticDynamic.dynamicDescription": "プロパティ値で特徴をシンボル化します。", - "xpack.maps.styles.staticDynamic.staticDescription": "静的スタイルプロパティで特徴をシンボル化します。", "xpack.maps.styles.vector.borderColorLabel": "境界線の色", "xpack.maps.styles.vector.borderWidthLabel": "境界線の幅", "xpack.maps.styles.vector.fillColorLabel": "塗りつぶす色", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 87c11adcb5e77..0306edcabd67d 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -6590,8 +6590,6 @@ "xpack.maps.source.wmsTitle": "Web 地图服务", "xpack.maps.style.heatmap.displayNameLabel": "热图样式", "xpack.maps.style.heatmap.resolutionStyleErrorMessage": "无法识别分辨率参数:{resolution}", - "xpack.maps.styles.staticDynamic.dynamicDescription": "使用属性值代表功能。", - "xpack.maps.styles.staticDynamic.staticDescription": "使用静态样式属性代表功能。", "xpack.maps.styles.vector.borderColorLabel": "边框颜色", "xpack.maps.styles.vector.borderWidthLabel": "边框宽度", "xpack.maps.styles.vector.fillColorLabel": "填充颜色", diff --git a/x-pack/test/api_integration/apis/telemetry/telemetry_local.js b/x-pack/test/api_integration/apis/telemetry/telemetry_local.js index 0bf24be0fa0b0..ef7124e2864f8 100644 --- a/x-pack/test/api_integration/apis/telemetry/telemetry_local.js +++ b/x-pack/test/api_integration/apis/telemetry/telemetry_local.js @@ -79,6 +79,10 @@ export default function({ getService }) { expect(stats.stack_stats.kibana.plugins.apm.services_per_agent).to.be.an('object'); expect(stats.stack_stats.kibana.plugins.infraops.last_24_hours).to.be.an('object'); expect(stats.stack_stats.kibana.plugins.kql.defaultQueryLanguage).to.be.a('string'); + expect(stats.stack_stats.kibana.plugins['maps-telemetry'].attributes.timeCaptured).to.be.a( + 'string' + ); + expect(stats.stack_stats.kibana.plugins.reporting.enabled).to.be(true); expect(stats.stack_stats.kibana.plugins.rollups.index_patterns).to.be.an('object'); expect(stats.stack_stats.kibana.plugins.spaces.available).to.be(true); diff --git a/x-pack/test/reporting/README.md b/x-pack/test/reporting/README.md index 30859fa96c015..d4a6c20a835e2 100644 --- a/x-pack/test/reporting/README.md +++ b/x-pack/test/reporting/README.md @@ -82,16 +82,6 @@ node scripts/functional_tests_server.js --config test/reporting/configs/chromium **Note:** Dashboard has some snapshot testing too, in `_dashboard_snapshots.js`. This test watches for a command line flag `--updateBaselines` which automates updating the baselines. Probably worthwhile to do some similar here in the long run. - ### Adding a new BWC test - - We have tests that ensure the latest version of Kibana will continue to generate reports from URLs generated in previous versions, to ensure backward compatibility. These tests are in `api/bwc_generation_urls.js`. It's important to update these every now and then and add new ones, especially if anything in the URL changed in a release. - - To add test coverage for a specific minor release,: -1. Checkout previous branch, e.g. `git checkout upstream/6.4` -2. Sync your environment via `yarn kbn bootstrap` (Note, if you run into problems you may want to first clean via `yarn kbn clean`) -3. Start up kibana and Elasticsearch (`yarn es snapshot --license trial` in one terminal, and `yarn start` in another) -4. Load the reporting test data that is used in the tests. Ensure you are in the `x-pack` directory and run: - ``` node ../scripts/es_archiver.js --es-url http://elastic:changeme@localhost:9200 load ../../../../test/functional/fixtures/es_archiver/dashboard/current/kibana ``` diff --git a/x-pack/test/reporting/api/bwc_existing_indexes.js b/x-pack/test/reporting/api/bwc_existing_indexes.js deleted file mode 100644 index ffcf123848bb2..0000000000000 --- a/x-pack/test/reporting/api/bwc_existing_indexes.js +++ /dev/null @@ -1,78 +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 * as GenerationUrls from './generation_urls'; - -/** - * This file tests the situation when a reporting index spans releases. By default reporting indexes are created - * on a weekly basis, but this is configurable so it is possible a user has this set to yearly. In that event, it - * is possible report data is getting posted to an index that was created by a very old version. We don't have a - * reporting index migration plan, so this test is important to ensure BWC, or that in the event we decide to make - * a major change in a major release, we handle it properly. - */ - -export default function({ getService }) { - const esArchiver = getService('esArchiver'); - const reportingAPI = getService('reportingAPI'); - const usageAPI = getService('usageAPI'); - - // FLAKY: https://github.com/elastic/kibana/issues/42725 - describe.skip('BWC report generation into existing indexes', () => { - let expectedCompletedReportCount; - let cleanupIndexAlias; - - describe('existing 6_2 index', () => { - before('load data and add index alias', async () => { - await reportingAPI.deleteAllReportingIndexes(); - await esArchiver.load('reporting/bwc/6_2'); - - // The index name in the 6_2 archive. - const ARCHIVED_REPORTING_INDEX = '.reporting-2018.03.11'; - cleanupIndexAlias = await reportingAPI.coerceReportsIntoExistingIndex( - ARCHIVED_REPORTING_INDEX - ); - - const stats = await usageAPI.getUsageStats(); - expectedCompletedReportCount = await reportingAPI.getCompletedReportCount(stats); - - await esArchiver.unload('reporting/bwc/6_2'); - }); - - after('remove index alias', async () => { - await cleanupIndexAlias(); - }); - - // Might not be great test practice to lump all these jobs together but reporting takes awhile and it'll be - // more efficient to post them all up front, then sequentially. - it('multiple jobs posted', async () => { - const reportPaths = []; - reportPaths.push( - await reportingAPI.postJob(GenerationUrls.CSV_DISCOVER_KUERY_AND_FILTER_6_3) - ); - reportPaths.push( - await reportingAPI.postJob(GenerationUrls.PDF_PRESERVE_DASHBOARD_FILTER_6_3) - ); - reportPaths.push( - await reportingAPI.postJob(GenerationUrls.PDF_PRESERVE_PIE_VISUALIZATION_6_3) - ); - reportPaths.push(await reportingAPI.postJob(GenerationUrls.PDF_PRINT_DASHBOARD_6_3)); - reportPaths.push( - await reportingAPI.postJob( - GenerationUrls.PDF_PRINT_PIE_VISUALIZATION_FILTER_AND_SAVED_SEARCH_6_3 - ) - ); - - await reportingAPI.expectAllJobsToFinishSuccessfully(reportPaths); - }).timeout(1540000); - - it('jobs completed successfully', async () => { - const stats = await usageAPI.getUsageStats(); - expectedCompletedReportCount += 5; - reportingAPI.expectCompletedReportCount(stats, expectedCompletedReportCount); - }); - }); - }); -} diff --git a/x-pack/test/reporting/api/bwc_generation_urls.js b/x-pack/test/reporting/api/bwc_generation_urls.js deleted file mode 100644 index 25b40ff3f74a8..0000000000000 --- a/x-pack/test/reporting/api/bwc_generation_urls.js +++ /dev/null @@ -1,54 +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 * as GenerationUrls from './generation_urls'; - -export default function({ getService }) { - const reportingAPI = getService('reportingAPI'); - const usageAPI = getService('usageAPI'); - - describe('BWC report generation urls', () => { - describe('Pre 6_2', () => { - before(async () => { - await reportingAPI.deleteAllReportingIndexes(); - }); - - // The URL being tested was captured from release 6.4 and then the layout section was removed to test structure before - // preserve_layout was introduced. See https://github.com/elastic/kibana/issues/23414 - it('job posted successfully', async () => { - const path = await reportingAPI.postJob(GenerationUrls.PDF_PRINT_DASHBOARD_PRE_6_2); - await reportingAPI.waitForJobToFinish(path); - const stats = await usageAPI.getUsageStats(); - reportingAPI.expectCompletedReportCount(stats, 1); - }).timeout(500000); - }); - - describe('6_2', () => { - before(async () => { - await reportingAPI.deleteAllReportingIndexes(); - }); - - // Might not be great test practice to lump all these jobs together but reporting takes awhile and it'll be - // more efficient to post them all up front, then sequentially. - it('multiple jobs posted', async () => { - const reportPaths = []; - reportPaths.push(await reportingAPI.postJob(GenerationUrls.PDF_PRINT_DASHBOARD_6_2)); - reportPaths.push(await reportingAPI.postJob(GenerationUrls.PDF_PRESERVE_VISUALIZATION_6_2)); - reportPaths.push(await reportingAPI.postJob(GenerationUrls.CSV_DISCOVER_FILTER_QUERY_6_2)); - - await reportingAPI.expectAllJobsToFinishSuccessfully(reportPaths); - }).timeout(1540000); - - it('jobs completed successfully', async () => { - const stats = await usageAPI.getUsageStats(); - reportingAPI.expectCompletedReportCount(stats, 3); - }); - }); - - // 6.3 urls currently being tested as part of the "bwc_existing_indexes" test suite. Reports are time consuming, - // don't replicate tests if we don't need to, so no specific 6_3 url tests here. - }); -} diff --git a/x-pack/test/reporting/api/chromium_tests.js b/x-pack/test/reporting/api/chromium_tests.js index 6594a80db491b..2d5a31bb40da3 100644 --- a/x-pack/test/reporting/api/chromium_tests.js +++ b/x-pack/test/reporting/api/chromium_tests.js @@ -27,8 +27,6 @@ export default function({ loadTestFile, getService }) { await esArchiver.unload(OSS_DATA_ARCHIVE_PATH); }); - loadTestFile(require.resolve('./bwc_existing_indexes')); - loadTestFile(require.resolve('./bwc_generation_urls')); loadTestFile(require.resolve('./usage')); }); } diff --git a/x-pack/test/reporting/api/generation_urls.js b/x-pack/test/reporting/api/generation_urls.js index 182d395704a25..b98b1a26651a1 100644 --- a/x-pack/test/reporting/api/generation_urls.js +++ b/x-pack/test/reporting/api/generation_urls.js @@ -4,13 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -// These all have the domain name portion stripped out. The api infrastructure assumes it when we post to it anyhow. - -// The URL below was captured from release 6.4 and then the layout section was removed to test structure before -// preserve_layout was introduced. See https://github.com/elastic/kibana/issues/23414 -export const PDF_PRINT_DASHBOARD_PRE_6_2 = - '/api/reporting/generate/printablePdf?jobParams=(browserTimezone:America%2FNew_York,objectType:dashboard,relativeUrls:!(%27%2Fapp%2Fkibana%23%2Fdashboard%2F2ae34a60-3dd4-11e8-b2b9-5d5dc1715159%3F_g%3D(refreshInterval:(pause:!!t,value:0),time:(from:!%27Mon%2BApr%2B09%2B2018%2B17:56:08%2BGMT-0400!%27,mode:absolute,to:!%27Wed%2BApr%2B11%2B2018%2B17:56:08%2BGMT-0400!%27))%26_a%3D(description:!%27!%27,filters:!!(),fullScreenMode:!!f,options:(hidePanelTitles:!!f,useMargins:!!t),panels:!!((embeddableConfig:(),gridData:(h:15,i:!%271!%27,w:24,x:0,y:0),id:!%27145ced90-3dcb-11e8-8660-4d65aa086b3c!%27,panelIndex:!%271!%27,type:visualization,version:!%276.3.0!%27),(embeddableConfig:(),gridData:(h:15,i:!%272!%27,w:24,x:24,y:0),id:e2023110-3dcb-11e8-8660-4d65aa086b3c,panelIndex:!%272!%27,type:visualization,version:!%276.3.0!%27)),query:(language:lucene,query:!%27!%27),timeRestore:!!f,title:!%27couple%2Bpanels!%27,viewMode:view)%27),title:%27couple%20panels%27)'; - export const PDF_PRINT_DASHBOARD_6_3 = '/api/reporting/generate/printablePdf?jobParams=(browserTimezone:America%2FNew_York,layout:(id:print),objectType:dashboard,relativeUrls:!(%27%2Fapp%2Fkibana%23%2Fdashboard%2F2ae34a60-3dd4-11e8-b2b9-5d5dc1715159%3F_g%3D(refreshInterval:(display:Off,pause:!!f,value:0),time:(from:!%27Mon%2BApr%2B09%2B2018%2B17:56:08%2BGMT-0400!%27,mode:absolute,to:!%27Wed%2BApr%2B11%2B2018%2B17:56:08%2BGMT-0400!%27))%26_a%3D(description:!%27!%27,filters:!!(),fullScreenMode:!!f,options:(hidePanelTitles:!!f,useMargins:!!t),panels:!!((embeddableConfig:(),gridData:(h:15,i:!%271!%27,w:24,x:0,y:0),id:!%27145ced90-3dcb-11e8-8660-4d65aa086b3c!%27,panelIndex:!%271!%27,type:visualization,version:!%276.3.0!%27),(embeddableConfig:(),gridData:(h:15,i:!%272!%27,w:24,x:24,y:0),id:e2023110-3dcb-11e8-8660-4d65aa086b3c,panelIndex:!%272!%27,type:visualization,version:!%276.3.0!%27)),query:(language:lucene,query:!%27!%27),timeRestore:!!f,title:!%27couple%2Bpanels!%27,viewMode:view)%27),title:%27couple%20panels%27)'; export const PDF_PRESERVE_DASHBOARD_FILTER_6_3 = @@ -21,10 +14,3 @@ export const PDF_PRINT_PIE_VISUALIZATION_FILTER_AND_SAVED_SEARCH_6_3 = '/api/reporting/generate/printablePdf?jobParams=(browserTimezone:America%2FNew_York,layout:(id:print),objectType:visualization,relativeUrls:!(%27%2Fapp%2Fkibana%23%2Fvisualize%2Fedit%2Fbefdb6b0-3e59-11e8-9fc3-39e49624228e%3F_g%3D(refreshInterval:(display:Off,pause:!!f,value:0),time:(from:!%27Mon%2BApr%2B09%2B2018%2B17:56:08%2BGMT-0400!%27,mode:absolute,to:!%27Wed%2BApr%2B11%2B2018%2B17:56:08%2BGMT-0400!%27))%26_a%3D(filters:!!((!%27$state!%27:(store:appState),meta:(alias:!!n,disabled:!!f,index:a0f483a0-3dc9-11e8-8660-4d65aa086b3c,key:animal.keyword,negate:!!f,params:(query:dog,type:phrase),type:phrase,value:dog),query:(match:(animal.keyword:(query:dog,type:phrase))))),linked:!!t,query:(language:lucene,query:!%27!%27),uiState:(),vis:(aggs:!!((enabled:!!t,id:!%271!%27,params:(),schema:metric,type:count),(enabled:!!t,id:!%272!%27,params:(field:name.keyword,missingBucket:!!f,missingBucketLabel:Missing,order:desc,orderBy:!%271!%27,otherBucket:!!f,otherBucketLabel:Other,size:5),schema:segment,type:terms)),params:(addLegend:!!t,addTooltip:!!t,isDonut:!!t,labels:(last_level:!!t,show:!!f,truncate:100,values:!!t),legendPosition:right,type:pie),title:!%27Filter%2BTest:%2Banimals:%2Blinked%2Bto%2Bsearch%2Bwith%2Bfilter!%27,type:pie))%27),title:%27Filter%20Test:%20animals:%20linked%20to%20search%20with%20filter%27)'; export const CSV_DISCOVER_KUERY_AND_FILTER_6_3 = '/api/reporting/generate/csv?jobParams=(conflictedTypesFields:!(),fields:!(%27@timestamp%27,agent,bytes,clientip),indexPatternId:%270bf35f60-3dc9-11e8-8660-4d65aa086b3c%27,metaFields:!(_source,_id,_type,_index,_score),searchRequest:(body:(_source:(excludes:!(),includes:!(%27@timestamp%27,agent,bytes,clientip)),docvalue_fields:!(%27@timestamp%27),query:(bool:(filter:!((bool:(minimum_should_match:1,should:!((match:(clientip:%2773.14.212.83%27)))))),must:!((range:(bytes:(gte:100,lt:1000))),(range:(%27@timestamp%27:(format:epoch_millis,gte:1369165215770,lte:1526931615770)))),must_not:!(),should:!())),script_fields:(),sort:!((%27@timestamp%27:(order:desc,unmapped_type:boolean))),stored_fields:!(%27@timestamp%27,agent,bytes,clientip),version:!t),index:%27logstash-*%27),title:%27Bytes%20and%20kuery%20in%20saved%20search%20with%20filter%27,type:search)'; - -export const PDF_PRINT_DASHBOARD_6_2 = - '/api/reporting/generate/printablePdf?jobParams=(browserTimezone:America%2FNew_York,layout:(id:print),objectType:dashboard,queryString:%27_g%3D(refreshInterval:(display:Off,pause:!!f,value:0),time:(from:!%27Mon%2BApr%2B09%2B2018%2B17:56:08%2BGMT-0400!%27,mode:absolute,to:!%27Wed%2BApr%2B11%2B2018%2B17:56:08%2BGMT-0400!%27))%26_a%3D(description:!%27!%27,filters:!!((!%27$state!%27:(store:appState),meta:(alias:!!n,disabled:!!f,field:isDog,index:a0f483a0-3dc9-11e8-8660-4d65aa086b3c,key:isDog,negate:!!f,params:(value:!!t),type:phrase,value:true),script:(script:(inline:!%27boolean%2Bcompare(Supplier%2Bs,%2Bdef%2Bv)%2B%257Breturn%2Bs.get()%2B%253D%253D%2Bv%3B%257Dcompare(()%2B-%253E%2B%257B%2Breturn%2Bdoc%255B!!!%27animal.keyword!!!%27%255D.value%2B%253D%253D%2B!!!%27dog!!!%27%2B%257D,%2Bparams.value)%3B!%27,lang:painless,params:(value:!!t))))),fullScreenMode:!!f,options:(hidePanelTitles:!!f,useMargins:!!t),panels:!!((gridData:(h:3,i:!%274!%27,w:6,x:6,y:0),id:edb65990-53ca-11e8-b481-c9426d020fcd,panelIndex:!%274!%27,type:visualization,version:!%276.2.4!%27),(gridData:(h:3,i:!%275!%27,w:6,x:0,y:0),id:!%270644f890-53cb-11e8-b481-c9426d020fcd!%27,panelIndex:!%275!%27,type:visualization,version:!%276.2.4!%27)),query:(language:lucene,query:!%27weightLbs:%253E15!%27),timeRestore:!!t,title:!%27Animal%2BWeights%2B(created%2Bin%2B6.2)!%27,viewMode:view)%27,savedObjectId:%271b2f47b0-53cb-11e8-b481-c9426d020fcd%27)'; -export const PDF_PRESERVE_VISUALIZATION_6_2 = - '/api/reporting/generate/printablePdf?jobParams=(browserTimezone:America%2FNew_York,layout:(dimensions:(height:441,width:1002),id:preserve_layout),objectType:visualization,queryString:%27_g%3D(refreshInterval:(display:Off,pause:!!f,value:0),time:(from:!%27Mon%2BApr%2B09%2B2018%2B17:56:08%2BGMT-0400!%27,mode:absolute,to:!%27Wed%2BApr%2B11%2B2018%2B17:56:08%2BGMT-0400!%27))%26_a%3D(filters:!!(),linked:!!f,query:(language:lucene,query:!%27weightLbs:%253E10!%27),uiState:(),vis:(aggs:!!((enabled:!!t,id:!%271!%27,params:(),schema:metric,type:count),(enabled:!!t,id:!%272!%27,params:(field:weightLbs,missingBucket:!!f,missingBucketLabel:Missing,order:desc,orderBy:!%271!%27,otherBucket:!!f,otherBucketLabel:Other,size:5),schema:segment,type:terms)),params:(addLegend:!!t,addTooltip:!!t,isDonut:!!t,labels:(last_level:!!t,show:!!f,truncate:100,values:!!t),legendPosition:right,type:pie),title:!%27Weight%2Bin%2Blbs%2Bpie%2Bcreated%2Bin%2B6.2!%27,type:pie))%27,savedObjectId:%270644f890-53cb-11e8-b481-c9426d020fcd%27)'; -export const CSV_DISCOVER_FILTER_QUERY_6_2 = - '/api/reporting/generate/csv?jobParams=(conflictedTypesFields:!(),fields:!(%27@timestamp%27,animal,sound,weightLbs),indexPatternId:a0f483a0-3dc9-11e8-8660-4d65aa086b3c,metaFields:!(_source,_id,_type,_index,_score),searchRequest:(body:(_source:(excludes:!(),includes:!(%27@timestamp%27,animal,sound,weightLbs)),docvalue_fields:!(%27@timestamp%27),query:(bool:(filter:!(),must:!((query_string:(analyze_wildcard:!t,default_field:%27*%27,query:%27weightLbs:%3E10%27)),(match_phrase:(sound.keyword:(query:growl))),(range:(%27@timestamp%27:(format:epoch_millis,gte:1523310968000,lte:1523483768000)))),must_not:!(),should:!())),script_fields:(),sort:!((%27@timestamp%27:(order:desc,unmapped_type:boolean))),stored_fields:!(%27@timestamp%27,animal,sound,weightLbs),version:!t),index:%27animals-*%27),title:%27Search%20created%20in%206.2%27,type:search)'; diff --git a/x-pack/test/reporting/configs/chromium_api.js b/x-pack/test/reporting/configs/chromium_api.js index f016738c0e052..95649dfb5d7a3 100644 --- a/x-pack/test/reporting/configs/chromium_api.js +++ b/x-pack/test/reporting/configs/chromium_api.js @@ -28,6 +28,7 @@ export default async function({ readConfigFile }) { '["info","warning","error","fatal","optimize","reporting"]', '--xpack.endpoint.enabled=true', '--xpack.reporting.csv.enablePanelActionDownload=true', + '--xpack.reporting.capture.maxAttempts=1', '--xpack.security.session.idleTimeout=3600000', '--xpack.spaces.enabled=false', ],