diff --git a/Jenkinsfile b/Jenkinsfile index bca451a1918ea..0602f0417c629 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -14,11 +14,11 @@ stage("Kibana Pipeline") { // This stage is just here to help the BlueOcean UI a 'kibana-intake-agent': kibanaPipeline.intakeWorker('kibana-intake', './test/scripts/jenkins_unit.sh'), 'x-pack-intake-agent': kibanaPipeline.intakeWorker('x-pack-intake', './test/scripts/jenkins_xpack.sh'), 'kibana-oss-agent': kibanaPipeline.withWorkers('kibana-oss-tests', { kibanaPipeline.buildOss() }, [ - 'oss-firefoxSmoke': kibanaPipeline.getPostBuildWorker('firefoxSmoke', { - retryable('kibana-firefoxSmoke') { - runbld('./test/scripts/jenkins_firefox_smoke.sh', 'Execute kibana-firefoxSmoke') - } - }), + // 'oss-firefoxSmoke': kibanaPipeline.getPostBuildWorker('firefoxSmoke', { + // retryable('kibana-firefoxSmoke') { + // runbld('./test/scripts/jenkins_firefox_smoke.sh', 'Execute kibana-firefoxSmoke') + // } + // }), 'oss-ciGroup1': kibanaPipeline.getOssCiGroupWorker(1), 'oss-ciGroup2': kibanaPipeline.getOssCiGroupWorker(2), 'oss-ciGroup3': kibanaPipeline.getOssCiGroupWorker(3), @@ -39,11 +39,11 @@ stage("Kibana Pipeline") { // This stage is just here to help the BlueOcean UI a // 'oss-visualRegression': kibanaPipeline.getPostBuildWorker('visualRegression', { runbld('./test/scripts/jenkins_visual_regression.sh', 'Execute kibana-visualRegression') }), ]), 'kibana-xpack-agent': kibanaPipeline.withWorkers('kibana-xpack-tests', { kibanaPipeline.buildXpack() }, [ - 'xpack-firefoxSmoke': kibanaPipeline.getPostBuildWorker('xpack-firefoxSmoke', { - retryable('xpack-firefoxSmoke') { - runbld('./test/scripts/jenkins_xpack_firefox_smoke.sh', 'Execute xpack-firefoxSmoke') - } - }), + // 'xpack-firefoxSmoke': kibanaPipeline.getPostBuildWorker('xpack-firefoxSmoke', { + // retryable('xpack-firefoxSmoke') { + // runbld('./test/scripts/jenkins_xpack_firefox_smoke.sh', 'Execute xpack-firefoxSmoke') + // } + // }), 'xpack-ciGroup1': kibanaPipeline.getXpackCiGroupWorker(1), 'xpack-ciGroup2': kibanaPipeline.getXpackCiGroupWorker(2), 'xpack-ciGroup3': kibanaPipeline.getXpackCiGroupWorker(3), diff --git a/docs/CHANGELOG.asciidoc b/docs/CHANGELOG.asciidoc index 6328b2dca7f06..d6137c9f96339 100644 --- a/docs/CHANGELOG.asciidoc +++ b/docs/CHANGELOG.asciidoc @@ -39,6 +39,12 @@ This section summarizes the changes in each release. See also <>. +[float] +[[known-issue-7.6.0]] +=== Known issue + +* When you use the default `dateFormat:tz: browser` setting, the timestamps appear in UTC instead of the local time of the user browser. To use the local time of the user browser, set `dateFormat:tz:` to the timezone of the user. {issue}57457[#57457] + [float] [[enhancement-7.6.0]] === Enhancements diff --git a/docs/development/core/public/kibana-plugin-public.ihttpfetcherror.md b/docs/development/core/public/kibana-plugin-public.ihttpfetcherror.md index 6109671bb1aa6..49287cc6e261e 100644 --- a/docs/development/core/public/kibana-plugin-public.ihttpfetcherror.md +++ b/docs/development/core/public/kibana-plugin-public.ihttpfetcherror.md @@ -16,6 +16,7 @@ export interface IHttpFetchError extends Error | Property | Type | Description | | --- | --- | --- | | [body](./kibana-plugin-public.ihttpfetcherror.body.md) | any | | +| [name](./kibana-plugin-public.ihttpfetcherror.name.md) | string | | | [req](./kibana-plugin-public.ihttpfetcherror.req.md) | Request | | | [request](./kibana-plugin-public.ihttpfetcherror.request.md) | Request | | | [res](./kibana-plugin-public.ihttpfetcherror.res.md) | Response | | diff --git a/docs/development/core/public/kibana-plugin-public.ihttpfetcherror.name.md b/docs/development/core/public/kibana-plugin-public.ihttpfetcherror.name.md new file mode 100644 index 0000000000000..ba986b75503ed --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.ihttpfetcherror.name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [IHttpFetchError](./kibana-plugin-public.ihttpfetcherror.md) > [name](./kibana-plugin-public.ihttpfetcherror.name.md) + +## IHttpFetchError.name property + +Signature: + +```typescript +readonly name: string; +``` diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 25d762263ce21..ef371bcf24dc2 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -453,7 +453,7 @@ us improve your user experience. Your data is never shared with anyone. Set to `false` to disable telemetry capabilities entirely. You can alternatively opt out through the *Advanced Settings* in {kib}. -`vega.enableExternalUrls:`:: *Default: false* Set this value to true to allow Vega to use any URL to access external data sources and images. If false, Vega can only get data from Elasticsearch. +`vis_type_vega.enableExternalUrls:`:: *Default: false* Set this value to true to allow Vega to use any URL to access external data sources and images. If false, Vega can only get data from Elasticsearch. `xpack.license_management.enabled`:: *Default: true* Set this value to false to disable the License Management user interface. diff --git a/src/core/public/http/fetch.test.ts b/src/core/public/http/fetch.test.ts index a99b7607d7149..efd9fdd053674 100644 --- a/src/core/public/http/fetch.test.ts +++ b/src/core/public/http/fetch.test.ts @@ -37,6 +37,7 @@ describe('Fetch', () => { }); afterEach(() => { fetchMock.restore(); + fetchInstance.removeAllInterceptors(); }); describe('http requests', () => { @@ -287,6 +288,42 @@ describe('Fetch', () => { }); }); + it('preserves the name of the original error', async () => { + expect.assertions(1); + + const abortError = new DOMException('The operation was aborted.', 'AbortError'); + + fetchMock.get('*', Promise.reject(abortError)); + + await fetchInstance.fetch('/my/path').catch(e => { + expect(e.name).toEqual('AbortError'); + }); + }); + + it('exposes the request to the interceptors in case of aborted request', async () => { + const responseErrorSpy = jest.fn(); + const abortError = new DOMException('The operation was aborted.', 'AbortError'); + + fetchMock.get('*', Promise.reject(abortError)); + + fetchInstance.intercept({ + responseError: responseErrorSpy, + }); + + await expect(fetchInstance.fetch('/my/path')).rejects.toThrow(); + + expect(responseErrorSpy).toHaveBeenCalledTimes(1); + const interceptedResponse = responseErrorSpy.mock.calls[0][0]; + + expect(interceptedResponse.request).toEqual( + expect.objectContaining({ + method: 'GET', + url: 'http://localhost/myBase/my/path', + }) + ); + expect(interceptedResponse.error.name).toEqual('AbortError'); + }); + it('should support get() helper', async () => { fetchMock.get('*', {}); await fetchInstance.get('/my/path', { method: 'POST' }); @@ -368,11 +405,6 @@ describe('Fetch', () => { fetchMock.get('*', { foo: 'bar' }); }); - afterEach(() => { - fetchMock.restore(); - fetchInstance.removeAllInterceptors(); - }); - it('should make request and receive response', async () => { fetchInstance.intercept({}); diff --git a/src/core/public/http/fetch.ts b/src/core/public/http/fetch.ts index 1043b50dff958..b433acdb6dbb9 100644 --- a/src/core/public/http/fetch.ts +++ b/src/core/public/http/fetch.ts @@ -146,11 +146,7 @@ export class Fetch { try { response = await window.fetch(request); } catch (err) { - if (err.name === 'AbortError') { - throw err; - } else { - throw new HttpFetchError(err.message, request); - } + throw new HttpFetchError(err.message, err.name ?? 'Error', request); } const contentType = response.headers.get('Content-Type') || ''; @@ -170,11 +166,11 @@ export class Fetch { } } } catch (err) { - throw new HttpFetchError(err.message, request, response, body); + throw new HttpFetchError(err.message, err.name ?? 'Error', request, response, body); } if (!response.ok) { - throw new HttpFetchError(response.statusText, request, response, body); + throw new HttpFetchError(response.statusText, 'Error', request, response, body); } return { fetchOptions, request, response, body }; diff --git a/src/core/public/http/http_fetch_error.ts b/src/core/public/http/http_fetch_error.ts index 2156df5798974..74aed4049613e 100644 --- a/src/core/public/http/http_fetch_error.ts +++ b/src/core/public/http/http_fetch_error.ts @@ -21,16 +21,19 @@ import { IHttpFetchError } from './types'; /** @internal */ export class HttpFetchError extends Error implements IHttpFetchError { + public readonly name: string; public readonly req: Request; public readonly res?: Response; constructor( message: string, + name: string, public readonly request: Request, public readonly response?: Response, public readonly body?: any ) { super(message); + this.name = name; this.req = request; this.res = response; diff --git a/src/core/public/http/types.ts b/src/core/public/http/types.ts index c38b9da442943..5909572c7e545 100644 --- a/src/core/public/http/types.ts +++ b/src/core/public/http/types.ts @@ -291,6 +291,7 @@ export interface IHttpResponseInterceptorOverrides { /** @public */ export interface IHttpFetchError extends Error { + readonly name: string; readonly request: Request; readonly response?: Response; /** diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 6046bd274e82f..5ae5e1ee5195a 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -733,6 +733,8 @@ export type IContextProvider, TContextName export interface IHttpFetchError extends Error { // (undocumented) readonly body?: any; + // (undocumented) + readonly name: string; // @deprecated (undocumented) readonly req: Request; // (undocumented) diff --git a/src/core/server/legacy/plugins/__snapshots__/get_nav_links.test.ts.snap b/src/core/server/legacy/plugins/__snapshots__/get_nav_links.test.ts.snap new file mode 100644 index 0000000000000..c1b7164908ed6 --- /dev/null +++ b/src/core/server/legacy/plugins/__snapshots__/get_nav_links.test.ts.snap @@ -0,0 +1,56 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` 1`] = ` +Array [ + Object { + "category": undefined, + "disableSubUrlTracking": undefined, + "disabled": false, + "euiIconType": undefined, + "hidden": false, + "icon": undefined, + "id": "link-a", + "linkToLastSubUrl": true, + "order": 0, + "subUrlBase": "/some-custom-url", + "title": "AppA", + "tooltip": "", + "url": "/some-custom-url", + }, + Object { + "category": undefined, + "disableSubUrlTracking": true, + "disabled": false, + "euiIconType": undefined, + "hidden": false, + "icon": undefined, + "id": "link-b", + "linkToLastSubUrl": true, + "order": 0, + "subUrlBase": "/url-b", + "title": "AppB", + "tooltip": "", + "url": "/url-b", + }, + Object { + "category": undefined, + "euiIconType": undefined, + "icon": undefined, + "id": "app-a", + "linkToLastSubUrl": true, + "order": 0, + "title": "AppA", + "url": "/app/app-a", + }, + Object { + "category": undefined, + "euiIconType": undefined, + "icon": undefined, + "id": "app-b", + "linkToLastSubUrl": true, + "order": 0, + "title": "AppB", + "url": "/app/app-b", + }, +] +`; diff --git a/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts b/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts index 1c6ab91a39279..44f02f0c90d4e 100644 --- a/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts +++ b/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts @@ -30,72 +30,8 @@ import { collectUiExports as collectLegacyUiExports } from '../../../../legacy/u import { LoggerFactory } from '../../logging'; import { PackageInfo } from '../../config'; - -import { - LegacyUiExports, - LegacyNavLink, - LegacyPluginSpec, - LegacyPluginPack, - LegacyConfig, -} from '../types'; - -const REMOVE_FROM_ARRAY: LegacyNavLink[] = []; - -function getUiAppsNavLinks({ uiAppSpecs = [] }: LegacyUiExports, pluginSpecs: LegacyPluginSpec[]) { - return uiAppSpecs.flatMap(spec => { - if (!spec) { - return REMOVE_FROM_ARRAY; - } - - const id = spec.pluginId || spec.id; - - if (!id) { - throw new Error('Every app must specify an id'); - } - - if (spec.pluginId && !pluginSpecs.some(plugin => plugin.getId() === spec.pluginId)) { - throw new Error(`Unknown plugin id "${spec.pluginId}"`); - } - - const listed = typeof spec.listed === 'boolean' ? spec.listed : true; - - if (spec.hidden || !listed) { - return REMOVE_FROM_ARRAY; - } - - return { - id, - category: spec.category, - title: spec.title, - order: typeof spec.order === 'number' ? spec.order : 0, - icon: spec.icon, - euiIconType: spec.euiIconType, - url: spec.url || `/app/${id}`, - linkToLastSubUrl: spec.linkToLastSubUrl, - }; - }); -} - -function getNavLinks(uiExports: LegacyUiExports, pluginSpecs: LegacyPluginSpec[]) { - return (uiExports.navLinkSpecs || []) - .map(spec => ({ - id: spec.id, - category: spec.category, - title: spec.title, - order: typeof spec.order === 'number' ? spec.order : 0, - url: spec.url, - subUrlBase: spec.subUrlBase || spec.url, - disableSubUrlTracking: spec.disableSubUrlTracking, - icon: spec.icon, - euiIconType: spec.euiIconType, - linkToLastSub: 'linkToLastSubUrl' in spec ? spec.linkToLastSubUrl : false, - hidden: 'hidden' in spec ? spec.hidden : false, - disabled: 'disabled' in spec ? spec.disabled : false, - tooltip: spec.tooltip || '', - })) - .concat(getUiAppsNavLinks(uiExports, pluginSpecs)) - .sort((a, b) => a.order - b.order); -} +import { LegacyPluginSpec, LegacyPluginPack, LegacyConfig } from '../types'; +import { getNavLinks } from './get_nav_links'; export async function findLegacyPluginSpecs( settings: unknown, diff --git a/src/core/server/legacy/plugins/get_nav_links.test.ts b/src/core/server/legacy/plugins/get_nav_links.test.ts new file mode 100644 index 0000000000000..dcb19020f769e --- /dev/null +++ b/src/core/server/legacy/plugins/get_nav_links.test.ts @@ -0,0 +1,283 @@ +/* + * 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 { LegacyUiExports, LegacyPluginSpec, LegacyAppSpec, LegacyNavLinkSpec } from '../types'; +import { getNavLinks } from './get_nav_links'; + +const createLegacyExports = ({ + uiAppSpecs = [], + navLinkSpecs = [], +}: { + uiAppSpecs?: LegacyAppSpec[]; + navLinkSpecs?: LegacyNavLinkSpec[]; +}): LegacyUiExports => ({ + uiAppSpecs, + navLinkSpecs, + injectedVarsReplacers: [], + defaultInjectedVarProviders: [], + savedObjectMappings: [], + savedObjectSchemas: {}, + savedObjectMigrations: {}, + savedObjectValidations: {}, +}); + +const createPluginSpecs = (...ids: string[]): LegacyPluginSpec[] => + ids.map( + id => + ({ + getId: () => id, + } as LegacyPluginSpec) + ); + +describe('getNavLinks', () => { + describe('generating from uiAppSpecs', () => { + it('generates navlinks from legacy app specs', () => { + const navlinks = getNavLinks( + createLegacyExports({ + uiAppSpecs: [ + { + id: 'app-a', + title: 'AppA', + pluginId: 'pluginA', + }, + { + id: 'app-b', + title: 'AppB', + pluginId: 'pluginA', + }, + ], + }), + createPluginSpecs('pluginA') + ); + + expect(navlinks.length).toEqual(2); + expect(navlinks[0]).toEqual( + expect.objectContaining({ + id: 'app-a', + title: 'AppA', + url: '/app/app-a', + }) + ); + expect(navlinks[1]).toEqual( + expect.objectContaining({ + id: 'app-b', + title: 'AppB', + url: '/app/app-b', + }) + ); + }); + + it('uses the app id to generates the navlink id even if pluginId is specified', () => { + const navlinks = getNavLinks( + createLegacyExports({ + uiAppSpecs: [ + { + id: 'app-a', + title: 'AppA', + pluginId: 'pluginA', + }, + { + id: 'app-b', + title: 'AppB', + pluginId: 'pluginA', + }, + ], + }), + createPluginSpecs('pluginA') + ); + + expect(navlinks.length).toEqual(2); + expect(navlinks[0].id).toEqual('app-a'); + expect(navlinks[1].id).toEqual('app-b'); + }); + + it('throws if an app reference a missing plugin', () => { + expect(() => { + getNavLinks( + createLegacyExports({ + uiAppSpecs: [ + { + id: 'app-a', + title: 'AppA', + pluginId: 'notExistingPlugin', + }, + ], + }), + createPluginSpecs('pluginA') + ); + }).toThrowErrorMatchingInlineSnapshot(`"Unknown plugin id \\"notExistingPlugin\\""`); + }); + + it('uses all known properties of the navlink', () => { + const navlinks = getNavLinks( + createLegacyExports({ + uiAppSpecs: [ + { + id: 'app-a', + title: 'AppA', + category: { + label: 'My Category', + }, + order: 42, + url: '/some-custom-url', + icon: 'fa-snowflake', + euiIconType: 'euiIcon', + linkToLastSubUrl: true, + hidden: false, + }, + ], + }), + [] + ); + expect(navlinks.length).toBe(1); + expect(navlinks[0]).toEqual({ + id: 'app-a', + title: 'AppA', + category: { + label: 'My Category', + }, + order: 42, + url: '/some-custom-url', + icon: 'fa-snowflake', + euiIconType: 'euiIcon', + linkToLastSubUrl: true, + }); + }); + }); + + describe('generating from navLinkSpecs', () => { + it('generates navlinks from legacy navLink specs', () => { + const navlinks = getNavLinks( + createLegacyExports({ + navLinkSpecs: [ + { + id: 'link-a', + title: 'AppA', + url: '/some-custom-url', + }, + { + id: 'link-b', + title: 'AppB', + url: '/some-other-url', + disableSubUrlTracking: true, + }, + ], + }), + createPluginSpecs('pluginA') + ); + + expect(navlinks.length).toEqual(2); + expect(navlinks[0]).toEqual( + expect.objectContaining({ + id: 'link-a', + title: 'AppA', + url: '/some-custom-url', + hidden: false, + disabled: false, + }) + ); + expect(navlinks[1]).toEqual( + expect.objectContaining({ + id: 'link-b', + title: 'AppB', + url: '/some-other-url', + disableSubUrlTracking: true, + }) + ); + }); + + it('only uses known properties to create the navlink', () => { + const navlinks = getNavLinks( + createLegacyExports({ + navLinkSpecs: [ + { + id: 'link-a', + title: 'AppA', + category: { + label: 'My Second Cat', + }, + order: 72, + url: '/some-other-custom', + subUrlBase: '/some-other-custom/sub', + disableSubUrlTracking: true, + icon: 'fa-corn', + euiIconType: 'euiIconBis', + linkToLastSubUrl: false, + hidden: false, + tooltip: 'My other tooltip', + }, + ], + }), + [] + ); + expect(navlinks.length).toBe(1); + expect(navlinks[0]).toEqual({ + id: 'link-a', + title: 'AppA', + category: { + label: 'My Second Cat', + }, + order: 72, + url: '/some-other-custom', + subUrlBase: '/some-other-custom/sub', + disableSubUrlTracking: true, + icon: 'fa-corn', + euiIconType: 'euiIconBis', + linkToLastSubUrl: false, + hidden: false, + disabled: false, + tooltip: 'My other tooltip', + }); + }); + }); + + describe('generating from both apps and navlinks', () => { + const navlinks = getNavLinks( + createLegacyExports({ + uiAppSpecs: [ + { + id: 'app-a', + title: 'AppA', + }, + { + id: 'app-b', + title: 'AppB', + }, + ], + navLinkSpecs: [ + { + id: 'link-a', + title: 'AppA', + url: '/some-custom-url', + }, + { + id: 'link-b', + title: 'AppB', + url: '/url-b', + disableSubUrlTracking: true, + }, + ], + }), + [] + ); + + expect(navlinks.length).toBe(4); + expect(navlinks).toMatchSnapshot(); + }); +}); diff --git a/src/core/server/legacy/plugins/get_nav_links.ts b/src/core/server/legacy/plugins/get_nav_links.ts new file mode 100644 index 0000000000000..067fb204ca7f3 --- /dev/null +++ b/src/core/server/legacy/plugins/get_nav_links.ts @@ -0,0 +1,82 @@ +/* + * 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 { + LegacyUiExports, + LegacyNavLink, + LegacyPluginSpec, + LegacyNavLinkSpec, + LegacyAppSpec, +} from '../types'; + +function legacyAppToNavLink(spec: LegacyAppSpec): LegacyNavLink { + if (!spec.id) { + throw new Error('Every app must specify an id'); + } + return { + id: spec.id, + category: spec.category, + title: spec.title ?? spec.id, + order: typeof spec.order === 'number' ? spec.order : 0, + icon: spec.icon, + euiIconType: spec.euiIconType, + url: spec.url || `/app/${spec.id}`, + linkToLastSubUrl: spec.linkToLastSubUrl ?? true, + }; +} + +function legacyLinkToNavLink(spec: LegacyNavLinkSpec): LegacyNavLink { + return { + id: spec.id, + category: spec.category, + title: spec.title, + order: typeof spec.order === 'number' ? spec.order : 0, + url: spec.url, + subUrlBase: spec.subUrlBase || spec.url, + disableSubUrlTracking: spec.disableSubUrlTracking, + icon: spec.icon, + euiIconType: spec.euiIconType, + linkToLastSubUrl: spec.linkToLastSubUrl ?? true, + hidden: spec.hidden ?? false, + disabled: spec.disabled ?? false, + tooltip: spec.tooltip ?? '', + }; +} + +function isHidden(app: LegacyAppSpec) { + return app.listed === false || app.hidden === true; +} + +export function getNavLinks(uiExports: LegacyUiExports, pluginSpecs: LegacyPluginSpec[]) { + const navLinkSpecs = uiExports.navLinkSpecs || []; + const appSpecs = (uiExports.uiAppSpecs || []).filter( + app => app !== undefined && !isHidden(app) + ) as LegacyAppSpec[]; + + const pluginIds = (pluginSpecs || []).map(spec => spec.getId()); + appSpecs.forEach(spec => { + if (spec.pluginId && !pluginIds.includes(spec.pluginId)) { + throw new Error(`Unknown plugin id "${spec.pluginId}"`); + } + }); + + return [...navLinkSpecs.map(legacyLinkToNavLink), ...appSpecs.map(legacyAppToNavLink)].sort( + (a, b) => a.order - b.order + ); +} diff --git a/src/core/server/legacy/types.ts b/src/core/server/legacy/types.ts index d51058ca561c6..0c1a7730f92a7 100644 --- a/src/core/server/legacy/types.ts +++ b/src/core/server/legacy/types.ts @@ -131,16 +131,20 @@ export type VarsReplacer = ( * @internal * @deprecated */ -export type LegacyNavLinkSpec = Record & ChromeNavLink; +export type LegacyNavLinkSpec = Partial & { + id: string; + title: string; + url: string; +}; /** * @internal * @deprecated */ -export type LegacyAppSpec = Pick< - ChromeNavLink, - 'title' | 'order' | 'icon' | 'euiIconType' | 'url' | 'linkToLastSubUrl' | 'hidden' | 'category' -> & { pluginId?: string; id?: string; listed?: boolean }; +export type LegacyAppSpec = Partial & { + pluginId?: string; + listed?: boolean; +}; /** * @internal diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index cf8fafd7ea7c4..1ec167613e766 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -2125,11 +2125,11 @@ export const validBodyOutput: readonly ["data", "stream"]; // Warnings were encountered during analysis: // // src/core/server/http/router/response.ts:316:3 - (ae-forgotten-export) The symbol "KibanaResponse" needs to be exported by the entry point index.d.ts -// src/core/server/legacy/types.ts:158:3 - (ae-forgotten-export) The symbol "VarsProvider" needs to be exported by the entry point index.d.ts -// src/core/server/legacy/types.ts:159:3 - (ae-forgotten-export) The symbol "VarsReplacer" needs to be exported by the entry point index.d.ts -// src/core/server/legacy/types.ts:160:3 - (ae-forgotten-export) The symbol "LegacyNavLinkSpec" needs to be exported by the entry point index.d.ts -// src/core/server/legacy/types.ts:161:3 - (ae-forgotten-export) The symbol "LegacyAppSpec" needs to be exported by the entry point index.d.ts -// src/core/server/legacy/types.ts:162:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" needs to be exported by the entry point index.d.ts +// src/core/server/legacy/types.ts:162:3 - (ae-forgotten-export) The symbol "VarsProvider" needs to be exported by the entry point index.d.ts +// src/core/server/legacy/types.ts:163:3 - (ae-forgotten-export) The symbol "VarsReplacer" needs to be exported by the entry point index.d.ts +// src/core/server/legacy/types.ts:164:3 - (ae-forgotten-export) The symbol "LegacyNavLinkSpec" needs to be exported by the entry point index.d.ts +// src/core/server/legacy/types.ts:165:3 - (ae-forgotten-export) The symbol "LegacyAppSpec" needs to be exported by the entry point index.d.ts +// src/core/server/legacy/types.ts:166:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" needs to be exported by the entry point index.d.ts // src/core/server/plugins/plugins_service.ts:43:5 - (ae-forgotten-export) The symbol "InternalPluginInfo" needs to be exported by the entry point index.d.ts // src/core/server/plugins/types.ts:226:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts // src/core/server/plugins/types.ts:226:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index 395e0da218307..ea81193c1dd0a 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -25,18 +25,17 @@ import { migrations } from './migrations'; import { importApi } from './server/routes/api/import'; import { exportApi } from './server/routes/api/export'; import { managementApi } from './server/routes/api/management'; -import * as systemApi from './server/lib/system_api'; import mappings from './mappings.json'; import { getUiSettingDefaults } from './ui_setting_defaults'; import { registerCspCollector } from './server/lib/csp_usage_collector'; import { injectVars } from './inject_vars'; import { i18n } from '@kbn/i18n'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils'; +import { kbnBaseUrl } from '../../../plugins/kibana_legacy/server'; const mkdirAsync = promisify(Fs.mkdir); export default function(kibana) { - const kbnBaseUrl = '/app/kibana'; return new kibana.Plugin({ id: 'kibana', config: function(Joi) { @@ -323,7 +322,6 @@ export default function(kibana) { exportApi(server); managementApi(server); registerCspCollector(usageCollection, server); - server.expose('systemApi', systemApi); server.injectUiAppVars('kibana', () => injectVars(server)); }, }); diff --git a/src/legacy/core_plugins/kibana/public/dashboard/index.ts b/src/legacy/core_plugins/kibana/public/dashboard/index.ts index d0157882689d3..5b9fb8c0b6360 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/index.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/index.ts @@ -25,5 +25,5 @@ export { createSavedDashboardLoader } from './saved_dashboard/saved_dashboards'; // Core will be looking for this when loading our plugin in the new platform export const plugin = (context: PluginInitializerContext) => { - return new DashboardPlugin(); + return new DashboardPlugin(context); }; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/legacy.ts b/src/legacy/core_plugins/kibana/public/dashboard/legacy.ts index 9c13337a71126..cedb6fbc9b5ef 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/legacy.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/legacy.ts @@ -19,18 +19,12 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from './legacy_imports'; -import { start as data } from '../../../data/public/legacy'; -import { start as embeddables } from '../../../embeddable_api/public/np_ready/public/legacy'; import { plugin } from './index'; (async () => { - const instance = plugin({} as PluginInitializerContext); + const instance = plugin({ + env: npSetup.plugins.kibanaLegacy.env, + } as PluginInitializerContext); instance.setup(npSetup.core, npSetup.plugins); - instance.start(npStart.core, { - ...npStart.plugins, - data, - npData: npStart.plugins.data, - embeddables, - navigation: npStart.plugins.navigation, - }); + instance.start(npStart.core, npStart.plugins); })(); diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts index e608eb7b7f48c..cc104c1a931d0 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts @@ -24,8 +24,9 @@ import { AppMountContext, ChromeStart, IUiSettingsClient, - LegacyCoreStart, + CoreStart, SavedObjectsClientContract, + PluginInitializerContext, } from 'kibana/public'; import { Storage } from '../../../../../../plugins/kibana_utils/public'; import { @@ -43,13 +44,14 @@ import { import { initDashboardApp } from './legacy_app'; import { IEmbeddableStart } from '../../../../../../plugins/embeddable/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../../../plugins/navigation/public'; -import { DataPublicPluginStart as NpDataStart } from '../../../../../../plugins/data/public'; +import { DataPublicPluginStart } from '../../../../../../plugins/data/public'; import { SharePluginStart } from '../../../../../../plugins/share/public'; import { KibanaLegacyStart } from '../../../../../../plugins/kibana_legacy/public'; export interface RenderDeps { - core: LegacyCoreStart; - npDataStart: NpDataStart; + pluginInitializerContext: PluginInitializerContext; + core: CoreStart; + data: DataPublicPluginStart; navigation: NavigationStart; savedObjectsClient: SavedObjectsClientContract; savedDashboards: SavedObjectLoader; @@ -58,8 +60,8 @@ export interface RenderDeps { uiSettings: IUiSettingsClient; chrome: ChromeStart; addBasePath: (path: string) => string; - savedQueryService: NpDataStart['query']['savedQueries']; - embeddables: IEmbeddableStart; + savedQueryService: DataPublicPluginStart['query']['savedQueries']; + embeddable: IEmbeddableStart; localStorage: Storage; share: SharePluginStart; config: KibanaLegacyStart['config']; @@ -71,7 +73,11 @@ export const renderApp = (element: HTMLElement, appBasePath: string, deps: Rende if (!angularModuleInstance) { angularModuleInstance = createLocalAngularModule(deps.core, deps.navigation); // global routing stuff - configureAppAngularModule(angularModuleInstance, deps.core as LegacyCoreStart, true); + configureAppAngularModule( + angularModuleInstance, + { core: deps.core, env: deps.pluginInitializerContext.env }, + true + ); initDashboardApp(angularModuleInstance, deps); } diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx index f94acf2dc1991..c0a0693431295 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx @@ -103,7 +103,7 @@ export function initDashboardAppDirective(app: any, deps: RenderDeps) { $route, $scope, $routeParams, - indexPatterns: deps.npDataStart.indexPatterns, + indexPatterns: deps.data.indexPatterns, kbnUrlStateStorage, history, ...deps, diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx index 3f9343ededd13..465203be0d34c 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx @@ -96,6 +96,7 @@ export class DashboardAppController { }; constructor({ + pluginInitializerContext, $scope, $route, $routeParams, @@ -103,10 +104,10 @@ export class DashboardAppController { localStorage, indexPatterns, savedQueryService, - embeddables, + embeddable, share, dashboardCapabilities, - npDataStart: { query: queryService }, + data: { query: queryService }, core: { notifications, overlays, @@ -141,7 +142,7 @@ export class DashboardAppController { const dashboardStateManager = new DashboardStateManager({ savedDashboard: dash, hideWriteControls: dashboardConfig.getHideWriteControls(), - kibanaVersion: injectedMetadata.getKibanaVersion(), + kibanaVersion: pluginInitializerContext.env.packageInfo.version, kbnUrlStateStorage, history, }); @@ -186,9 +187,9 @@ export class DashboardAppController { let panelIndexPatterns: IndexPattern[] = []; Object.values(container.getChildIds()).forEach(id => { - const embeddable = container.getChild(id); - if (isErrorEmbeddable(embeddable)) return; - const embeddableIndexPatterns = (embeddable.getOutput() as any).indexPatterns; + const embeddableInstance = container.getChild(id); + if (isErrorEmbeddable(embeddableInstance)) return; + const embeddableIndexPatterns = (embeddableInstance.getOutput() as any).indexPatterns; if (!embeddableIndexPatterns) return; panelIndexPatterns.push(...embeddableIndexPatterns); }); @@ -284,7 +285,7 @@ export class DashboardAppController { let outputSubscription: Subscription | undefined; const dashboardDom = document.getElementById('dashboardViewport'); - const dashboardFactory = embeddables.getEmbeddableFactory( + const dashboardFactory = embeddable.getEmbeddableFactory( DASHBOARD_CONTAINER_TYPE ) as DashboardContainerFactory; dashboardFactory @@ -818,8 +819,8 @@ export class DashboardAppController { if (dashboardContainer && !isErrorEmbeddable(dashboardContainer)) { openAddPanelFlyout({ embeddable: dashboardContainer, - getAllFactories: embeddables.getEmbeddableFactories, - getFactory: embeddables.getEmbeddableFactory, + getAllFactories: embeddable.getEmbeddableFactories, + getFactory: embeddable.getEmbeddableFactory, notifications, overlays, SavedObjectFinder: getSavedObjectFinder(savedObjects, uiSettings), @@ -829,7 +830,7 @@ export class DashboardAppController { navActions[TopNavIds.VISUALIZE] = async () => { const type = 'visualization'; - const factory = embeddables.getEmbeddableFactory(type); + const factory = embeddable.getEmbeddableFactory(type); if (!factory) { throw new EmbeddableFactoryNotFoundError(type); } diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js index b0f70b7a0c68f..ce9cc85be57b2 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js @@ -99,7 +99,7 @@ export function initDashboardApp(app, deps) { // syncs `_g` portion of url with query services const { stop: stopSyncingGlobalStateWithUrl } = syncQuery( - deps.npDataStart.query, + deps.data.query, kbnUrlStateStorage ); @@ -137,36 +137,31 @@ export function initDashboardApp(app, deps) { }, resolve: { dash: function($rootScope, $route, redirectWhenMissing, kbnUrl, history) { - return ensureDefaultIndexPattern(deps.core, deps.npDataStart, $rootScope, kbnUrl).then( - () => { - const savedObjectsClient = deps.savedObjectsClient; - const title = $route.current.params.title; - if (title) { - return savedObjectsClient - .find({ - search: `"${title}"`, - search_fields: 'title', - type: 'dashboard', - }) - .then(results => { - // The search isn't an exact match, lets see if we can find a single exact match to use - const matchingDashboards = results.savedObjects.filter( - dashboard => - dashboard.attributes.title.toLowerCase() === title.toLowerCase() - ); - if (matchingDashboards.length === 1) { - history.replace(createDashboardEditUrl(matchingDashboards[0].id)); - } else { - history.replace( - `${DashboardConstants.LANDING_PAGE_PATH}?filter="${title}"` - ); - $route.reload(); - } - return new Promise(() => {}); - }); - } + return ensureDefaultIndexPattern(deps.core, deps.data, $rootScope, kbnUrl).then(() => { + const savedObjectsClient = deps.savedObjectsClient; + const title = $route.current.params.title; + if (title) { + return savedObjectsClient + .find({ + search: `"${title}"`, + search_fields: 'title', + type: 'dashboard', + }) + .then(results => { + // The search isn't an exact match, lets see if we can find a single exact match to use + const matchingDashboards = results.savedObjects.filter( + dashboard => dashboard.attributes.title.toLowerCase() === title.toLowerCase() + ); + if (matchingDashboards.length === 1) { + history.replace(createDashboardEditUrl(matchingDashboards[0].id)); + } else { + history.replace(`${DashboardConstants.LANDING_PAGE_PATH}?filter="${title}"`); + $route.reload(); + } + return new Promise(() => {}); + }); } - ); + }); }, }, }) @@ -177,7 +172,7 @@ export function initDashboardApp(app, deps) { requireUICapability: 'dashboard.createNew', resolve: { dash: function(redirectWhenMissing, $rootScope, kbnUrl) { - return ensureDefaultIndexPattern(deps.core, deps.npDataStart, $rootScope, kbnUrl) + return ensureDefaultIndexPattern(deps.core, deps.data, $rootScope, kbnUrl) .then(() => { return deps.savedDashboards.get(); }) @@ -197,7 +192,7 @@ export function initDashboardApp(app, deps) { dash: function($rootScope, $route, redirectWhenMissing, kbnUrl, history) { const id = $route.current.params.id; - return ensureDefaultIndexPattern(deps.core, deps.npDataStart, $rootScope, kbnUrl) + return ensureDefaultIndexPattern(deps.core, deps.data, $rootScope, kbnUrl) .then(() => { return deps.savedDashboards.get(id); }) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts b/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts index 2ee6bd85344e5..efc8e8037e19a 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts @@ -20,19 +20,16 @@ import { BehaviorSubject } from 'rxjs'; import { App, + AppMountParameters, CoreSetup, CoreStart, - LegacyCoreStart, Plugin, + PluginInitializerContext, SavedObjectsClientContract, } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { RenderDeps } from './np_ready/application'; -import { DataStart } from '../../../data/public'; -import { - DataPublicPluginStart as NpDataStart, - DataPublicPluginSetup as NpDataSetup, -} from '../../../../../plugins/data/public'; +import { DataPublicPluginStart, DataPublicPluginSetup } from '../../../../../plugins/data/public'; import { IEmbeddableStart } from '../../../../../plugins/embeddable/public'; import { Storage } from '../../../../../plugins/kibana_utils/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public'; @@ -52,9 +49,8 @@ import { createKbnUrlTracker } from '../../../../../plugins/kibana_utils/public' import { getQueryStateContainer } from '../../../../../plugins/data/public'; export interface DashboardPluginStartDependencies { - data: DataStart; - npData: NpDataStart; - embeddables: IEmbeddableStart; + data: DataPublicPluginStart; + embeddable: IEmbeddableStart; navigation: NavigationStart; share: SharePluginStart; kibanaLegacy: KibanaLegacyStart; @@ -63,14 +59,14 @@ export interface DashboardPluginStartDependencies { export interface DashboardPluginSetupDependencies { home: HomePublicPluginSetup; kibanaLegacy: KibanaLegacySetup; - data: NpDataSetup; + data: DataPublicPluginSetup; } export class DashboardPlugin implements Plugin { private startDependencies: { - npDataStart: NpDataStart; + data: DataPublicPluginStart; savedObjectsClient: SavedObjectsClientContract; - embeddables: IEmbeddableStart; + embeddable: IEmbeddableStart; navigation: NavigationStart; share: SharePluginStart; dashboardConfig: KibanaLegacyStart['dashboardConfig']; @@ -79,12 +75,11 @@ export class DashboardPlugin implements Plugin { private appStateUpdater = new BehaviorSubject(() => ({})); private stopUrlTracking: (() => void) | undefined = undefined; - public setup( - core: CoreSetup, - { home, kibanaLegacy, data: npData }: DashboardPluginSetupDependencies - ) { + constructor(private initializerContext: PluginInitializerContext) {} + + public setup(core: CoreSetup, { home, kibanaLegacy, data }: DashboardPluginSetupDependencies) { const { querySyncStateContainer, stop: stopQuerySyncStateContainer } = getQueryStateContainer( - npData.query + data.query ); const { appMounted, appUnMounted, stop: stopUrlTracker } = createKbnUrlTracker({ baseUrl: core.http.basePath.prepend('/app/kibana'), @@ -106,41 +101,43 @@ export class DashboardPlugin implements Plugin { const app: App = { id: '', title: 'Dashboards', - mount: async ({ core: contextCore }, params) => { + mount: async (params: AppMountParameters) => { + const [coreStart] = await core.getStartServices(); if (this.startDependencies === null) { throw new Error('not started yet'); } appMounted(); const { savedObjectsClient, - embeddables, + embeddable, navigation, share, - npDataStart, + data: dataStart, dashboardConfig, } = this.startDependencies; const savedDashboards = createSavedDashboardLoader({ savedObjectsClient, - indexPatterns: npDataStart.indexPatterns, - chrome: contextCore.chrome, - overlays: contextCore.overlays, + indexPatterns: dataStart.indexPatterns, + chrome: coreStart.chrome, + overlays: coreStart.overlays, }); const deps: RenderDeps = { - core: contextCore as LegacyCoreStart, + pluginInitializerContext: this.initializerContext, + core: coreStart, dashboardConfig, navigation, share, - npDataStart, + data: dataStart, savedObjectsClient, savedDashboards, - chrome: contextCore.chrome, - addBasePath: contextCore.http.basePath.prepend, - uiSettings: contextCore.uiSettings, + chrome: coreStart.chrome, + addBasePath: coreStart.http.basePath.prepend, + uiSettings: coreStart.uiSettings, config: kibanaLegacy.config, - savedQueryService: npDataStart.query.savedQueries, - embeddables, - dashboardCapabilities: contextCore.application.capabilities.dashboard, + savedQueryService: dataStart.query.savedQueries, + embeddable, + dashboardCapabilities: coreStart.application.capabilities.dashboard, localStorage: new Storage(localStorage), }; const { renderApp } = await import('./np_ready/application'); @@ -178,18 +175,17 @@ export class DashboardPlugin implements Plugin { start( { savedObjects: { client: savedObjectsClient } }: CoreStart, { - data: dataStart, - embeddables, + embeddable, navigation, - npData, + data, share, kibanaLegacy: { dashboardConfig }, }: DashboardPluginStartDependencies ) { this.startDependencies = { - npDataStart: npData, + data, savedObjectsClient, - embeddables, + embeddable, navigation, share, dashboardConfig, diff --git a/src/legacy/core_plugins/kibana/public/home/index.ts b/src/legacy/core_plugins/kibana/public/home/index.ts index 768e1a96de935..74b6da33c6542 100644 --- a/src/legacy/core_plugins/kibana/public/home/index.ts +++ b/src/legacy/core_plugins/kibana/public/home/index.ts @@ -17,17 +17,13 @@ * under the License. */ +import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; import { HomePlugin } from './plugin'; -(async () => { - const instance = new HomePlugin(); - instance.setup(npSetup.core, { - ...npSetup.plugins, - __LEGACY: { - metadata: npStart.core.injectedMetadata.getLegacyMetadata(), - }, - }); +const instance = new HomePlugin({ + env: npSetup.plugins.kibanaLegacy.env, +} as PluginInitializerContext); +instance.setup(npSetup.core, npSetup.plugins); - instance.start(npStart.core, npStart.plugins); -})(); +instance.start(npStart.core, npStart.plugins); diff --git a/src/legacy/core_plugins/kibana/public/home/kibana_services.ts b/src/legacy/core_plugins/kibana/public/home/kibana_services.ts index 57696d874cc40..6cb1531be6b5b 100644 --- a/src/legacy/core_plugins/kibana/public/home/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/home/kibana_services.ts @@ -21,12 +21,10 @@ import { ChromeStart, DocLinksStart, HttpStart, - LegacyNavLink, NotificationsSetup, OverlayStart, SavedObjectsClientContract, IUiSettingsClient, - UiSettingsState, } from 'kibana/public'; import { UiStatsMetricType } from '@kbn/analytics'; import { TelemetryPluginStart } from '../../../../../plugins/telemetry/public'; @@ -39,19 +37,7 @@ import { KibanaLegacySetup } from '../../../../../plugins/kibana_legacy/public'; export interface HomeKibanaServices { indexPatternService: any; - metadata: { - app: unknown; - bundleId: string; - nav: LegacyNavLink[]; - version: string; - branch: string; - buildNum: number; - buildSha: string; - basePath: string; - serverName: string; - devMode: boolean; - uiSettings: { defaults: UiSettingsState; user?: UiSettingsState | undefined }; - }; + kibanaVersion: string; getInjected: (name: string, defaultValue?: any) => unknown; chrome: ChromeStart; uiSettings: IUiSettingsClient; diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/__snapshots__/sample_data_view_data_button.test.js.snap b/src/legacy/core_plugins/kibana/public/home/np_ready/components/__snapshots__/sample_data_view_data_button.test.js.snap index e08d802406fff..661d1d33a5283 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/__snapshots__/sample_data_view_data_button.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/__snapshots__/sample_data_view_data_button.test.js.snap @@ -14,6 +14,7 @@ exports[`should render popover when appLinks is not empty 1`] = ` } closePopover={[Function]} + data-test-subj="launchSampleDataSetecommerce" display="inlineBlock" hasArrow={true} id="sampleDataLinksecommerce" diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/sample_data_view_data_button.js b/src/legacy/core_plugins/kibana/public/home/np_ready/components/sample_data_view_data_button.js index e6f5c07c94f9f..cb43c18a8e78b 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/sample_data_view_data_button.js +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/sample_data_view_data_button.js @@ -112,6 +112,7 @@ export class SampleDataViewDataButton extends React.Component { closePopover={this.closePopover} panelPaddingSize="none" anchorPosition="downCenter" + data-test-subj={`launchSampleDataSet${this.props.id}`} > diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/tutorial/replace_template_strings.js b/src/legacy/core_plugins/kibana/public/home/np_ready/components/tutorial/replace_template_strings.js index daf996444eb3c..c7e623657bf71 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/tutorial/replace_template_strings.js +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/tutorial/replace_template_strings.js @@ -33,7 +33,7 @@ mustacheWriter.escapedValue = function escapedValue(token, context) { }; export function replaceTemplateStrings(text, params = {}) { - const { getInjected, metadata, docLinks } = getServices(); + const { getInjected, kibanaVersion, docLinks } = getServices(); const variables = { // '{' and '}' can not be used in template since they are used as template tags. @@ -58,7 +58,7 @@ export function replaceTemplateStrings(text, params = {}) { version: docLinks.DOC_LINK_VERSION, }, kibana: { - version: metadata.version, + version: kibanaVersion, }, }, params: params, diff --git a/src/legacy/core_plugins/kibana/public/home/plugin.ts b/src/legacy/core_plugins/kibana/public/home/plugin.ts index 5cc7c9c11dd2f..75e7cc2e453be 100644 --- a/src/legacy/core_plugins/kibana/public/home/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/home/plugin.ts @@ -17,7 +17,13 @@ * under the License. */ -import { CoreSetup, CoreStart, LegacyNavLink, Plugin, UiSettingsState } from 'kibana/public'; +import { + AppMountParameters, + CoreSetup, + CoreStart, + Plugin, + PluginInitializerContext, +} from 'kibana/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; import { TelemetryPluginStart } from 'src/plugins/telemetry/public'; @@ -38,21 +44,6 @@ export interface HomePluginStartDependencies { } export interface HomePluginSetupDependencies { - __LEGACY: { - metadata: { - app: unknown; - bundleId: string; - nav: LegacyNavLink[]; - version: string; - branch: string; - buildNum: number; - buildSha: string; - basePath: string; - serverName: string; - devMode: boolean; - uiSettings: { defaults: UiSettingsState; user?: UiSettingsState | undefined }; - }; - }; usageCollection: UsageCollectionSetup; kibanaLegacy: KibanaLegacySetup; home: HomePublicPluginSetup; @@ -65,31 +56,26 @@ export class HomePlugin implements Plugin { private directories: readonly FeatureCatalogueEntry[] | null = null; private telemetry?: TelemetryPluginStart; - setup( - core: CoreSetup, - { - home, - kibanaLegacy, - usageCollection, - __LEGACY: { ...legacyServices }, - }: HomePluginSetupDependencies - ) { + constructor(private initializerContext: PluginInitializerContext) {} + + setup(core: CoreSetup, { home, kibanaLegacy, usageCollection }: HomePluginSetupDependencies) { kibanaLegacy.registerLegacyApp({ id: 'home', title: 'Home', - mount: async ({ core: contextCore }, params) => { + mount: async (params: AppMountParameters) => { const trackUiMetric = usageCollection.reportUiStats.bind(usageCollection, 'Kibana_home'); + const [coreStart] = await core.getStartServices(); setServices({ - ...legacyServices, trackUiMetric, - http: contextCore.http, + kibanaVersion: this.initializerContext.env.packageInfo.version, + http: coreStart.http, toastNotifications: core.notifications.toasts, - banners: contextCore.overlays.banners, + banners: coreStart.overlays.banners, getInjected: core.injectedMetadata.getInjectedVar, - docLinks: contextCore.docLinks, + docLinks: coreStart.docLinks, savedObjectsClient: this.savedObjectsClient!, + chrome: coreStart.chrome, telemetry: this.telemetry, - chrome: contextCore.chrome, uiSettings: core.uiSettings, addBasePath: core.http.basePath.prepend, getBasePath: core.http.basePath.get, diff --git a/src/legacy/core_plugins/kibana/public/visualize/index.ts b/src/legacy/core_plugins/kibana/public/visualize/index.ts index 83b820a8e3134..c3ae39d9fde25 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/index.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/index.ts @@ -25,5 +25,5 @@ export { VisualizeConstants, createVisualizeEditUrl } from './np_ready/visualize // Core will be looking for this when loading our plugin in the new platform export const plugin = (context: PluginInitializerContext) => { - return new VisualizePlugin(); + return new VisualizePlugin(context); }; diff --git a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts index 428e6cb225710..6082fb8428ac3 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts @@ -19,11 +19,12 @@ import { ChromeStart, - LegacyCoreStart, + CoreStart, SavedObjectsClientContract, ToastsStart, IUiSettingsClient, I18nStart, + PluginInitializerContext, } from 'kibana/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public'; @@ -38,11 +39,12 @@ import { Chrome } from './legacy_imports'; import { KibanaLegacyStart } from '../../../../../plugins/kibana_legacy/public'; export interface VisualizeKibanaServices { + pluginInitializerContext: PluginInitializerContext; addBasePath: (url: string) => string; chrome: ChromeStart; - core: LegacyCoreStart; + core: CoreStart; data: DataPublicPluginStart; - embeddables: IEmbeddableStart; + embeddable: IEmbeddableStart; getBasePath: () => string; indexPatterns: IndexPatternsContract; legacyChrome: Chrome; diff --git a/src/legacy/core_plugins/kibana/public/visualize/legacy.ts b/src/legacy/core_plugins/kibana/public/visualize/legacy.ts index 2d615e3132b01..bc2d700f6c6a1 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/legacy.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/legacy.ts @@ -19,21 +19,19 @@ import { PluginInitializerContext } from 'kibana/public'; import { legacyChrome, npSetup, npStart } from './legacy_imports'; -import { start as embeddables } from '../../../embeddable_api/public/np_ready/public/legacy'; import { start as visualizations } from '../../../visualizations/public/np_ready/public/legacy'; import { plugin } from './index'; -(() => { - const instance = plugin({} as PluginInitializerContext); - instance.setup(npSetup.core, { - ...npSetup.plugins, - __LEGACY: { - legacyChrome, - }, - }); - instance.start(npStart.core, { - ...npStart.plugins, - embeddables, - visualizations, - }); -})(); +const instance = plugin({ + env: npSetup.plugins.kibanaLegacy.env, +} as PluginInitializerContext); +instance.setup(npSetup.core, { + ...npSetup.plugins, + __LEGACY: { + legacyChrome, + }, +}); +instance.start(npStart.core, { + ...npStart.plugins, + visualizations, +}); diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts index 44e7e9c2a7413..3d5fd6605f56b 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts @@ -20,7 +20,7 @@ import angular, { IModule } from 'angular'; import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; -import { AppMountContext, LegacyCoreStart } from 'kibana/public'; +import { AppMountContext } from 'kibana/public'; import { AppStateProvider, AppState, @@ -53,7 +53,11 @@ export const renderApp = async ( if (!angularModuleInstance) { angularModuleInstance = createLocalAngularModule(deps.core, deps.navigation); // global routing stuff - configureAppAngularModule(angularModuleInstance, deps.core as LegacyCoreStart, true); + configureAppAngularModule( + angularModuleInstance, + { core: deps.core, env: deps.pluginInitializerContext.env }, + true + ); // custom routing stuff initVisualizeApp(angularModuleInstance, deps); } diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js index 46ae45c3a5fa2..27fb9b63843c4 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js @@ -31,6 +31,7 @@ import { getEditBreadcrumbs } from '../breadcrumbs'; import { addHelpMenuToAppChrome } from '../help_menu/help_menu_util'; import { FilterStateManager } from '../../../../../data/public'; import { unhashUrl } from '../../../../../../../plugins/kibana_utils/public'; +import { kbnBaseUrl } from '../../../../../../../plugins/kibana_legacy/public'; import { SavedObjectSaveModal, showSaveModal, @@ -74,7 +75,6 @@ function VisualizeAppController( kbnUrl, redirectWhenMissing, Promise, - kbnBaseUrl, getAppState, globalState ) { diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization.js index 18a60f7c3c10b..502bd6e56fb1f 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization.js @@ -31,7 +31,7 @@ export function initVisualizationDirective(app, deps) { link: function($scope, element) { $scope.renderFunction = async () => { if (!$scope._handler) { - $scope._handler = await deps.embeddables + $scope._handler = await deps.embeddable .getEmbeddableFactory('visualization') .createFromObject($scope.savedObj, { timeRange: $scope.timeRange, diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization_editor.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization_editor.js index b2386f83b252c..8032152f88173 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization_editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization_editor.js @@ -36,7 +36,7 @@ export function initVisEditorDirective(app, deps) { editor.render({ core: deps.core, data: deps.data, - embeddables: deps.embeddables, + embeddable: deps.embeddable, uiState: $scope.uiState, timeRange: $scope.timeRange, filters: $scope.filters, diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/types.d.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/types.d.ts index 17be5e4051b12..524bc4b3196b7 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/types.d.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/types.d.ts @@ -26,7 +26,7 @@ export interface EditorRenderProps { appState: AppState; core: LegacyCoreStart; data: DataPublicPluginStart; - embeddables: IEmbeddableStart; + embeddable: IEmbeddableStart; filters: Filter[]; uiState: PersistedState; timeRange: TimeRange; diff --git a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts index ce93fe7c2d578..16715677d1e20 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts @@ -20,10 +20,11 @@ import { i18n } from '@kbn/i18n'; import { + AppMountParameters, CoreSetup, CoreStart, - LegacyCoreStart, Plugin, + PluginInitializerContext, SavedObjectsClientContract, } from 'kibana/public'; @@ -45,7 +46,7 @@ import { Chrome } from './legacy_imports'; export interface VisualizePluginStartDependencies { data: DataPublicPluginStart; - embeddables: IEmbeddableStart; + embeddable: IEmbeddableStart; navigation: NavigationStart; share: SharePluginStart; visualizations: VisualizationsStart; @@ -63,13 +64,15 @@ export interface VisualizePluginSetupDependencies { export class VisualizePlugin implements Plugin { private startDependencies: { data: DataPublicPluginStart; - embeddables: IEmbeddableStart; + embeddable: IEmbeddableStart; navigation: NavigationStart; savedObjectsClient: SavedObjectsClientContract; share: SharePluginStart; visualizations: VisualizationsStart; } | null = null; + constructor(private initializerContext: PluginInitializerContext) {} + public async setup( core: CoreSetup, { home, kibanaLegacy, __LEGACY, usageCollection }: VisualizePluginSetupDependencies @@ -77,14 +80,15 @@ export class VisualizePlugin implements Plugin { kibanaLegacy.registerLegacyApp({ id: 'visualize', title: 'Visualize', - mount: async ({ core: contextCore }, params) => { + mount: async (params: AppMountParameters) => { + const [coreStart] = await core.getStartServices(); if (this.startDependencies === null) { throw new Error('not started yet'); } const { savedObjectsClient, - embeddables, + embeddable, navigation, visualizations, data, @@ -93,11 +97,12 @@ export class VisualizePlugin implements Plugin { const deps: VisualizeKibanaServices = { ...__LEGACY, - addBasePath: contextCore.http.basePath.prepend, - core: contextCore as LegacyCoreStart, - chrome: contextCore.chrome, + pluginInitializerContext: this.initializerContext, + addBasePath: coreStart.http.basePath.prepend, + core: coreStart, + chrome: coreStart.chrome, data, - embeddables, + embeddable, getBasePath: core.http.basePath.get, indexPatterns: data.indexPatterns, localStorage: new Storage(localStorage), @@ -106,13 +111,13 @@ export class VisualizePlugin implements Plugin { savedVisualizations: visualizations.getSavedVisualizationsLoader(), savedQueryService: data.query.savedQueries, share, - toastNotifications: contextCore.notifications.toasts, - uiSettings: contextCore.uiSettings, + toastNotifications: coreStart.notifications.toasts, + uiSettings: coreStart.uiSettings, config: kibanaLegacy.config, - visualizeCapabilities: contextCore.application.capabilities.visualize, + visualizeCapabilities: coreStart.application.capabilities.visualize, visualizations, usageCollection, - I18nContext: contextCore.i18n.Context, + I18nContext: coreStart.i18n.Context, }; setServices(deps); @@ -137,11 +142,11 @@ export class VisualizePlugin implements Plugin { public start( core: CoreStart, - { embeddables, navigation, data, share, visualizations }: VisualizePluginStartDependencies + { embeddable, navigation, data, share, visualizations }: VisualizePluginStartDependencies ) { this.startDependencies = { data, - embeddables, + embeddable, navigation, savedObjectsClient: core.savedObjects.client, share, diff --git a/src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx b/src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx index 48a1a6f9d2121..32ea71c0bc005 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx @@ -30,7 +30,7 @@ import { DefaultEditorControllerState } from './default_editor_controller'; import { getInitialWidth } from './editor_size'; function DefaultEditor({ - embeddables, + embeddable, savedObj, uiState, timeRange, @@ -56,7 +56,7 @@ function DefaultEditor({ } if (!visHandler.current) { - const embeddableFactory = embeddables.getEmbeddableFactory( + const embeddableFactory = embeddable.getEmbeddableFactory( 'visualization' ) as VisualizeEmbeddableFactory; setFactory(embeddableFactory); @@ -82,7 +82,7 @@ function DefaultEditor({ } visualize(); - }, [uiState, savedObj, timeRange, filters, appState, query, factory, embeddables]); + }, [uiState, savedObj, timeRange, filters, appState, query, factory, embeddable]); useEffect(() => { return () => { diff --git a/src/legacy/core_plugins/vis_type_vega/index.ts b/src/legacy/core_plugins/vis_type_vega/index.ts index 52c253c6ac0b5..ccef24f8f9746 100644 --- a/src/legacy/core_plugins/vis_type_vega/index.ts +++ b/src/legacy/core_plugins/vis_type_vega/index.ts @@ -39,17 +39,10 @@ const vegaPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPlugin return { emsTileLayerId: mapConfig.emsTileLayerId, - enableExternalUrls: serverConfig.get('vega.enableExternalUrls'), }; }, }, init: (server: Legacy.Server) => ({}), - config(Joi: any) { - return Joi.object({ - enabled: Joi.boolean().default(true), - enableExternalUrls: Joi.boolean().default(false), - }).default(); - }, } as Legacy.PluginSpecOptions); // eslint-disable-next-line import/no-default-export diff --git a/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js b/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js index b2ad45b5d7b6d..868e5729bd494 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js +++ b/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js @@ -47,6 +47,7 @@ import { createVegaTypeDefinition } from '../vega_type'; // this test has to be migrated to the newly created integration test environment. // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { npStart } from 'ui/new_platform'; +import { setInjectedVars } from '../services'; const THRESHOLD = 0.1; const PIXEL_DIFF = 30; @@ -60,6 +61,12 @@ describe('VegaVisualizations', () => { let vegaVisualizationDependencies; let visRegComplete = false; + setInjectedVars({ + emsTileLayerId: {}, + enableExternalUrls: true, + esShardTimeout: 10000, + }); + beforeEach(ngMock.module('kibana')); beforeEach( ngMock.inject((Private, $injector) => { diff --git a/src/legacy/core_plugins/vis_type_vega/public/legacy.ts b/src/legacy/core_plugins/vis_type_vega/public/legacy.ts index a7928c7d65e81..38ce706ed13ef 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/legacy.ts +++ b/src/legacy/core_plugins/vis_type_vega/public/legacy.ts @@ -26,9 +26,8 @@ import { LegacyDependenciesPlugin } from './shim'; import { plugin } from '.'; const setupPlugins: Readonly = { - expressions: npSetup.plugins.expressions, + ...npSetup.plugins, visualizations: visualizationsSetup, - data: npSetup.plugins.data, // Temporary solution // It will be removed when all dependent services are migrated to the new platform. @@ -36,7 +35,7 @@ const setupPlugins: Readonly = { }; const startPlugins: Readonly = { - data: npStart.plugins.data, + ...npStart.plugins, }; const pluginInstance = plugin({} as PluginInitializerContext); diff --git a/src/legacy/core_plugins/vis_type_vega/public/plugin.ts b/src/legacy/core_plugins/vis_type_vega/public/plugin.ts index 9721de9848cfc..b354433330caf 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_vega/public/plugin.ts @@ -31,6 +31,7 @@ import { import { createVegaFn } from './vega_fn'; import { createVegaTypeDefinition } from './vega_type'; +import { VisTypeVegaSetup } from '../../../../plugins/vis_type_vega/public'; /** @internal */ export interface VegaVisualizationDependencies extends LegacyDependenciesPluginSetup { @@ -45,6 +46,7 @@ export interface VegaPluginSetupDependencies { expressions: ReturnType; visualizations: VisualizationsSetup; data: ReturnType; + visTypeVega: VisTypeVegaSetup; __LEGACY: LegacyDependenciesPlugin; } @@ -63,11 +65,11 @@ export class VegaPlugin implements Plugin, void> { public async setup( core: CoreSetup, - { data, expressions, visualizations, __LEGACY }: VegaPluginSetupDependencies + { data, expressions, visualizations, visTypeVega, __LEGACY }: VegaPluginSetupDependencies ) { setInjectedVars({ + enableExternalUrls: visTypeVega.config.enableExternalUrls, esShardTimeout: core.injectedMetadata.getInjectedVar('esShardTimeout') as number, - enableExternalUrls: core.injectedMetadata.getInjectedVar('enableExternalUrls') as boolean, emsTileLayerId: core.injectedMetadata.getInjectedVar('emsTileLayerId', true), }); setUISettings(core.uiSettings); diff --git a/src/legacy/ui/public/legacy_compat/__tests__/xsrf.js b/src/legacy/ui/public/legacy_compat/__tests__/xsrf.js index fc12a18d72823..3ca836e23881a 100644 --- a/src/legacy/ui/public/legacy_compat/__tests__/xsrf.js +++ b/src/legacy/ui/public/legacy_compat/__tests__/xsrf.js @@ -27,13 +27,6 @@ import { $setupXsrfRequestInterceptor } from '../../../../../plugins/kibana_lega import { version } from '../../../../../core/server/utils/package_json'; const xsrfHeader = 'kbn-version'; -const newPlatform = { - injectedMetadata: { - getLegacyMetadata() { - return { version }; - }, - }, -}; describe('chrome xsrf apis', function() { const sandbox = sinon.createSandbox(); @@ -45,7 +38,7 @@ describe('chrome xsrf apis', function() { describe('jQuery support', function() { it('adds a global jQuery prefilter', function() { sandbox.stub($, 'ajaxPrefilter'); - $setupXsrfRequestInterceptor(newPlatform); + $setupXsrfRequestInterceptor(version); expect($.ajaxPrefilter.callCount).to.be(1); }); @@ -54,7 +47,7 @@ describe('chrome xsrf apis', function() { beforeEach(function() { sandbox.stub($, 'ajaxPrefilter'); - $setupXsrfRequestInterceptor(newPlatform); + $setupXsrfRequestInterceptor(version); prefilter = $.ajaxPrefilter.args[0][0]; }); @@ -79,7 +72,7 @@ describe('chrome xsrf apis', function() { beforeEach(function() { sandbox.stub($, 'ajaxPrefilter'); - ngMock.module($setupXsrfRequestInterceptor(newPlatform)); + ngMock.module($setupXsrfRequestInterceptor(version)); }); beforeEach( diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts index ff8fc9b07879c..b7994c7f68afb 100644 --- a/src/legacy/ui/public/new_platform/new_platform.ts +++ b/src/legacy/ui/public/new_platform/new_platform.ts @@ -44,6 +44,7 @@ import { NavigationPublicPluginSetup, NavigationPublicPluginStart, } from '../../../../plugins/navigation/public'; +import { VisTypeVegaSetup } from '../../../../plugins/vis_type_vega/public'; export interface PluginsSetup { bfetch: BfetchPublicSetup; @@ -61,6 +62,7 @@ export interface PluginsSetup { usageCollection: UsageCollectionSetup; advancedSettings: AdvancedSettingsSetup; management: ManagementSetup; + visTypeVega: VisTypeVegaSetup; telemetry?: TelemetryPluginSetup; } diff --git a/src/legacy/ui/public/system_api/__tests__/system_api.js b/src/legacy/ui/public/system_api/__tests__/system_api.js index 822edaa08fdd6..816024f13f8b2 100644 --- a/src/legacy/ui/public/system_api/__tests__/system_api.js +++ b/src/legacy/ui/public/system_api/__tests__/system_api.js @@ -31,8 +31,8 @@ describe('system_api', () => { }; const newHeaders = addSystemApiHeader(headers); - expect(newHeaders).to.have.property('kbn-system-api'); - expect(newHeaders['kbn-system-api']).to.be(true); + expect(newHeaders).to.have.property('kbn-system-request'); + expect(newHeaders['kbn-system-request']).to.be(true); expect(newHeaders).to.have.property('kbn-version'); expect(newHeaders['kbn-version']).to.be('4.6.0'); @@ -40,7 +40,16 @@ describe('system_api', () => { }); describe('#isSystemApiRequest', () => { - it('returns true for a system API HTTP request', () => { + it('returns true for a system HTTP request', () => { + const mockRequest = { + headers: { + 'kbn-system-request': true, + }, + }; + expect(isSystemApiRequest(mockRequest)).to.be(true); + }); + + it('returns true for a legacy system API HTTP request', () => { const mockRequest = { headers: { 'kbn-system-api': true, diff --git a/src/plugins/console/public/application/models/legacy_core_editor/__tests__/input.test.js b/src/plugins/console/public/application/models/legacy_core_editor/__tests__/input.test.js new file mode 100644 index 0000000000000..a68a2b3939864 --- /dev/null +++ b/src/plugins/console/public/application/models/legacy_core_editor/__tests__/input.test.js @@ -0,0 +1,568 @@ +/* + * 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 '../legacy_core_editor.test.mocks'; +import RowParser from '../../../../lib/row_parser'; +import { createTokenIterator } from '../../../factories'; +import $ from 'jquery'; +import { create } from '../create'; + +describe('Input', () => { + let coreEditor; + beforeEach(() => { + // Set up our document body + document.body.innerHTML = `
+
+
+
+
`; + + coreEditor = create(document.querySelector('#ConAppEditor')); + + $(coreEditor.getContainer()).show(); + }); + afterEach(() => { + $(coreEditor.getContainer()).hide(); + }); + + describe('.getLineCount', () => { + it('returns the correct line length', async () => { + await coreEditor.setValue('1\n2\n3\n4', true); + expect(coreEditor.getLineCount()).toBe(4); + }); + }); + + describe('Tokenization', () => { + function tokensAsList() { + const iter = createTokenIterator({ + editor: coreEditor, + position: { lineNumber: 1, column: 1 }, + }); + const ret = []; + let t = iter.getCurrentToken(); + const parser = new RowParser(coreEditor); + if (parser.isEmptyToken(t)) { + t = parser.nextNonEmptyToken(iter); + } + while (t) { + ret.push({ value: t.value, type: t.type }); + t = parser.nextNonEmptyToken(iter); + } + + return ret; + } + + let testCount = 0; + + function tokenTest(tokenList, prefix, data) { + if (data && typeof data !== 'string') { + data = JSON.stringify(data, null, 3); + } + if (data) { + if (prefix) { + data = prefix + '\n' + data; + } + } else { + data = prefix; + } + + test('Token test ' + testCount++ + ' prefix: ' + prefix, async function() { + await coreEditor.setValue(data, true); + const tokens = tokensAsList(); + const normTokenList = []; + for (let i = 0; i < tokenList.length; i++) { + normTokenList.push({ type: tokenList[i++], value: tokenList[i] }); + } + + expect(tokens).toEqual(normTokenList); + }); + } + + tokenTest(['method', 'GET', 'url.part', '_search'], 'GET _search'); + + tokenTest(['method', 'GET', 'url.slash', '/', 'url.part', '_search'], 'GET /_search'); + + tokenTest( + [ + 'method', + 'GET', + 'url.protocol_host', + 'http://somehost', + 'url.slash', + '/', + 'url.part', + '_search', + ], + 'GET http://somehost/_search' + ); + + tokenTest(['method', 'GET', 'url.protocol_host', 'http://somehost'], 'GET http://somehost'); + + tokenTest( + ['method', 'GET', 'url.protocol_host', 'http://somehost', 'url.slash', '/'], + 'GET http://somehost/' + ); + + tokenTest( + ['method', 'GET', 'url.protocol_host', 'http://test:user@somehost', 'url.slash', '/'], + 'GET http://test:user@somehost/' + ); + + tokenTest( + ['method', 'GET', 'url.part', '_cluster', 'url.slash', '/', 'url.part', 'nodes'], + 'GET _cluster/nodes' + ); + + tokenTest( + [ + 'method', + 'GET', + 'url.slash', + '/', + 'url.part', + '_cluster', + 'url.slash', + '/', + 'url.part', + 'nodes', + ], + 'GET /_cluster/nodes' + ); + + tokenTest( + ['method', 'GET', 'url.part', 'index', 'url.slash', '/', 'url.part', '_search'], + 'GET index/_search' + ); + + tokenTest(['method', 'GET', 'url.part', 'index'], 'GET index'); + + tokenTest( + ['method', 'GET', 'url.part', 'index', 'url.slash', '/', 'url.part', 'type'], + 'GET index/type' + ); + + tokenTest( + [ + 'method', + 'GET', + 'url.slash', + '/', + 'url.part', + 'index', + 'url.slash', + '/', + 'url.part', + 'type', + 'url.slash', + '/', + ], + 'GET /index/type/' + ); + + tokenTest( + [ + 'method', + 'GET', + 'url.part', + 'index', + 'url.slash', + '/', + 'url.part', + 'type', + 'url.slash', + '/', + 'url.part', + '_search', + ], + 'GET index/type/_search' + ); + + tokenTest( + [ + 'method', + 'GET', + 'url.part', + 'index', + 'url.slash', + '/', + 'url.part', + 'type', + 'url.slash', + '/', + 'url.part', + '_search', + 'url.questionmark', + '?', + 'url.param', + 'value', + 'url.equal', + '=', + 'url.value', + '1', + ], + 'GET index/type/_search?value=1' + ); + + tokenTest( + [ + 'method', + 'GET', + 'url.part', + 'index', + 'url.slash', + '/', + 'url.part', + 'type', + 'url.slash', + '/', + 'url.part', + '1', + ], + 'GET index/type/1' + ); + + tokenTest( + [ + 'method', + 'GET', + 'url.slash', + '/', + 'url.part', + 'index1', + 'url.comma', + ',', + 'url.part', + 'index2', + 'url.slash', + '/', + ], + 'GET /index1,index2/' + ); + + tokenTest( + [ + 'method', + 'GET', + 'url.slash', + '/', + 'url.part', + 'index1', + 'url.comma', + ',', + 'url.part', + 'index2', + 'url.slash', + '/', + 'url.part', + '_search', + ], + 'GET /index1,index2/_search' + ); + + tokenTest( + [ + 'method', + 'GET', + 'url.part', + 'index1', + 'url.comma', + ',', + 'url.part', + 'index2', + 'url.slash', + '/', + 'url.part', + '_search', + ], + 'GET index1,index2/_search' + ); + + tokenTest( + [ + 'method', + 'GET', + 'url.slash', + '/', + 'url.part', + 'index1', + 'url.comma', + ',', + 'url.part', + 'index2', + ], + 'GET /index1,index2' + ); + + tokenTest( + ['method', 'GET', 'url.part', 'index1', 'url.comma', ',', 'url.part', 'index2'], + 'GET index1,index2' + ); + + tokenTest( + ['method', 'GET', 'url.slash', '/', 'url.part', 'index1', 'url.comma', ','], + 'GET /index1,' + ); + + tokenTest( + ['method', 'PUT', 'url.slash', '/', 'url.part', 'index', 'url.slash', '/'], + 'PUT /index/' + ); + + tokenTest( + ['method', 'GET', 'url.part', 'index', 'url.slash', '/', 'url.part', '_search'], + 'GET index/_search ' + ); + + tokenTest(['method', 'PUT', 'url.slash', '/', 'url.part', 'index'], 'PUT /index'); + + tokenTest( + [ + 'method', + 'PUT', + 'url.slash', + '/', + 'url.part', + 'index1', + 'url.comma', + ',', + 'url.part', + 'index2', + 'url.slash', + '/', + 'url.part', + 'type1', + 'url.comma', + ',', + 'url.part', + 'type2', + ], + 'PUT /index1,index2/type1,type2' + ); + + tokenTest( + [ + 'method', + 'PUT', + 'url.slash', + '/', + 'url.part', + 'index1', + 'url.slash', + '/', + 'url.part', + 'type1', + 'url.comma', + ',', + 'url.part', + 'type2', + 'url.comma', + ',', + ], + 'PUT /index1/type1,type2,' + ); + + tokenTest( + [ + 'method', + 'PUT', + 'url.part', + 'index1', + 'url.comma', + ',', + 'url.part', + 'index2', + 'url.slash', + '/', + 'url.part', + 'type1', + 'url.comma', + ',', + 'url.part', + 'type2', + 'url.slash', + '/', + 'url.part', + '1234', + ], + 'PUT index1,index2/type1,type2/1234' + ); + + tokenTest( + [ + 'method', + 'POST', + 'url.part', + '_search', + 'paren.lparen', + '{', + 'variable', + '"q"', + 'punctuation.colon', + ':', + 'paren.lparen', + '{', + 'paren.rparen', + '}', + 'paren.rparen', + '}', + ], + 'POST _search\n' + '{\n' + ' "q": {}\n' + ' \n' + '}' + ); + + tokenTest( + [ + 'method', + 'POST', + 'url.part', + '_search', + 'paren.lparen', + '{', + 'variable', + '"q"', + 'punctuation.colon', + ':', + 'paren.lparen', + '{', + 'variable', + '"s"', + 'punctuation.colon', + ':', + 'paren.lparen', + '{', + 'paren.rparen', + '}', + 'paren.rparen', + '}', + 'paren.rparen', + '}', + ], + 'POST _search\n' + '{\n' + ' "q": { "s": {}}\n' + ' \n' + '}' + ); + + function statesAsList() { + const ret = []; + const maxLine = coreEditor.getLineCount(); + for (let line = 1; line <= maxLine; line++) ret.push(coreEditor.getLineState(line)); + return ret; + } + + function statesTest(statesList, prefix, data) { + if (data && typeof data !== 'string') { + data = JSON.stringify(data, null, 3); + } + if (data) { + if (prefix) { + data = prefix + '\n' + data; + } + } else { + data = prefix; + } + + test('States test ' + testCount++ + ' prefix: ' + prefix, async function() { + await coreEditor.setValue(data, true); + const modes = statesAsList(); + expect(modes).toEqual(statesList); + }); + } + + statesTest( + ['start', 'json', 'json', 'start'], + 'POST _search\n' + '{\n' + ' "query": { "match_all": {} }\n' + '}' + ); + + statesTest( + ['start', 'json', ['json', 'json'], ['json', 'json'], 'json', 'start'], + 'POST _search\n' + '{\n' + ' "query": { \n' + ' "match_all": {} \n' + ' }\n' + '}' + ); + + statesTest( + ['start', 'json', 'json', 'start'], + 'POST _search\n' + '{\n' + ' "script": { "source": "" }\n' + '}' + ); + + statesTest( + ['start', 'json', 'json', 'start'], + 'POST _search\n' + '{\n' + ' "script": ""\n' + '}' + ); + + statesTest( + ['start', 'json', ['json', 'json'], 'json', 'start'], + 'POST _search\n' + '{\n' + ' "script": {\n' + ' }\n' + '}' + ); + + statesTest( + [ + 'start', + 'json', + ['script-start', 'json', 'json', 'json'], + ['script-start', 'json', 'json', 'json'], + ['json', 'json'], + 'json', + 'start', + ], + 'POST _search\n' + + '{\n' + + ' "test": { "script": """\n' + + ' test script\n' + + ' """\n' + + ' }\n' + + '}' + ); + + statesTest( + ['start', 'json', ['script-start', 'json'], ['script-start', 'json'], 'json', 'start'], + 'POST _search\n' + '{\n' + ' "script": """\n' + ' test script\n' + ' """,\n' + '}' + ); + + statesTest( + ['start', 'json', 'json', 'start'], + 'POST _search\n' + '{\n' + ' "script": """test script""",\n' + '}' + ); + + statesTest( + ['start', 'json', ['string_literal', 'json'], ['string_literal', 'json'], 'json', 'start'], + 'POST _search\n' + '{\n' + ' "something": """\n' + ' test script\n' + ' """,\n' + '}' + ); + + statesTest( + [ + 'start', + 'json', + ['string_literal', 'json', 'json', 'json'], + ['string_literal', 'json', 'json', 'json'], + ['json', 'json'], + ['json', 'json'], + 'json', + 'start', + ], + 'POST _search\n' + + '{\n' + + ' "something": { "f" : """\n' + + ' test script\n' + + ' """,\n' + + ' "g": 1\n' + + ' }\n' + + '}' + ); + + statesTest( + ['start', 'json', 'json', 'start'], + 'POST _search\n' + '{\n' + ' "something": """test script""",\n' + '}' + ); + }); +}); diff --git a/src/plugins/console/public/application/models/legacy_core_editor/__tests__/input_tokenization.test.js b/src/plugins/console/public/application/models/legacy_core_editor/__tests__/input_tokenization.test.js deleted file mode 100644 index 019b3c1d0538a..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/__tests__/input_tokenization.test.js +++ /dev/null @@ -1,559 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import '../legacy_core_editor.test.mocks'; -import RowParser from '../../../../lib/row_parser'; -import { createTokenIterator } from '../../../factories'; -import $ from 'jquery'; -import { create } from '../create'; - -describe('Input Tokenization', () => { - let coreEditor; - beforeEach(() => { - // Set up our document body - document.body.innerHTML = `
-
-
-
-
`; - - coreEditor = create(document.querySelector('#ConAppEditor')); - - $(coreEditor.getContainer()).show(); - }); - afterEach(() => { - $(coreEditor.getContainer()).hide(); - }); - - function tokensAsList() { - const iter = createTokenIterator({ - editor: coreEditor, - position: { lineNumber: 1, column: 1 }, - }); - const ret = []; - let t = iter.getCurrentToken(); - const parser = new RowParser(coreEditor); - if (parser.isEmptyToken(t)) { - t = parser.nextNonEmptyToken(iter); - } - while (t) { - ret.push({ value: t.value, type: t.type }); - t = parser.nextNonEmptyToken(iter); - } - - return ret; - } - - let testCount = 0; - - function tokenTest(tokenList, prefix, data) { - if (data && typeof data !== 'string') { - data = JSON.stringify(data, null, 3); - } - if (data) { - if (prefix) { - data = prefix + '\n' + data; - } - } else { - data = prefix; - } - - test('Token test ' + testCount++ + ' prefix: ' + prefix, async function() { - await coreEditor.setValue(data, true); - const tokens = tokensAsList(); - const normTokenList = []; - for (let i = 0; i < tokenList.length; i++) { - normTokenList.push({ type: tokenList[i++], value: tokenList[i] }); - } - - expect(tokens).toEqual(normTokenList); - }); - } - - tokenTest(['method', 'GET', 'url.part', '_search'], 'GET _search'); - - tokenTest(['method', 'GET', 'url.slash', '/', 'url.part', '_search'], 'GET /_search'); - - tokenTest( - [ - 'method', - 'GET', - 'url.protocol_host', - 'http://somehost', - 'url.slash', - '/', - 'url.part', - '_search', - ], - 'GET http://somehost/_search' - ); - - tokenTest(['method', 'GET', 'url.protocol_host', 'http://somehost'], 'GET http://somehost'); - - tokenTest( - ['method', 'GET', 'url.protocol_host', 'http://somehost', 'url.slash', '/'], - 'GET http://somehost/' - ); - - tokenTest( - ['method', 'GET', 'url.protocol_host', 'http://test:user@somehost', 'url.slash', '/'], - 'GET http://test:user@somehost/' - ); - - tokenTest( - ['method', 'GET', 'url.part', '_cluster', 'url.slash', '/', 'url.part', 'nodes'], - 'GET _cluster/nodes' - ); - - tokenTest( - [ - 'method', - 'GET', - 'url.slash', - '/', - 'url.part', - '_cluster', - 'url.slash', - '/', - 'url.part', - 'nodes', - ], - 'GET /_cluster/nodes' - ); - - tokenTest( - ['method', 'GET', 'url.part', 'index', 'url.slash', '/', 'url.part', '_search'], - 'GET index/_search' - ); - - tokenTest(['method', 'GET', 'url.part', 'index'], 'GET index'); - - tokenTest( - ['method', 'GET', 'url.part', 'index', 'url.slash', '/', 'url.part', 'type'], - 'GET index/type' - ); - - tokenTest( - [ - 'method', - 'GET', - 'url.slash', - '/', - 'url.part', - 'index', - 'url.slash', - '/', - 'url.part', - 'type', - 'url.slash', - '/', - ], - 'GET /index/type/' - ); - - tokenTest( - [ - 'method', - 'GET', - 'url.part', - 'index', - 'url.slash', - '/', - 'url.part', - 'type', - 'url.slash', - '/', - 'url.part', - '_search', - ], - 'GET index/type/_search' - ); - - tokenTest( - [ - 'method', - 'GET', - 'url.part', - 'index', - 'url.slash', - '/', - 'url.part', - 'type', - 'url.slash', - '/', - 'url.part', - '_search', - 'url.questionmark', - '?', - 'url.param', - 'value', - 'url.equal', - '=', - 'url.value', - '1', - ], - 'GET index/type/_search?value=1' - ); - - tokenTest( - [ - 'method', - 'GET', - 'url.part', - 'index', - 'url.slash', - '/', - 'url.part', - 'type', - 'url.slash', - '/', - 'url.part', - '1', - ], - 'GET index/type/1' - ); - - tokenTest( - [ - 'method', - 'GET', - 'url.slash', - '/', - 'url.part', - 'index1', - 'url.comma', - ',', - 'url.part', - 'index2', - 'url.slash', - '/', - ], - 'GET /index1,index2/' - ); - - tokenTest( - [ - 'method', - 'GET', - 'url.slash', - '/', - 'url.part', - 'index1', - 'url.comma', - ',', - 'url.part', - 'index2', - 'url.slash', - '/', - 'url.part', - '_search', - ], - 'GET /index1,index2/_search' - ); - - tokenTest( - [ - 'method', - 'GET', - 'url.part', - 'index1', - 'url.comma', - ',', - 'url.part', - 'index2', - 'url.slash', - '/', - 'url.part', - '_search', - ], - 'GET index1,index2/_search' - ); - - tokenTest( - [ - 'method', - 'GET', - 'url.slash', - '/', - 'url.part', - 'index1', - 'url.comma', - ',', - 'url.part', - 'index2', - ], - 'GET /index1,index2' - ); - - tokenTest( - ['method', 'GET', 'url.part', 'index1', 'url.comma', ',', 'url.part', 'index2'], - 'GET index1,index2' - ); - - tokenTest( - ['method', 'GET', 'url.slash', '/', 'url.part', 'index1', 'url.comma', ','], - 'GET /index1,' - ); - - tokenTest( - ['method', 'PUT', 'url.slash', '/', 'url.part', 'index', 'url.slash', '/'], - 'PUT /index/' - ); - - tokenTest( - ['method', 'GET', 'url.part', 'index', 'url.slash', '/', 'url.part', '_search'], - 'GET index/_search ' - ); - - tokenTest(['method', 'PUT', 'url.slash', '/', 'url.part', 'index'], 'PUT /index'); - - tokenTest( - [ - 'method', - 'PUT', - 'url.slash', - '/', - 'url.part', - 'index1', - 'url.comma', - ',', - 'url.part', - 'index2', - 'url.slash', - '/', - 'url.part', - 'type1', - 'url.comma', - ',', - 'url.part', - 'type2', - ], - 'PUT /index1,index2/type1,type2' - ); - - tokenTest( - [ - 'method', - 'PUT', - 'url.slash', - '/', - 'url.part', - 'index1', - 'url.slash', - '/', - 'url.part', - 'type1', - 'url.comma', - ',', - 'url.part', - 'type2', - 'url.comma', - ',', - ], - 'PUT /index1/type1,type2,' - ); - - tokenTest( - [ - 'method', - 'PUT', - 'url.part', - 'index1', - 'url.comma', - ',', - 'url.part', - 'index2', - 'url.slash', - '/', - 'url.part', - 'type1', - 'url.comma', - ',', - 'url.part', - 'type2', - 'url.slash', - '/', - 'url.part', - '1234', - ], - 'PUT index1,index2/type1,type2/1234' - ); - - tokenTest( - [ - 'method', - 'POST', - 'url.part', - '_search', - 'paren.lparen', - '{', - 'variable', - '"q"', - 'punctuation.colon', - ':', - 'paren.lparen', - '{', - 'paren.rparen', - '}', - 'paren.rparen', - '}', - ], - 'POST _search\n' + '{\n' + ' "q": {}\n' + ' \n' + '}' - ); - - tokenTest( - [ - 'method', - 'POST', - 'url.part', - '_search', - 'paren.lparen', - '{', - 'variable', - '"q"', - 'punctuation.colon', - ':', - 'paren.lparen', - '{', - 'variable', - '"s"', - 'punctuation.colon', - ':', - 'paren.lparen', - '{', - 'paren.rparen', - '}', - 'paren.rparen', - '}', - 'paren.rparen', - '}', - ], - 'POST _search\n' + '{\n' + ' "q": { "s": {}}\n' + ' \n' + '}' - ); - - function statesAsList() { - const ret = []; - const maxLine = coreEditor.getLineCount(); - for (let line = 1; line <= maxLine; line++) ret.push(coreEditor.getLineState(line)); - return ret; - } - - function statesTest(statesList, prefix, data) { - if (data && typeof data !== 'string') { - data = JSON.stringify(data, null, 3); - } - if (data) { - if (prefix) { - data = prefix + '\n' + data; - } - } else { - data = prefix; - } - - test('States test ' + testCount++ + ' prefix: ' + prefix, async function() { - await coreEditor.setValue(data, true); - const modes = statesAsList(); - expect(modes).toEqual(statesList); - }); - } - - statesTest( - ['start', 'json', 'json', 'start'], - 'POST _search\n' + '{\n' + ' "query": { "match_all": {} }\n' + '}' - ); - - statesTest( - ['start', 'json', ['json', 'json'], ['json', 'json'], 'json', 'start'], - 'POST _search\n' + '{\n' + ' "query": { \n' + ' "match_all": {} \n' + ' }\n' + '}' - ); - - statesTest( - ['start', 'json', 'json', 'start'], - 'POST _search\n' + '{\n' + ' "script": { "source": "" }\n' + '}' - ); - - statesTest( - ['start', 'json', 'json', 'start'], - 'POST _search\n' + '{\n' + ' "script": ""\n' + '}' - ); - - statesTest( - ['start', 'json', ['json', 'json'], 'json', 'start'], - 'POST _search\n' + '{\n' + ' "script": {\n' + ' }\n' + '}' - ); - - statesTest( - [ - 'start', - 'json', - ['script-start', 'json', 'json', 'json'], - ['script-start', 'json', 'json', 'json'], - ['json', 'json'], - 'json', - 'start', - ], - 'POST _search\n' + - '{\n' + - ' "test": { "script": """\n' + - ' test script\n' + - ' """\n' + - ' }\n' + - '}' - ); - - statesTest( - ['start', 'json', ['script-start', 'json'], ['script-start', 'json'], 'json', 'start'], - 'POST _search\n' + '{\n' + ' "script": """\n' + ' test script\n' + ' """,\n' + '}' - ); - - statesTest( - ['start', 'json', 'json', 'start'], - 'POST _search\n' + '{\n' + ' "script": """test script""",\n' + '}' - ); - - statesTest( - ['start', 'json', ['string_literal', 'json'], ['string_literal', 'json'], 'json', 'start'], - 'POST _search\n' + '{\n' + ' "something": """\n' + ' test script\n' + ' """,\n' + '}' - ); - - statesTest( - [ - 'start', - 'json', - ['string_literal', 'json', 'json', 'json'], - ['string_literal', 'json', 'json', 'json'], - ['json', 'json'], - ['json', 'json'], - 'json', - 'start', - ], - 'POST _search\n' + - '{\n' + - ' "something": { "f" : """\n' + - ' test script\n' + - ' """,\n' + - ' "g": 1\n' + - ' }\n' + - '}' - ); - - statesTest( - ['start', 'json', 'json', 'start'], - 'POST _search\n' + '{\n' + ' "something": """test script""",\n' + '}' - ); -}); diff --git a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts index 19a86648d6dd3..47947e985092b 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts +++ b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts @@ -189,8 +189,9 @@ export class LegacyCoreEditor implements CoreEditor { } getLineCount() { - const text = this.getValue(); - return text.split('\n').length; + // Only use this function to return line count as it uses + // a cache. + return this.editor.getSession().getLength(); } addMarker(range: Range) { diff --git a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts index 9679eaa2884ce..1271f167c6cc1 100644 --- a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts +++ b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts @@ -78,7 +78,7 @@ export class SenseEditor { } else { curRow = rowOrPos as number; } - const maxLines = this.coreEditor.getValue().split('\n').length; + const maxLines = this.coreEditor.getLineCount(); for (; curRow < maxLines - 1; curRow++) { if (this.parser.isStartRequestRow(curRow, this.coreEditor)) { break; diff --git a/src/plugins/console/public/lib/ace_token_provider/token_provider.ts b/src/plugins/console/public/lib/ace_token_provider/token_provider.ts index 761eb1d206cfe..134ab6c0e82d5 100644 --- a/src/plugins/console/public/lib/ace_token_provider/token_provider.ts +++ b/src/plugins/console/public/lib/ace_token_provider/token_provider.ts @@ -66,7 +66,10 @@ export class AceTokensProvider implements TokensProvider { getTokens(lineNumber: number): Token[] | null { if (lineNumber < 1) return null; - const lineCount = this.session.doc.getAllLines().length; + // Important: must use a .session.getLength because this is a cached value. + // Calculating line length here will lead to performance issues because this function + // may be called inside of tight loops. + const lineCount = this.session.getLength(); if (lineNumber > lineCount) { return null; } diff --git a/src/plugins/console/public/types/core_editor.ts b/src/plugins/console/public/types/core_editor.ts index 8de4c78333fee..79dc3ca74200b 100644 --- a/src/plugins/console/public/types/core_editor.ts +++ b/src/plugins/console/public/types/core_editor.ts @@ -181,6 +181,10 @@ export interface CoreEditor { /** * Return the current line count in the buffer. + * + * @remark + * This function should be usable in a tight loop and must make used of a cached + * line count. */ getLineCount(): number; diff --git a/src/plugins/kibana_legacy/common/kbn_base_url.ts b/src/plugins/kibana_legacy/common/kbn_base_url.ts new file mode 100644 index 0000000000000..69711626750ea --- /dev/null +++ b/src/plugins/kibana_legacy/common/kbn_base_url.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export const kbnBaseUrl = '/app/kibana'; diff --git a/src/plugins/kibana_legacy/public/angular/angular_config.tsx b/src/plugins/kibana_legacy/public/angular/angular_config.tsx index 9a33cff82ed63..67d62cab7409b 100644 --- a/src/plugins/kibana_legacy/public/angular/angular_config.tsx +++ b/src/plugins/kibana_legacy/public/angular/angular_config.tsx @@ -31,7 +31,7 @@ import $ from 'jquery'; import { cloneDeep, forOwn, get, set } from 'lodash'; import React, { Fragment } from 'react'; import * as Rx from 'rxjs'; -import { ChromeBreadcrumb } from 'kibana/public'; +import { ChromeBreadcrumb, EnvironmentMode, PackageInfo } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -79,34 +79,53 @@ function isDummyRoute($route: any, isLocalAngular: boolean) { export const configureAppAngularModule = ( angularModule: IModule, - newPlatform: LegacyCoreStart, + newPlatform: + | LegacyCoreStart + | { + core: CoreStart; + readonly env: { + mode: Readonly; + packageInfo: Readonly; + }; + }, isLocalAngular: boolean ) => { - const legacyMetadata = newPlatform.injectedMetadata.getLegacyMetadata(); - - forOwn(newPlatform.injectedMetadata.getInjectedVars(), (val, name) => { - if (name !== undefined) { - // The legacy platform modifies some of these values, clone to an unfrozen object. - angularModule.value(name, cloneDeep(val)); - } - }); + const core = 'core' in newPlatform ? newPlatform.core : newPlatform; + const packageInfo = + 'injectedMetadata' in newPlatform + ? newPlatform.injectedMetadata.getLegacyMetadata() + : newPlatform.env.packageInfo; + + if ('injectedMetadata' in newPlatform) { + forOwn(newPlatform.injectedMetadata.getInjectedVars(), (val, name) => { + if (name !== undefined) { + // The legacy platform modifies some of these values, clone to an unfrozen object. + angularModule.value(name, cloneDeep(val)); + } + }); + } angularModule - .value('kbnVersion', newPlatform.injectedMetadata.getKibanaVersion()) - .value('buildNum', legacyMetadata.buildNum) - .value('buildSha', legacyMetadata.buildSha) - .value('serverName', legacyMetadata.serverName) - .value('esUrl', getEsUrl(newPlatform)) - .value('uiCapabilities', newPlatform.application.capabilities) - .config(setupCompileProvider(newPlatform)) + .value('kbnVersion', packageInfo.version) + .value('buildNum', packageInfo.buildNum) + .value('buildSha', packageInfo.buildSha) + .value('esUrl', getEsUrl(core)) + .value('uiCapabilities', core.application.capabilities) + .config( + setupCompileProvider( + 'injectedMetadata' in newPlatform + ? newPlatform.injectedMetadata.getLegacyMetadata().devMode + : newPlatform.env.mode.dev + ) + ) .config(setupLocationProvider()) - .config($setupXsrfRequestInterceptor(newPlatform)) - .run(capture$httpLoadingCount(newPlatform)) - .run($setupBreadcrumbsAutoClear(newPlatform, isLocalAngular)) - .run($setupBadgeAutoClear(newPlatform, isLocalAngular)) - .run($setupHelpExtensionAutoClear(newPlatform, isLocalAngular)) - .run($setupUrlOverflowHandling(newPlatform, isLocalAngular)) - .run($setupUICapabilityRedirect(newPlatform)); + .config($setupXsrfRequestInterceptor(packageInfo.version)) + .run(capture$httpLoadingCount(core)) + .run($setupBreadcrumbsAutoClear(core, isLocalAngular)) + .run($setupBadgeAutoClear(core, isLocalAngular)) + .run($setupHelpExtensionAutoClear(core, isLocalAngular)) + .run($setupUrlOverflowHandling(core, isLocalAngular)) + .run($setupUICapabilityRedirect(core)); }; const getEsUrl = (newPlatform: CoreStart) => { @@ -122,10 +141,8 @@ const getEsUrl = (newPlatform: CoreStart) => { }; }; -const setupCompileProvider = (newPlatform: LegacyCoreStart) => ( - $compileProvider: ICompileProvider -) => { - if (!newPlatform.injectedMetadata.getLegacyMetadata().devMode) { +const setupCompileProvider = (devMode: boolean) => ($compileProvider: ICompileProvider) => { + if (!devMode) { $compileProvider.debugInfoEnabled(false); } }; @@ -140,9 +157,7 @@ const setupLocationProvider = () => ($locationProvider: ILocationProvider) => { $locationProvider.hashPrefix(''); }; -export const $setupXsrfRequestInterceptor = (newPlatform: LegacyCoreStart) => { - const version = newPlatform.injectedMetadata.getLegacyMetadata().version; - +export const $setupXsrfRequestInterceptor = (version: string) => { // Configure jQuery prefilter $.ajaxPrefilter(({ kbnXsrfToken = true }: any, originalOptions, jqXHR) => { if (kbnXsrfToken) { diff --git a/src/plugins/kibana_legacy/public/index.ts b/src/plugins/kibana_legacy/public/index.ts index 19833d638fe4c..18f01854de259 100644 --- a/src/plugins/kibana_legacy/public/index.ts +++ b/src/plugins/kibana_legacy/public/index.ts @@ -24,6 +24,7 @@ export const plugin = (initializerContext: PluginInitializerContext) => new KibanaLegacyPlugin(initializerContext); export * from './plugin'; +export { kbnBaseUrl } from '../common/kbn_base_url'; export { initAngularBootstrap } from './angular_bootstrap'; export * from './angular'; diff --git a/src/plugins/kibana_legacy/public/mocks.ts b/src/plugins/kibana_legacy/public/mocks.ts index aab3ab315f0c6..8e9a05b186191 100644 --- a/src/plugins/kibana_legacy/public/mocks.ts +++ b/src/plugins/kibana_legacy/public/mocks.ts @@ -17,6 +17,7 @@ * under the License. */ +import { EnvironmentMode, PackageInfo } from 'kibana/server'; import { KibanaLegacyPlugin } from './plugin'; export type Setup = jest.Mocked>; @@ -28,6 +29,10 @@ const createSetupContract = (): Setup => ({ config: { defaultAppId: 'home', }, + env: {} as { + mode: Readonly; + packageInfo: Readonly; + }, }); const createStartContract = (): Start => ({ diff --git a/src/plugins/kibana_legacy/public/plugin.ts b/src/plugins/kibana_legacy/public/plugin.ts index 86e56c44646c0..2ad620f355848 100644 --- a/src/plugins/kibana_legacy/public/plugin.ts +++ b/src/plugins/kibana_legacy/public/plugin.ts @@ -107,7 +107,17 @@ export class KibanaLegacyPlugin { this.forwards.push({ legacyAppId, newAppId, ...options }); }, + /** + * @deprecated + * The `defaultAppId` config key is temporarily exposed to be used in the legacy platform. + * As this setting is going away, no new code should depend on it. + */ config: this.initializerContext.config.get(), + /** + * @deprecated + * Temporarily exposing the NP env to simulate initializer contexts in the LP. + */ + env: this.initializerContext.env, }; } diff --git a/src/plugins/kibana_legacy/public/utils/system_api.ts b/src/plugins/kibana_legacy/public/utils/system_api.ts index 397de4dcc2bb3..49d4a78584737 100644 --- a/src/plugins/kibana_legacy/public/utils/system_api.ts +++ b/src/plugins/kibana_legacy/public/utils/system_api.ts @@ -19,7 +19,8 @@ import { IRequestConfig } from 'angular'; -const SYSTEM_API_HEADER_NAME = 'kbn-system-api'; +const SYSTEM_REQUEST_HEADER_NAME = 'kbn-system-request'; +const LEGACY_SYSTEM_API_HEADER_NAME = 'kbn-system-api'; /** * Adds a custom header designating request as system API @@ -28,7 +29,7 @@ const SYSTEM_API_HEADER_NAME = 'kbn-system-api'; */ export function addSystemApiHeader(originalHeaders: Record) { const systemApiHeaders = { - [SYSTEM_API_HEADER_NAME]: true, + [SYSTEM_REQUEST_HEADER_NAME]: true, }; return { ...originalHeaders, @@ -44,5 +45,7 @@ export function addSystemApiHeader(originalHeaders: Record) { */ export function isSystemApiRequest(request: IRequestConfig) { const { headers } = request; - return headers && !!headers[SYSTEM_API_HEADER_NAME]; + return ( + headers && (!!headers[SYSTEM_REQUEST_HEADER_NAME] || !!headers[LEGACY_SYSTEM_API_HEADER_NAME]) + ); } diff --git a/src/plugins/kibana_legacy/server/index.ts b/src/plugins/kibana_legacy/server/index.ts index 4d0fe8364a66c..98c754795e947 100644 --- a/src/plugins/kibana_legacy/server/index.ts +++ b/src/plugins/kibana_legacy/server/index.ts @@ -32,6 +32,8 @@ export const config: PluginConfigDescriptor = { ], }; +export { kbnBaseUrl } from '../common/kbn_base_url'; + class Plugin { public setup(core: CoreSetup) {} diff --git a/src/plugins/vis_type_timeseries/server/routes/post_vis_schema.ts b/src/plugins/vis_type_timeseries/server/routes/post_vis_schema.ts index 3aca50b5b4710..7893ad456e83b 100644 --- a/src/plugins/vis_type_timeseries/server/routes/post_vis_schema.ts +++ b/src/plugins/vis_type_timeseries/server/routes/post_vis_schema.ts @@ -32,11 +32,11 @@ const numberIntegerRequired = Joi.number() .integer() .required(); const numberOptional = Joi.number().optional(); -const numberRequired = Joi.number().required(); const queryObject = Joi.object({ language: Joi.string().allow(''), query: Joi.string().allow(''), }); +const numberOptionalOrEmptyString = Joi.alternatives(numberOptional, Joi.string().valid('')); const annotationsItems = Joi.object({ color: stringOptionalNullable, @@ -74,6 +74,16 @@ const metricsItems = Joi.object({ numerator: stringOptionalNullable, denominator: stringOptionalNullable, sigma: stringOptionalNullable, + unit: stringOptionalNullable, + model_type: stringOptionalNullable, + mode: stringOptionalNullable, + lag: numberOptional, + alpha: numberOptional, + beta: numberOptional, + gamma: numberOptional, + period: numberOptional, + multiplicative: Joi.boolean(), + window: numberOptional, function: stringOptionalNullable, script: stringOptionalNullable, variables: Joi.array() @@ -121,7 +131,7 @@ const seriesItems = Joi.object({ }) ) .optional(), - fill: numberOptional, + fill: numberOptionalOrEmptyString, filter: Joi.object({ query: stringRequired, language: stringOptionalNullable, @@ -131,11 +141,11 @@ const seriesItems = Joi.object({ hidden: Joi.boolean().optional(), id: stringRequired, label: stringOptionalNullable, - line_width: numberOptional, + line_width: numberOptionalOrEmptyString, metrics: Joi.array().items(metricsItems), offset_time: stringOptionalNullable, override_index_pattern: numberOptional, - point_size: numberRequired, + point_size: numberOptionalOrEmptyString, separate_axis: numberIntegerOptional, seperate_axis: numberIntegerOptional, series_index_pattern: stringOptionalNullable, diff --git a/src/legacy/core_plugins/kibana/server/lib/system_api.js b/src/plugins/vis_type_vega/config.ts similarity index 67% rename from src/legacy/core_plugins/kibana/server/lib/system_api.js rename to src/plugins/vis_type_vega/config.ts index 3e2ab667dd98b..c03e86c0a3569 100644 --- a/src/legacy/core_plugins/kibana/server/lib/system_api.js +++ b/src/plugins/vis_type_vega/config.ts @@ -17,15 +17,11 @@ * under the License. */ -const SYSTEM_API_HEADER_NAME = 'kbn-system-api'; +import { schema, TypeOf } from '@kbn/config-schema'; -/** - * Checks on the *server-side*, if an HTTP request is a system API request - * - * @param request HAPI request object - * @return true if request is a system API request; false, otherwise - * @deprecated Use KibanaRequest#isSystemApi - */ -export function isSystemApiRequest(request) { - return !!request.headers[SYSTEM_API_HEADER_NAME]; -} +export const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: true }), + enableExternalUrls: schema.boolean({ defaultValue: false }), +}); + +export type ConfigSchema = TypeOf; diff --git a/src/plugins/vis_type_vega/kibana.json b/src/plugins/vis_type_vega/kibana.json new file mode 100644 index 0000000000000..6bfd6c9536df4 --- /dev/null +++ b/src/plugins/vis_type_vega/kibana.json @@ -0,0 +1,6 @@ +{ + "id": "visTypeVega", + "version": "kibana", + "server": true, + "ui": true +} diff --git a/src/plugins/vis_type_vega/public/index.ts b/src/plugins/vis_type_vega/public/index.ts new file mode 100644 index 0000000000000..71f3474f8217e --- /dev/null +++ b/src/plugins/vis_type_vega/public/index.ts @@ -0,0 +1,38 @@ +/* + * 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 { PluginInitializerContext } from 'kibana/public'; +import { ConfigSchema } from '../config'; + +export const plugin = (initializerContext: PluginInitializerContext) => ({ + setup() { + return { + /** + * The configuration is temporarily exposed to allow the legacy vega plugin to consume + * the setting. Once the vega plugin is migrated completely, this will become an implementation + * detail. + * @deprecated + */ + config: initializerContext.config.get(), + }; + }, + start() {}, +}); + +export type VisTypeVegaSetup = ReturnType['setup']>; diff --git a/src/legacy/core_plugins/kibana/server/lib/__tests__/system_api.js b/src/plugins/vis_type_vega/server/index.ts similarity index 57% rename from src/legacy/core_plugins/kibana/server/lib/__tests__/system_api.js rename to src/plugins/vis_type_vega/server/index.ts index a63a93f3a70d5..4c809ff3c5a93 100644 --- a/src/legacy/core_plugins/kibana/server/lib/__tests__/system_api.js +++ b/src/plugins/vis_type_vega/server/index.ts @@ -17,25 +17,22 @@ * under the License. */ -import expect from '@kbn/expect'; -import { isSystemApiRequest } from '../system_api'; +import { PluginConfigDescriptor } from 'kibana/server'; -describe('system_api', () => { - describe('#isSystemApiRequest', () => { - it('returns true for a system API HTTP request', () => { - const mockHapiRequest = { - headers: { - 'kbn-system-api': true, - }, - }; - expect(isSystemApiRequest(mockHapiRequest)).to.be(true); - }); +import { configSchema, ConfigSchema } from '../config'; - it('returns false for a non-system API HTTP request', () => { - const mockHapiRequest = { - headers: {}, - }; - expect(isSystemApiRequest(mockHapiRequest)).to.be(false); - }); - }); +export const config: PluginConfigDescriptor = { + exposeToBrowser: { + enableExternalUrls: true, + }, + schema: configSchema, + deprecations: ({ renameFromRoot }) => [ + renameFromRoot('vega.enableExternalUrls', 'vis_type_vega.enableExternalUrls'), + renameFromRoot('vega.enabled', 'vis_type_vega.enabled'), + ], +}; + +export const plugin = () => ({ + setup() {}, + start() {}, }); diff --git a/test/functional/apps/context/_discover_navigation.js b/test/functional/apps/context/_discover_navigation.js index aabce6baa8783..b906296037888 100644 --- a/test/functional/apps/context/_discover_navigation.js +++ b/test/functional/apps/context/_discover_navigation.js @@ -39,12 +39,10 @@ export default function({ getService, getPageObjects }) { await Promise.all( TEST_COLUMN_NAMES.map(columnName => PageObjects.discover.clickFieldListItemAdd(columnName)) ); - await Promise.all( - TEST_FILTER_COLUMN_NAMES.map(async ([columnName, value]) => { - await PageObjects.discover.clickFieldListItem(columnName); - await PageObjects.discover.clickFieldListPlusFilter(columnName, value); - }) - ); + for (const [columnName, value] of TEST_FILTER_COLUMN_NAMES) { + await PageObjects.discover.clickFieldListItem(columnName); + await PageObjects.discover.clickFieldListPlusFilter(columnName, value); + } }); it('should open the context view with the selected document as anchor', async function() { diff --git a/test/functional/apps/home/_sample_data.ts b/test/functional/apps/home/_sample_data.ts index 8088b5a0f9da9..8bc528e045566 100644 --- a/test/functional/apps/home/_sample_data.ts +++ b/test/functional/apps/home/_sample_data.ts @@ -84,7 +84,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { }); it('should launch sample flights data set dashboard', async () => { - await PageObjects.home.launchSampleDataSet('flights'); + await PageObjects.home.launchSampleDashboard('flights'); await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); const todayYearMonthDay = moment().format('MMM D, YYYY'); @@ -96,7 +96,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { }); it('should render visualizations', async () => { - await PageObjects.home.launchSampleDataSet('flights'); + await PageObjects.home.launchSampleDashboard('flights'); await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); log.debug('Checking pie charts rendered'); @@ -115,7 +115,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { }); it('should launch sample logs data set dashboard', async () => { - await PageObjects.home.launchSampleDataSet('logs'); + await PageObjects.home.launchSampleDashboard('logs'); await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); const todayYearMonthDay = moment().format('MMM D, YYYY'); @@ -127,7 +127,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { }); it('should launch sample ecommerce data set dashboard', async () => { - await PageObjects.home.launchSampleDataSet('ecommerce'); + await PageObjects.home.launchSampleDashboard('ecommerce'); await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); const todayYearMonthDay = moment().format('MMM D, YYYY'); diff --git a/test/functional/page_objects/home_page.ts b/test/functional/page_objects/home_page.ts index a641fbda023c3..6225b4e3aca62 100644 --- a/test/functional/page_objects/home_page.ts +++ b/test/functional/page_objects/home_page.ts @@ -19,9 +19,12 @@ import { FtrProviderContext } from '../ftr_provider_context'; -export function HomePageProvider({ getService }: FtrProviderContext) { +export function HomePageProvider({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const retry = getService('retry'); + const find = getService('find'); + const PageObjects = getPageObjects(['common']); + let isOss = true; class HomePage { async clickSynopsis(title: string) { @@ -63,6 +66,14 @@ export function HomePageProvider({ getService }: FtrProviderContext) { }); } + async launchSampleDashboard(id: string) { + await this.launchSampleDataSet(id); + isOss = await PageObjects.common.isOss(); + if (!isOss) { + await find.clickByLinkText('Dashboard'); + } + } + async launchSampleDataSet(id: string) { await this.addSampleDataSet(id); await testSubjects.click(`launchSampleDataSet${id}`); diff --git a/x-pack/legacy/plugins/graph/index.ts b/x-pack/legacy/plugins/graph/index.ts index 143d07cfdbd57..b2d6fd3957d64 100644 --- a/x-pack/legacy/plugins/graph/index.ts +++ b/x-pack/legacy/plugins/graph/index.ts @@ -44,14 +44,6 @@ export const graph: LegacyPluginInitializer = kibana => { }, init(server) { - server.injectUiAppVars('graph', () => { - const config = server.config(); - return { - graphSavePolicy: config.get('xpack.graph.savePolicy'), - canEditDrillDownUrls: config.get('xpack.graph.canEditDrillDownUrls'), - }; - }); - server.plugins.xpack_main.registerFeature({ id: 'graph', name: i18n.translate('xpack.graph.featureRegistry.graphFeatureName', { diff --git a/x-pack/legacy/plugins/graph/public/index.ts b/x-pack/legacy/plugins/graph/public/index.ts index d9854acb9332c..fb60a66fb28cc 100644 --- a/x-pack/legacy/plugins/graph/public/index.ts +++ b/x-pack/legacy/plugins/graph/public/index.ts @@ -7,9 +7,11 @@ import { npSetup, npStart } from 'ui/new_platform'; import { LicensingPluginSetup } from '../../../../plugins/licensing/public'; import { GraphPlugin } from './plugin'; +import { GraphSetup } from '../../../../plugins/graph/public'; type XpackNpSetupDeps = typeof npSetup.plugins & { licensing: LicensingPluginSetup; + graph: GraphSetup; }; (async () => { diff --git a/x-pack/legacy/plugins/graph/public/plugin.ts b/x-pack/legacy/plugins/graph/public/plugin.ts index b4ca4bf423181..4ccaf6b5dfa27 100644 --- a/x-pack/legacy/plugins/graph/public/plugin.ts +++ b/x-pack/legacy/plugins/graph/public/plugin.ts @@ -5,12 +5,19 @@ */ // NP type imports -import { CoreSetup, CoreStart, Plugin, SavedObjectsClientContract } from 'src/core/public'; +import { + AppMountParameters, + CoreSetup, + CoreStart, + Plugin, + SavedObjectsClientContract, +} from 'src/core/public'; import { Plugin as DataPlugin } from 'src/plugins/data/public'; import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { LicensingPluginSetup } from '../../../../plugins/licensing/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../../src/plugins/navigation/public'; import { initAngularBootstrap } from '../../../../../src/plugins/kibana_legacy/public'; +import { GraphSetup } from '../../../../plugins/graph/public'; export interface GraphPluginStartDependencies { npData: ReturnType; @@ -19,6 +26,7 @@ export interface GraphPluginStartDependencies { export interface GraphPluginSetupDependencies { licensing: LicensingPluginSetup; + graph: GraphSetup; } export class GraphPlugin implements Plugin { @@ -26,12 +34,13 @@ export class GraphPlugin implements Plugin { private npDataStart: ReturnType | null = null; private savedObjectsClient: SavedObjectsClientContract | null = null; - setup(core: CoreSetup, { licensing }: GraphPluginSetupDependencies) { + setup(core: CoreSetup, { licensing, graph }: GraphPluginSetupDependencies) { initAngularBootstrap(); core.application.register({ id: 'graph', title: 'Graph', - mount: async ({ core: contextCore }, params) => { + mount: async (params: AppMountParameters) => { + const [coreStart] = await core.getStartServices(); const { renderApp } = await import('./application'); return renderApp({ ...params, @@ -41,18 +50,16 @@ export class GraphPlugin implements Plugin { savedObjectsClient: this.savedObjectsClient!, addBasePath: core.http.basePath.prepend, getBasePath: core.http.basePath.get, - canEditDrillDownUrls: core.injectedMetadata.getInjectedVar( - 'canEditDrillDownUrls' - ) as boolean, - graphSavePolicy: core.injectedMetadata.getInjectedVar('graphSavePolicy') as string, + canEditDrillDownUrls: graph.config.canEditDrillDownUrls, + graphSavePolicy: graph.config.savePolicy, storage: new Storage(window.localStorage), - capabilities: contextCore.application.capabilities.graph, - coreStart: contextCore, - chrome: contextCore.chrome, - config: contextCore.uiSettings, - toastNotifications: contextCore.notifications.toasts, + capabilities: coreStart.application.capabilities.graph, + coreStart, + chrome: coreStart.chrome, + config: coreStart.uiSettings, + toastNotifications: coreStart.notifications.toasts, indexPatterns: this.npDataStart!.indexPatterns, - overlays: contextCore.overlays, + overlays: coreStart.overlays, }); }, }); diff --git a/x-pack/legacy/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js b/x-pack/legacy/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js index 3c639239757db..bafb12de068bb 100644 --- a/x-pack/legacy/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js +++ b/x-pack/legacy/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js @@ -225,8 +225,8 @@ class TimeseriesChartIntl extends Component { this.renderFocusChart(); } - componentDidUpdate() { - if (this.props.renderFocusChartOnly === false) { + componentDidUpdate(prevProps) { + if (this.props.renderFocusChartOnly === false || prevProps.svgWidth !== this.props.svgWidth) { this.renderChart(); this.drawContextChartSelection(); } @@ -424,11 +424,8 @@ class TimeseriesChartIntl extends Component { } focusLoadTo = Math.min(focusLoadTo, contextXMax); - const brushVisibility = focusLoadFrom !== contextXMin || focusLoadTo !== contextXMax; - this.setBrushVisibility(brushVisibility); - if (focusLoadFrom !== contextXMin || focusLoadTo !== contextXMax) { - this.setContextBrushExtent(new Date(focusLoadFrom), new Date(focusLoadTo), true); + this.setContextBrushExtent(new Date(focusLoadFrom), new Date(focusLoadTo)); const newSelectedBounds = { min: moment(new Date(focusLoadFrom)), max: moment(focusLoadFrom), @@ -442,6 +439,10 @@ class TimeseriesChartIntl extends Component { }; if (!_.isEqual(newSelectedBounds, this.selectedBounds)) { this.selectedBounds = newSelectedBounds; + this.setContextBrushExtent( + new Date(contextXScaleDomain[0]), + new Date(contextXScaleDomain[1]) + ); if (this.contextChartInitialized === false) { this.contextChartInitialized = true; contextChartSelected({ from: contextXScaleDomain[0], to: contextXScaleDomain[1] }); @@ -1178,36 +1179,29 @@ class TimeseriesChartIntl extends Component { '
' ); - const showBrush = show => { - if (show === true) { - const brushExtent = brush.extent(); - mask.reveal(brushExtent); - leftHandle.attr('x', contextXScale(brushExtent[0]) - 10); - rightHandle.attr('x', contextXScale(brushExtent[1]) + 0); - - topBorder.attr('x', contextXScale(brushExtent[0]) + 1); - // Use Math.max(0, ...) to make sure we don't end up - // with a negative width which would cause an SVG error. - topBorder.attr( - 'width', - Math.max(0, contextXScale(brushExtent[1]) - contextXScale(brushExtent[0]) - 2) - ); - } - - this.setBrushVisibility(show); - }; - - showBrush(!brush.empty()); - function brushing() { + const brushExtent = brush.extent(); + mask.reveal(brushExtent); + leftHandle.attr('x', contextXScale(brushExtent[0]) - 10); + rightHandle.attr('x', contextXScale(brushExtent[1]) + 0); + + topBorder.attr('x', contextXScale(brushExtent[0]) + 1); + // Use Math.max(0, ...) to make sure we don't end up + // with a negative width which would cause an SVG error. + const topBorderWidth = Math.max( + 0, + contextXScale(brushExtent[1]) - contextXScale(brushExtent[0]) - 2 + ); + topBorder.attr('width', topBorderWidth); + const isEmpty = brush.empty(); - showBrush(!isEmpty); + d3.selectAll('.brush-handle').style('visibility', isEmpty ? 'hidden' : 'visible'); } + brushing(); const that = this; function brushed() { const isEmpty = brush.empty(); - const selectedBounds = isEmpty ? contextXScale.domain() : brush.extent(); const selectionMin = selectedBounds[0].getTime(); const selectionMax = selectedBounds[1].getTime(); @@ -1221,8 +1215,6 @@ class TimeseriesChartIntl extends Component { return; } - showBrush(!isEmpty); - // Set the color of the swimlane cells according to whether they are inside the selection. contextGroup.selectAll('.swimlane-cell').style('fill', d => { const cellMs = d.date.getTime(); @@ -1238,26 +1230,6 @@ class TimeseriesChartIntl extends Component { } }; - setBrushVisibility = show => { - const mask = this.mask; - - if (mask !== undefined) { - const visibility = show ? 'visible' : 'hidden'; - mask.style('visibility', visibility); - - d3.selectAll('.brush').style('visibility', visibility); - - const brushHandles = d3.selectAll('.brush-handle-inner'); - brushHandles.style('visibility', visibility); - - const topBorder = d3.selectAll('.top-border'); - topBorder.style('visibility', visibility); - - const border = d3.selectAll('.chart-border-highlight'); - border.style('visibility', visibility); - } - }; - drawSwimlane = (swlGroup, swlWidth, swlHeight) => { const { contextAggregationInterval, swimlaneData } = this.props; @@ -1368,21 +1340,18 @@ class TimeseriesChartIntl extends Component { // Sets the extent of the brush on the context chart to the // supplied from and to Date objects. - setContextBrushExtent = (from, to, fireEvent) => { + setContextBrushExtent = (from, to) => { const brush = this.brush; const brushExtent = brush.extent(); const newExtent = [from, to]; - if ( - newExtent[0].getTime() === brushExtent[0].getTime() && - newExtent[1].getTime() === brushExtent[1].getTime() - ) { - fireEvent = false; - } - brush.extent(newExtent); brush(d3.select('.brush')); - if (fireEvent) { + + if ( + newExtent[0].getTime() !== brushExtent[0].getTime() || + newExtent[1].getTime() !== brushExtent[1].getTime() + ) { brush.event(d3.select('.brush')); } }; @@ -1403,7 +1372,7 @@ class TimeseriesChartIntl extends Component { to = Math.min(minBoundsMs + millis, maxBoundsMs); } - this.setContextBrushExtent(new Date(from), new Date(to), true); + this.setContextBrushExtent(new Date(from), new Date(to)); } showFocusChartTooltip(marker, circle) { diff --git a/x-pack/legacy/plugins/security/index.js b/x-pack/legacy/plugins/security/index.js index 2542a80391013..dd7459054b3c0 100644 --- a/x-pack/legacy/plugins/security/index.js +++ b/x-pack/legacy/plugins/security/index.js @@ -143,9 +143,6 @@ export const security = kibana => hostname: config.get('server.host'), port: config.get('server.port'), }, - isSystemAPIRequest: server.plugins.kibana.systemApi.isSystemApiRequest.bind( - server.plugins.kibana.systemApi - ), }); // Legacy xPack Info endpoint returns whatever we return in a callback for `registerLicenseCheckResultsGenerator` diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/index.ts b/x-pack/legacy/plugins/uptime/public/components/connected/index.ts index 13309acd03622..585f0bf7f25f5 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/connected/index.ts @@ -10,3 +10,5 @@ export { KueryBar } from './kuerybar/kuery_bar_container'; export { FilterGroup } from './filter_group/filter_group_container'; export { MonitorStatusDetails } from './monitor/status_details_container'; export { MonitorStatusBar } from './monitor/status_bar_container'; +export { MonitorListDrawer } from './monitor/list_drawer_container'; +export { MonitorListActionsPopover } from './monitor/drawer_popover_container'; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/drawer_popover_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/drawer_popover_container.tsx new file mode 100644 index 0000000000000..be29e12f716a9 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/drawer_popover_container.tsx @@ -0,0 +1,26 @@ +/* + * 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 { connect } from 'react-redux'; +import { AppState } from '../../../state'; +import { isIntegrationsPopupOpen } from '../../../state/selectors'; +import { PopoverState, toggleIntegrationsPopover } from '../../../state/actions'; +import { MonitorListActionsPopoverComponent } from '../../functional/monitor_list/monitor_list_drawer'; + +const mapStateToProps = (state: AppState) => ({ + popoverState: isIntegrationsPopupOpen(state), +}); + +const mapDispatchToProps = (dispatch: any) => ({ + togglePopoverIsVisible: (popoverState: PopoverState) => { + return dispatch(toggleIntegrationsPopover(popoverState)); + }, +}); + +export const MonitorListActionsPopover = connect( + mapStateToProps, + mapDispatchToProps +)(MonitorListActionsPopoverComponent); diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/list_drawer_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/list_drawer_container.tsx new file mode 100644 index 0000000000000..8c670b485cc56 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/list_drawer_container.tsx @@ -0,0 +1,49 @@ +/* + * 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, { useEffect } from 'react'; +import { connect } from 'react-redux'; +import { AppState } from '../../../state'; +import { getMonitorDetails } from '../../../state/selectors'; +import { MonitorDetailsActionPayload } from '../../../state/actions/types'; +import { fetchMonitorDetails } from '../../../state/actions/monitor'; +import { MonitorListDrawerComponent } from '../../functional/monitor_list/monitor_list_drawer/monitor_list_drawer'; +import { useUrlParams } from '../../../hooks'; +import { MonitorSummary } from '../../../../common/graphql/types'; +import { MonitorDetails } from '../../../../common/runtime_types/monitor'; + +interface ContainerProps { + summary: MonitorSummary; + monitorDetails: MonitorDetails; + loadMonitorDetails: typeof fetchMonitorDetails; +} + +const Container: React.FC = ({ summary, loadMonitorDetails, monitorDetails }) => { + const monitorId = summary?.monitor_id; + + const [getUrlParams] = useUrlParams(); + const { dateRangeStart: dateStart, dateRangeEnd: dateEnd } = getUrlParams(); + + useEffect(() => { + loadMonitorDetails({ + dateStart, + dateEnd, + monitorId, + }); + }, [dateStart, dateEnd, monitorId, loadMonitorDetails]); + return ; +}; + +const mapStateToProps = (state: AppState, { summary }: any) => ({ + monitorDetails: getMonitorDetails(state, summary), +}); + +const mapDispatchToProps = (dispatch: any) => ({ + loadMonitorDetails: (actionPayload: MonitorDetailsActionPayload) => + dispatch(fetchMonitorDetails(actionPayload)), +}); + +export const MonitorListDrawer = connect(mapStateToProps, mapDispatchToProps)(Container); diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx index db6337732091a..b2b555d32a3c7 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx @@ -31,7 +31,7 @@ interface OwnProps { type Props = OwnProps & StateProps & DispatchProps; -export const Container: React.FC = ({ +const Container: React.FC = ({ loadMonitorStatus, monitorId, monitorStatus, diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/ping_histogram.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/ping_histogram.test.tsx.snap index 8ee4dc3575469..db07faae44272 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/ping_histogram.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/ping_histogram.test.tsx.snap @@ -1,48 +1,248 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`PingHistogram component renders the component without errors 1`] = ` - - -
- , +
+
+
+
-
-
- +
+

+ No data to display +

+
+
+
+
, +] +`; + +exports[`PingHistogram component shallow renders the component without errors 1`] = ` + + - - -

- } - title={ - -
- -
-
- } - /> - - + /> +
`; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/ping_histogram.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/ping_histogram.test.tsx index de7cfc86abc0c..21c1fa86eeee4 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/ping_histogram.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/ping_histogram.test.tsx @@ -5,17 +5,51 @@ */ import React from 'react'; -import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import { PingHistogramComponent, PingHistogramComponentProps } from '../ping_histogram'; +import { renderWithRouter, shallowWithRouter } from '../../../../lib'; describe('PingHistogram component', () => { const props: PingHistogramComponentProps = { absoluteStartDate: 1548697920000, absoluteEndDate: 1548700920000, + data: { + histogram: [ + { x: 1581068329000, downCount: 6, upCount: 33, y: 1 }, + { x: 1581068359000, downCount: 6, upCount: 30, y: 1 }, + { x: 1581068389000, downCount: 6, upCount: 33, y: 1 }, + { x: 1581068419000, downCount: 6, upCount: 30, y: 1 }, + { x: 1581068449000, downCount: 6, upCount: 33, y: 1 }, + { x: 1581068479000, downCount: 6, upCount: 30, y: 1 }, + { x: 1581068509000, downCount: 6, upCount: 33, y: 1 }, + { x: 1581068539000, downCount: 6, upCount: 30, y: 1 }, + { x: 1581068569000, downCount: 6, upCount: 33, y: 1 }, + { x: 1581068599000, downCount: 6, upCount: 30, y: 1 }, + { x: 1581068629000, downCount: 6, upCount: 33, y: 1 }, + { x: 1581068659000, downCount: 6, upCount: 30, y: 1 }, + { x: 1581068689000, downCount: 6, upCount: 33, y: 1 }, + { x: 1581068719000, downCount: 6, upCount: 30, y: 1 }, + { x: 1581068749000, downCount: 5, upCount: 34, y: 1 }, + { x: 1581068779000, downCount: 3, upCount: 33, y: 1 }, + { x: 1581068809000, downCount: 3, upCount: 36, y: 1 }, + { x: 1581068839000, downCount: 3, upCount: 33, y: 1 }, + { x: 1581068869000, downCount: 3, upCount: 36, y: 1 }, + { x: 1581068899000, downCount: 3, upCount: 33, y: 1 }, + { x: 1581068929000, downCount: 3, upCount: 36, y: 1 }, + { x: 1581068959000, downCount: 3, upCount: 33, y: 1 }, + { x: 1581068989000, downCount: 3, upCount: 36, y: 1 }, + { x: 1581069019000, downCount: 1, upCount: 11, y: 1 }, + ], + interval: '1s', + }, }; + it('shallow renders the component without errors', () => { + const component = shallowWithRouter(); + expect(component).toMatchSnapshot(); + }); + it('renders the component without errors', () => { - const component = shallowWithIntl(); + const component = renderWithRouter(); expect(component).toMatchSnapshot(); }); }); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_chart.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_chart.tsx index 775e8c0c06aa5..7a6db6d952dd9 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_chart.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_chart.tsx @@ -8,6 +8,7 @@ import { Axis, Chart, Position, timeFormatter, Settings } from '@elastic/charts' import { EuiPanel, EuiTitle } from '@elastic/eui'; import React from 'react'; import { i18n } from '@kbn/i18n'; +import moment from 'moment'; import { FormattedMessage } from '@kbn/i18n/react'; import { getChartDateLabel } from '../../../lib/helper'; import { LocationDurationLine } from '../../../../common/graphql/types'; @@ -50,9 +51,15 @@ export const DurationChart = ({ loading, }: DurationChartProps) => { const hasLines = locationDurationLines.length > 0; - const [getUrlParams] = useUrlParams(); + const [getUrlParams, updateUrlParams] = useUrlParams(); const { absoluteDateRangeStart: min, absoluteDateRangeEnd: max } = getUrlParams(); + const onBrushEnd = (minX: number, maxX: number) => { + updateUrlParams({ + dateRangeStart: moment(minX).toISOString(), + dateRangeEnd: moment(maxX).toISOString(), + }); + }; return ( <> @@ -68,7 +75,12 @@ export const DurationChart = ({ {hasLines ? ( - + { - const [getUrlParams] = useUrlParams(); + const [getUrlParams, updateUrlParams] = useUrlParams(); const { absoluteDateRangeStart, absoluteDateRangeEnd } = getUrlParams(); + const onBrushEnd = (min: number, max: number) => { + updateUrlParams({ + dateRangeStart: moment(min).toISOString(), + dateRangeEnd: moment(max).toISOString(), + }); + }; + const id = 'downSeries'; return seriesHasDownValues(histogramSeries) ? (
- + = ({ const { colors: { danger, gray }, } = useContext(UptimeThemeContext); + + const [, updateUrlParams] = useUrlParams(); + if (!data || !data.histogram) /** * TODO: the Fragment, EuiTitle, and EuiPanel should be extracted to a dumb component @@ -93,6 +97,13 @@ export const PingHistogramComponent: React.FC = ({ const upMonitorsId = i18n.translate('xpack.uptime.snapshotHistogram.series.upLabel', { defaultMessage: 'Up', }); + + const onBrushEnd = (min: number, max: number) => { + updateUrlParams({ + dateRangeStart: moment(min).toISOString(), + dateRangeEnd: moment(max).toISOString(), + }); + }; return ( <> @@ -122,6 +133,7 @@ export const PingHistogramComponent: React.FC = ({ max: absoluteEndDate, }} showLegend={false} + onBrushEnd={onBrushEnd} /> = ({ 'The label on the y-axis of a chart that displays the number of times Heartbeat has pinged a set of services/websites.', })} /> + [x, downCount || 0])} diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/index.ts b/x-pack/legacy/plugins/uptime/public/components/functional/index.ts index 4ec4cf4f52607..e86ba548fb5d9 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/functional/index.ts @@ -6,7 +6,6 @@ export { DonutChart } from './charts/donut_chart'; export { EmptyState } from './empty_state'; -export { IntegrationLink } from './integration_link'; export { KueryBarComponent } from './kuery_bar/kuery_bar'; export { MonitorCharts } from './monitor_charts'; export { MonitorList } from './monitor_list'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/kuery_bar.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/kuery_bar.tsx index b1eb3f38097b2..496e8d898df3c 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/kuery_bar.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/kuery_bar.tsx @@ -15,7 +15,7 @@ import { esKuery, IIndexPattern, QuerySuggestion, - DataPublicPluginStart, + DataPublicPluginSetup, } from '../../../../../../../../src/plugins/data/public'; const Container = styled.div` @@ -33,7 +33,7 @@ function convertKueryToEsQuery(kuery: string, indexPattern: IIndexPattern) { } interface Props { - autocomplete: DataPublicPluginStart['autocomplete']; + autocomplete: DataPublicPluginSetup['autocomplete']; loadIndexPattern: any; indexPattern: any; } diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx index de6cc1982f1a8..58250222e1330 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx @@ -28,11 +28,11 @@ import { import { MonitorListStatusColumn } from './monitor_list_status_column'; import { formatUptimeGraphQLErrorList } from '../../../lib/helper/format_error_list'; import { ExpandedRowMap } from './types'; -import { MonitorListDrawer } from './monitor_list_drawer'; import { MonitorBarSeries } from '../charts'; import { MonitorPageLink } from './monitor_page_link'; import { OverviewPageLink } from './overview_page_link'; import * as labels from './translations'; +import { MonitorListDrawer } from '../../connected'; interface MonitorListQueryResult { monitorStates?: MonitorSummaryResult; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/integration_group.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_group.test.tsx.snap similarity index 66% rename from x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/integration_group.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_group.test.tsx.snap index bab69a6de9708..bb578d850ff7e 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/integration_group.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_group.test.tsx.snap @@ -4,6 +4,15 @@ exports[`IntegrationGroup will not display APM links when APM is unavailable 1`] + + + + + + + + + + + + + + + + + + + + + `; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/integration_link.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_link.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/integration_link.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_link.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap index 29f2c0b63991e..cf754581b1a33 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap @@ -52,7 +52,6 @@ exports[`MonitorListDrawer component renders a MonitorListDrawer when there are } > { }); it('will not display APM links when APM is unavailable', () => { - const component = shallowWithIntl( - - ); + const component = shallowWithIntl(); expect(component).toMatchSnapshot(); }); it('will not display infra links when infra is unavailable', () => { - const component = shallowWithIntl( - - ); + const component = shallowWithIntl(); expect(component).toMatchSnapshot(); }); it('will not display logging links when logging is unavailable', () => { - const component = shallowWithIntl( - - ); + const component = shallowWithIntl(); expect(component).toMatchSnapshot(); }); }); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/integration_link.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/integration_link.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/__tests__/integration_link.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/integration_link.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_list_drawer.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_list_drawer.test.tsx index c222728df3bb3..d870acefaaea6 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_list_drawer.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_list_drawer.test.tsx @@ -12,7 +12,6 @@ import { shallowWithRouter } from '../../../../../lib'; describe('MonitorListDrawer component', () => { let summary: MonitorSummary; - let loadMonitorDetails: any; let monitorDetails: MonitorDetails; beforeEach(() => { @@ -47,16 +46,11 @@ describe('MonitorListDrawer component', () => { 'Get https://expired.badssl.com: x509: certificate has expired or is not yet valid', }, }; - loadMonitorDetails = () => null; }); it('renders nothing when no summary data is present', () => { const component = shallowWithRouter( - + ); expect(component).toEqual({}); }); @@ -64,22 +58,14 @@ describe('MonitorListDrawer component', () => { it('renders nothing when no check data is present', () => { delete summary.state.checks; const component = shallowWithRouter( - + ); expect(component).toEqual({}); }); it('renders a MonitorListDrawer when there is only one check', () => { const component = shallowWithRouter( - + ); expect(component).toMatchSnapshot(); }); @@ -110,11 +96,7 @@ describe('MonitorListDrawer component', () => { ]; summary.state.checks = checks; const component = shallowWithRouter( - + ); expect(component).toMatchSnapshot(); }); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/index.ts b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/index.ts index 73fb07db60de8..2933a71c2240b 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/index.ts @@ -4,5 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export { MonitorListDrawer } from './monitor_list_drawer'; export { LocationLink } from './location_link'; +export { MonitorListActionsPopoverComponent } from './monitor_list_actions_popover'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/integration_group.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/integration_group.tsx similarity index 95% rename from x-pack/legacy/plugins/uptime/public/components/functional/integration_group.tsx rename to x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/integration_group.tsx index da66235e37f1a..34bff58a3e2d9 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/integration_group.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/integration_group.tsx @@ -5,7 +5,7 @@ */ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import React from 'react'; +import React, { useContext } from 'react'; import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -18,32 +18,29 @@ import { getLoggingContainerHref, getLoggingIpHref, getLoggingKubernetesHref, -} from '../../lib/helper'; -import { MonitorSummary } from '../../../common/graphql/types'; +} from '../../../../lib/helper'; +import { MonitorSummary } from '../../../../../common/graphql/types'; +import { UptimeSettingsContext } from '../../../../contexts'; interface IntegrationGroupProps { - basePath: string; - dateRangeStart: string; - dateRangeEnd: string; - isApmAvailable: boolean; - isInfraAvailable: boolean; - isLogsAvailable: boolean; summary: MonitorSummary; } -export const IntegrationGroup = ({ - basePath, - dateRangeStart, - dateRangeEnd, - isApmAvailable, - isInfraAvailable, - isLogsAvailable, - summary, -}: IntegrationGroupProps) => { +export const IntegrationGroup = ({ summary }: IntegrationGroupProps) => { + const { + basePath, + dateRangeStart, + dateRangeEnd, + isApmAvailable, + isInfraAvailable, + isLogsAvailable, + } = useContext(UptimeSettingsContext); + const domain = get(summary, 'state.url.domain', ''); const podUid = get(summary, 'state.checks[0].kubernetes.pod.uid', undefined); const containerId = get(summary, 'state.checks[0].container.id', undefined); const ip = get(summary, 'state.checks[0].monitor.ip', undefined); + return isApmAvailable || isInfraAvailable || isLogsAvailable ? ( {isApmAvailable ? ( diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/integration_link.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/integration_link.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/integration_link.tsx rename to x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/integration_link.tsx index a545cd7c42927..4b4c2003931a3 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/integration_link.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/integration_link.tsx @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiText, EuiToolTip } from '@elastic/eui'; import React from 'react'; import { i18n } from '@kbn/i18n'; +import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiText, EuiToolTip } from '@elastic/eui'; interface IntegrationLinkProps { ariaLabel: string; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_actions_popover.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_list_actions_popover.tsx similarity index 58% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_actions_popover.tsx rename to x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_list_actions_popover.tsx index af06761f50c83..6b946baa8d403 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_actions_popover.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_list_actions_popover.tsx @@ -4,17 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiPopover, EuiButton } from '@elastic/eui'; -import React, { useContext } from 'react'; -import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; -import { connect } from 'react-redux'; -import { MonitorSummary } from '../../../../common/graphql/types'; -import { IntegrationGroup } from '../integration_group'; -import { UptimeSettingsContext } from '../../../contexts'; -import { isIntegrationsPopupOpen } from '../../../state/selectors'; -import { AppState } from '../../../state'; -import { toggleIntegrationsPopover, PopoverState } from '../../../state/actions'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { EuiPopover, EuiButton } from '@elastic/eui'; +import { IntegrationGroup } from './integration_group'; +import { MonitorSummary } from '../../../../../common/graphql/types'; +import { toggleIntegrationsPopover, PopoverState } from '../../../../state/actions'; interface MonitorListActionsPopoverProps { summary: MonitorSummary; @@ -22,20 +18,12 @@ interface MonitorListActionsPopoverProps { togglePopoverIsVisible: typeof toggleIntegrationsPopover; } -const MonitorListActionsPopoverComponent = ({ +export const MonitorListActionsPopoverComponent = ({ summary, popoverState, togglePopoverIsVisible, }: MonitorListActionsPopoverProps) => { const popoverId = `${summary.monitor_id}_popover`; - const { - basePath, - dateRangeStart, - dateRangeEnd, - isApmAvailable, - isInfraAvailable, - isLogsAvailable, - } = useContext(UptimeSettingsContext); const monitorUrl: string | undefined = get(summary, 'state.url.full', undefined); const isPopoverOpen: boolean = @@ -64,30 +52,7 @@ const MonitorListActionsPopoverComponent = ({ id={popoverId} isOpen={isPopoverOpen} > - + ); }; - -const mapStateToProps = (state: AppState) => ({ - popoverState: isIntegrationsPopupOpen(state), -}); - -const mapDispatchToProps = (dispatch: any) => ({ - togglePopoverIsVisible: (popoverState: PopoverState) => { - return dispatch(toggleIntegrationsPopover(popoverState)); - }, -}); - -export const MonitorListActionsPopover = connect( - mapStateToProps, - mapDispatchToProps -)(MonitorListActionsPopoverComponent); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx index 35b649fa35795..8383596ccc346 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx @@ -4,20 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useEffect } from 'react'; -import { EuiLink, EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from '@elastic/eui'; +import React from 'react'; import styled from 'styled-components'; -import { connect } from 'react-redux'; +import { EuiLink, EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from '@elastic/eui'; import { MonitorSummary } from '../../../../../common/graphql/types'; -import { AppState } from '../../../../state'; -import { fetchMonitorDetails } from '../../../../state/actions/monitor'; import { MostRecentError } from './most_recent_error'; -import { getMonitorDetails } from '../../../../state/selectors'; import { MonitorStatusList } from './monitor_status_list'; import { MonitorDetails } from '../../../../../common/runtime_types'; -import { useUrlParams } from '../../../../hooks'; -import { MonitorDetailsActionPayload } from '../../../../state/actions/types'; -import { MonitorListActionsPopover } from '../monitor_list_actions_popover'; +import { MonitorListActionsPopover } from '../../../connected'; const ContainerDiv = styled.div` padding: 10px; @@ -34,34 +28,13 @@ interface MonitorListDrawerProps { * Monitor details to be fetched from rest api using monitorId */ monitorDetails: MonitorDetails; - - /** - * Redux action to trigger , loading monitor details - */ - loadMonitorDetails: typeof fetchMonitorDetails; } /** * The elements shown when the user expands the monitor list rows. */ -export function MonitorListDrawerComponent({ - summary, - loadMonitorDetails, - monitorDetails, -}: MonitorListDrawerProps) { - const monitorId = summary?.monitor_id; - const [getUrlParams] = useUrlParams(); - const { dateRangeStart: dateStart, dateRangeEnd: dateEnd } = getUrlParams(); - - useEffect(() => { - loadMonitorDetails({ - dateStart, - dateEnd, - monitorId, - }); - }, [dateStart, dateEnd, monitorId, loadMonitorDetails]); - +export function MonitorListDrawerComponent({ summary, monitorDetails }: MonitorListDrawerProps) { const monitorUrl = summary?.state?.url?.full || ''; return summary && summary.state.checks ? ( @@ -91,17 +64,3 @@ export function MonitorListDrawerComponent({ ) : null; } - -const mapStateToProps = (state: AppState, { summary }: any) => ({ - monitorDetails: getMonitorDetails(state, summary), -}); - -const mapDispatchToProps = (dispatch: any) => ({ - loadMonitorDetails: (actionPayload: MonitorDetailsActionPayload) => - dispatch(fetchMonitorDetails(actionPayload)), -}); - -export const MonitorListDrawer = connect( - mapStateToProps, - mapDispatchToProps -)(MonitorListDrawerComponent); diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/render_with_router.tsx b/x-pack/legacy/plugins/uptime/public/lib/helper/helper_with_router.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/render_with_router.tsx rename to x-pack/legacy/plugins/uptime/public/lib/helper/helper_with_router.tsx diff --git a/x-pack/legacy/plugins/uptime/public/lib/index.ts b/x-pack/legacy/plugins/uptime/public/lib/index.ts index 07a0792360044..06ac06e647adc 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/index.ts +++ b/x-pack/legacy/plugins/uptime/public/lib/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { renderWithRouter, shallowWithRouter, mountWithRouter } from './helper/render_with_router'; +export { renderWithRouter, shallowWithRouter, mountWithRouter } from './helper/helper_with_router'; diff --git a/x-pack/legacy/plugins/uptime/public/pages/__tests__/__snapshots__/overview.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/pages/__tests__/__snapshots__/overview.test.tsx.snap index fff947bd96024..71b3fb5c7146a 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/__tests__/__snapshots__/overview.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/pages/__tests__/__snapshots__/overview.test.tsx.snap @@ -54,6 +54,7 @@ exports[`MonitorPage shallow renders expected elements for valid props 1`] = ` { getQuerySuggestions: jest.fn(), hasQuerySuggestions: () => true, getValueSuggestions: jest.fn(), + addQuerySuggestionProvider: jest.fn(), }; it('shallow renders expected elements for valid props', () => { diff --git a/x-pack/legacy/plugins/uptime/public/pages/overview.tsx b/x-pack/legacy/plugins/uptime/public/pages/overview.tsx index ae7457e835c94..5360d66f87e99 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/overview.tsx +++ b/x-pack/legacy/plugins/uptime/public/pages/overview.tsx @@ -16,13 +16,13 @@ import { import { useUrlParams, useUptimeTelemetry, UptimePage } from '../hooks'; import { stringifyUrlParams } from '../lib/helper/stringify_url_params'; import { useTrackPageview } from '../../../infra/public'; -import { DataPublicPluginStart, IIndexPattern } from '../../../../../../src/plugins/data/public'; +import { DataPublicPluginSetup, IIndexPattern } from '../../../../../../src/plugins/data/public'; import { UptimeThemeContext } from '../contexts'; import { FilterGroup, KueryBar } from '../components/connected'; import { useUpdateKueryString } from '../hooks'; interface OverviewPageProps { - autocomplete: DataPublicPluginStart['autocomplete']; + autocomplete: DataPublicPluginSetup['autocomplete']; indexPattern: IIndexPattern; setEsKueryFilters: (esFilters: string) => void; } diff --git a/x-pack/legacy/plugins/uptime/public/routes.tsx b/x-pack/legacy/plugins/uptime/public/routes.tsx index 0f726d89e0d28..83be45083b645 100644 --- a/x-pack/legacy/plugins/uptime/public/routes.tsx +++ b/x-pack/legacy/plugins/uptime/public/routes.tsx @@ -6,13 +6,13 @@ import React, { FC } from 'react'; import { Route, Switch } from 'react-router-dom'; -import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; +import { DataPublicPluginSetup } from '../../../../../src/plugins/data/public'; import { OverviewPage } from './components/connected/pages/overview_container'; import { MONITOR_ROUTE, OVERVIEW_ROUTE } from '../common/constants'; import { MonitorPage, NotFoundPage } from './pages'; interface RouterProps { - autocomplete: DataPublicPluginStart['autocomplete']; + autocomplete: DataPublicPluginSetup['autocomplete']; } export const PageRouter: FC = ({ autocomplete }) => ( diff --git a/x-pack/legacy/plugins/uptime/public/uptime_app.tsx b/x-pack/legacy/plugins/uptime/public/uptime_app.tsx index dbde9f8b6a8c0..db34566d6c148 100644 --- a/x-pack/legacy/plugins/uptime/public/uptime_app.tsx +++ b/x-pack/legacy/plugins/uptime/public/uptime_app.tsx @@ -100,7 +100,6 @@ const Application = (props: UptimeAppProps) => {
- // @ts-ignore we need to update the type of this prop
diff --git a/x-pack/plugins/graph/config.ts b/x-pack/plugins/graph/config.ts new file mode 100644 index 0000000000000..3838d6ca34ba4 --- /dev/null +++ b/x-pack/plugins/graph/config.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; + +export const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: true }), + savePolicy: schema.oneOf( + [ + schema.literal('none'), + schema.literal('config'), + schema.literal('configAndData'), + schema.literal('configAndDataWithConsent'), + ], + { defaultValue: 'configAndData' } + ), + canEditDrillDownUrls: schema.boolean({ defaultValue: true }), +}); + +export type ConfigSchema = TypeOf; diff --git a/x-pack/plugins/graph/public/index.ts b/x-pack/plugins/graph/public/index.ts index ac9ca960c0c7f..7b2ce67631713 100644 --- a/x-pack/plugins/graph/public/index.ts +++ b/x-pack/plugins/graph/public/index.ts @@ -4,6 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ +import { PluginInitializerContext } from 'kibana/public'; import { GraphPlugin } from './plugin'; +import { ConfigSchema } from '../config'; -export const plugin = () => new GraphPlugin(); +export const plugin = (initializerContext: PluginInitializerContext) => + new GraphPlugin(initializerContext); + +export { GraphSetup } from './plugin'; diff --git a/x-pack/plugins/graph/public/plugin.ts b/x-pack/plugins/graph/public/plugin.ts index c0cec14e04d61..e911b400349f8 100644 --- a/x-pack/plugins/graph/public/plugin.ts +++ b/x-pack/plugins/graph/public/plugin.ts @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, CoreStart } from 'kibana/public'; import { Plugin } from 'src/core/public'; +import { PluginInitializerContext } from 'kibana/public'; import { toggleNavLink } from './services/toggle_nav_link'; import { LicensingPluginSetup } from '../../licensing/public'; import { checkLicense } from '../common/check_license'; @@ -14,15 +15,18 @@ import { FeatureCatalogueCategory, HomePublicPluginSetup, } from '../../../../src/plugins/home/public'; +import { ConfigSchema } from '../config'; export interface GraphPluginSetupDependencies { licensing: LicensingPluginSetup; home?: HomePublicPluginSetup; } -export class GraphPlugin implements Plugin { +export class GraphPlugin implements Plugin<{ config: Readonly }, void> { private licensing: LicensingPluginSetup | null = null; + constructor(private initializerContext: PluginInitializerContext) {} + setup(core: CoreSetup, { licensing, home }: GraphPluginSetupDependencies) { this.licensing = licensing; @@ -39,6 +43,16 @@ export class GraphPlugin implements Plugin { category: FeatureCatalogueCategory.DATA, }); } + + return { + /** + * The configuration is temporarily exposed to allow the legacy graph plugin to consume + * the setting. Once the graph plugin is migrated completely, this will become an implementation + * detail. + * @deprecated + */ + config: this.initializerContext.config.get(), + }; } start(core: CoreStart) { @@ -52,3 +66,5 @@ export class GraphPlugin implements Plugin { stop() {} } + +export type GraphSetup = ReturnType; diff --git a/x-pack/plugins/graph/server/index.ts b/x-pack/plugins/graph/server/index.ts index ac9ca960c0c7f..a5900e1778b90 100644 --- a/x-pack/plugins/graph/server/index.ts +++ b/x-pack/plugins/graph/server/index.ts @@ -4,6 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ +import { PluginConfigDescriptor } from 'kibana/server'; + +import { configSchema, ConfigSchema } from '../config'; import { GraphPlugin } from './plugin'; export const plugin = () => new GraphPlugin(); + +export const config: PluginConfigDescriptor = { + exposeToBrowser: { + canEditDrillDownUrls: true, + savePolicy: true, + }, + schema: configSchema, +}; diff --git a/x-pack/plugins/security/server/authentication/authenticator.test.ts b/x-pack/plugins/security/server/authentication/authenticator.test.ts index 7dd0587c0a1af..f7038d2320ef4 100644 --- a/x-pack/plugins/security/server/authentication/authenticator.test.ts +++ b/x-pack/plugins/security/server/authentication/authenticator.test.ts @@ -28,7 +28,6 @@ function getMockOptions(config: Partial = {}) { clusterClient: elasticsearchServiceMock.createClusterClient(), basePath: httpServiceMock.createSetupContract().basePath, loggers: loggingServiceMock.create(), - isSystemAPIRequest: jest.fn(), getServerBaseURL: jest.fn(), config: { session: { idleTimeout: null, lifespan: null }, @@ -287,10 +286,11 @@ describe('Authenticator', () => { it('creates session whenever authentication provider returns state for system API requests', async () => { const user = mockAuthenticatedUser(); - const request = httpServerMock.createKibanaRequest(); + const request = httpServerMock.createKibanaRequest({ + headers: { 'kbn-system-request': 'true' }, + }); const authorization = `Basic ${Buffer.from('foo:bar').toString('base64')}`; - mockOptions.isSystemAPIRequest.mockReturnValue(true); mockBasicAuthenticationProvider.authenticate.mockResolvedValue( AuthenticationResult.succeeded(user, { state: { authorization } }) ); @@ -308,10 +308,11 @@ describe('Authenticator', () => { it('creates session whenever authentication provider returns state for non-system API requests', async () => { const user = mockAuthenticatedUser(); - const request = httpServerMock.createKibanaRequest(); + const request = httpServerMock.createKibanaRequest({ + headers: { 'kbn-system-request': 'false' }, + }); const authorization = `Basic ${Buffer.from('foo:bar').toString('base64')}`; - mockOptions.isSystemAPIRequest.mockReturnValue(false); mockBasicAuthenticationProvider.authenticate.mockResolvedValue( AuthenticationResult.succeeded(user, { state: { authorization } }) ); @@ -329,9 +330,10 @@ describe('Authenticator', () => { it('does not extend session for system API calls.', async () => { const user = mockAuthenticatedUser(); - const request = httpServerMock.createKibanaRequest(); + const request = httpServerMock.createKibanaRequest({ + headers: { 'kbn-system-request': 'true' }, + }); - mockOptions.isSystemAPIRequest.mockReturnValue(true); mockBasicAuthenticationProvider.authenticate.mockResolvedValue( AuthenticationResult.succeeded(user) ); @@ -347,9 +349,10 @@ describe('Authenticator', () => { it('extends session for non-system API calls.', async () => { const user = mockAuthenticatedUser(); - const request = httpServerMock.createKibanaRequest(); + const request = httpServerMock.createKibanaRequest({ + headers: { 'kbn-system-request': 'false' }, + }); - mockOptions.isSystemAPIRequest.mockReturnValue(false); mockBasicAuthenticationProvider.authenticate.mockResolvedValue( AuthenticationResult.succeeded(user) ); @@ -511,9 +514,10 @@ describe('Authenticator', () => { }); it('does not touch session for system API calls if authentication fails with non-401 reason.', async () => { - const request = httpServerMock.createKibanaRequest(); + const request = httpServerMock.createKibanaRequest({ + headers: { 'kbn-system-request': 'true' }, + }); - mockOptions.isSystemAPIRequest.mockReturnValue(true); mockBasicAuthenticationProvider.authenticate.mockResolvedValue( AuthenticationResult.failed(new Error('some error')) ); @@ -527,9 +531,10 @@ describe('Authenticator', () => { }); it('does not touch session for non-system API calls if authentication fails with non-401 reason.', async () => { - const request = httpServerMock.createKibanaRequest(); + const request = httpServerMock.createKibanaRequest({ + headers: { 'kbn-system-request': 'false' }, + }); - mockOptions.isSystemAPIRequest.mockReturnValue(false); mockBasicAuthenticationProvider.authenticate.mockResolvedValue( AuthenticationResult.failed(new Error('some error')) ); @@ -545,9 +550,10 @@ describe('Authenticator', () => { it('replaces existing session with the one returned by authentication provider for system API requests', async () => { const user = mockAuthenticatedUser(); const newState = { authorization: 'Basic yyy' }; - const request = httpServerMock.createKibanaRequest(); + const request = httpServerMock.createKibanaRequest({ + headers: { 'kbn-system-request': 'true' }, + }); - mockOptions.isSystemAPIRequest.mockReturnValue(true); mockBasicAuthenticationProvider.authenticate.mockResolvedValue( AuthenticationResult.succeeded(user, { state: newState }) ); @@ -568,9 +574,10 @@ describe('Authenticator', () => { it('replaces existing session with the one returned by authentication provider for non-system API requests', async () => { const user = mockAuthenticatedUser(); const newState = { authorization: 'Basic yyy' }; - const request = httpServerMock.createKibanaRequest(); + const request = httpServerMock.createKibanaRequest({ + headers: { 'kbn-system-request': 'false' }, + }); - mockOptions.isSystemAPIRequest.mockReturnValue(false); mockBasicAuthenticationProvider.authenticate.mockResolvedValue( AuthenticationResult.succeeded(user, { state: newState }) ); @@ -589,9 +596,10 @@ describe('Authenticator', () => { }); it('clears session if provider failed to authenticate system API request with 401 with active session.', async () => { - const request = httpServerMock.createKibanaRequest(); + const request = httpServerMock.createKibanaRequest({ + headers: { 'kbn-system-request': 'true' }, + }); - mockOptions.isSystemAPIRequest.mockReturnValue(true); mockBasicAuthenticationProvider.authenticate.mockResolvedValue( AuthenticationResult.failed(Boom.unauthorized()) ); @@ -605,9 +613,10 @@ describe('Authenticator', () => { }); it('clears session if provider failed to authenticate non-system API request with 401 with active session.', async () => { - const request = httpServerMock.createKibanaRequest(); + const request = httpServerMock.createKibanaRequest({ + headers: { 'kbn-system-request': 'false' }, + }); - mockOptions.isSystemAPIRequest.mockReturnValue(false); mockBasicAuthenticationProvider.authenticate.mockResolvedValue( AuthenticationResult.failed(Boom.unauthorized()) ); @@ -636,9 +645,10 @@ describe('Authenticator', () => { }); it('does not clear session if provider can not handle system API request authentication with active session.', async () => { - const request = httpServerMock.createKibanaRequest(); + const request = httpServerMock.createKibanaRequest({ + headers: { 'kbn-system-request': 'true' }, + }); - mockOptions.isSystemAPIRequest.mockReturnValue(true); mockBasicAuthenticationProvider.authenticate.mockResolvedValue( AuthenticationResult.notHandled() ); @@ -652,9 +662,10 @@ describe('Authenticator', () => { }); it('does not clear session if provider can not handle non-system API request authentication with active session.', async () => { - const request = httpServerMock.createKibanaRequest(); + const request = httpServerMock.createKibanaRequest({ + headers: { 'kbn-system-request': 'false' }, + }); - mockOptions.isSystemAPIRequest.mockReturnValue(false); mockBasicAuthenticationProvider.authenticate.mockResolvedValue( AuthenticationResult.notHandled() ); @@ -668,9 +679,10 @@ describe('Authenticator', () => { }); it('clears session for system API request if it belongs to not configured provider.', async () => { - const request = httpServerMock.createKibanaRequest(); + const request = httpServerMock.createKibanaRequest({ + headers: { 'kbn-system-request': 'true' }, + }); - mockOptions.isSystemAPIRequest.mockReturnValue(true); mockBasicAuthenticationProvider.authenticate.mockResolvedValue( AuthenticationResult.notHandled() ); @@ -684,9 +696,10 @@ describe('Authenticator', () => { }); it('clears session for non-system API request if it belongs to not configured provider.', async () => { - const request = httpServerMock.createKibanaRequest(); + const request = httpServerMock.createKibanaRequest({ + headers: { 'kbn-system-request': 'false' }, + }); - mockOptions.isSystemAPIRequest.mockReturnValue(false); mockBasicAuthenticationProvider.authenticate.mockResolvedValue( AuthenticationResult.notHandled() ); diff --git a/x-pack/plugins/security/server/authentication/authenticator.ts b/x-pack/plugins/security/server/authentication/authenticator.ts index 0cfccb09b4128..38d76d8d9b8bc 100644 --- a/x-pack/plugins/security/server/authentication/authenticator.ts +++ b/x-pack/plugins/security/server/authentication/authenticator.ts @@ -88,7 +88,6 @@ export interface AuthenticatorOptions { loggers: LoggerFactory; clusterClient: IClusterClient; sessionStorageFactory: SessionStorageFactory; - isSystemAPIRequest: (request: KibanaRequest) => boolean; getServerBaseURL: () => string; } @@ -312,7 +311,7 @@ export class Authenticator { this.updateSessionValue(sessionStorage, { providerType, - isSystemAPIRequest: this.options.isSystemAPIRequest(request), + isSystemRequest: request.isSystemRequest, authenticationResult, existingSession: ownsSession ? existingSession : null, }); @@ -436,12 +435,12 @@ export class Authenticator { providerType, authenticationResult, existingSession, - isSystemAPIRequest, + isSystemRequest, }: { providerType: string; authenticationResult: AuthenticationResult; existingSession: ProviderSession | null; - isSystemAPIRequest: boolean; + isSystemRequest: boolean; } ) { if (!existingSession && !authenticationResult.shouldUpdateState()) { @@ -453,7 +452,7 @@ export class Authenticator { // state we should store it in the session regardless of whether it's a system API request or not. const sessionCanBeUpdated = (authenticationResult.succeeded() || authenticationResult.redirected()) && - (authenticationResult.shouldUpdateState() || !isSystemAPIRequest); + (authenticationResult.shouldUpdateState() || !isSystemRequest); // If provider owned the session, but failed to authenticate anyway, that likely means that // session is not valid and we should clear it. Also provider can specifically ask to clear diff --git a/x-pack/plugins/security/server/authentication/index.test.ts b/x-pack/plugins/security/server/authentication/index.test.ts index 60749ed8eee59..94773c92be3f0 100644 --- a/x-pack/plugins/security/server/authentication/index.test.ts +++ b/x-pack/plugins/security/server/authentication/index.test.ts @@ -47,7 +47,7 @@ describe('setupAuthentication()', () => { let mockSetupAuthenticationParams: { config: ConfigType; loggers: LoggerFactory; - getLegacyAPI(): Pick; + getLegacyAPI(): Pick; http: jest.Mocked; clusterClient: jest.Mocked; license: jest.Mocked; diff --git a/x-pack/plugins/security/server/authentication/index.ts b/x-pack/plugins/security/server/authentication/index.ts index 913a01e2e1ee9..94bd5d533cd9a 100644 --- a/x-pack/plugins/security/server/authentication/index.ts +++ b/x-pack/plugins/security/server/authentication/index.ts @@ -36,7 +36,7 @@ interface SetupAuthenticationParams { config: ConfigType; license: SecurityLicense; loggers: LoggerFactory; - getLegacyAPI(): Pick; + getLegacyAPI(): Pick; } export type Authentication = UnwrapPromise>; @@ -96,7 +96,6 @@ export async function setupAuthentication({ clusterClient, basePath: http.basePath, config: { session: config.session, authc: config.authc }, - isSystemAPIRequest: (request: KibanaRequest) => getLegacyAPI().isSystemAPIRequest(request), getServerBaseURL, loggers, sessionStorageFactory: await http.createCookieSessionStorageFactory({ diff --git a/x-pack/plugins/security/server/plugin.ts b/x-pack/plugins/security/server/plugin.ts index 219ba1437951e..a42e795228866 100644 --- a/x-pack/plugins/security/server/plugin.ts +++ b/x-pack/plugins/security/server/plugin.ts @@ -9,7 +9,6 @@ import { first } from 'rxjs/operators'; import { ICustomClusterClient, CoreSetup, - KibanaRequest, Logger, PluginInitializerContext, RecursiveReadonly, @@ -41,7 +40,6 @@ export type FeaturesService = Pick; */ export interface LegacyAPI { serverConfig: { protocol: string; hostname: string; port: number }; - isSystemAPIRequest: (request: KibanaRequest) => boolean; auditLogger: { log: (eventType: string, message: string, data?: Record) => void; }; diff --git a/x-pack/test/api_integration/apis/security/basic_login.js b/x-pack/test/api_integration/apis/security/basic_login.js index d4b41603944f6..9b6f49a9a916b 100644 --- a/x-pack/test/api_integration/apis/security/basic_login.js +++ b/x-pack/test/api_integration/apis/security/basic_login.js @@ -201,7 +201,7 @@ export default function({ getService }) { const systemAPIResponse = await supertest .get('/internal/security/me') .set('kbn-xsrf', 'xxx') - .set('kbn-system-api', 'true') + .set('kbn-system-request', 'true') .set('Cookie', sessionCookie.cookieString()) .expect(200); diff --git a/x-pack/test/api_integration/apis/security/session.ts b/x-pack/test/api_integration/apis/security/session.ts index 5d0935bb1ae2d..d819dd38dddb1 100644 --- a/x-pack/test/api_integration/apis/security/session.ts +++ b/x-pack/test/api_integration/apis/security/session.ts @@ -28,7 +28,7 @@ export default function({ getService }: FtrProviderContext) { supertest .get('/internal/security/session') .set('kbn-xsrf', 'xxx') - .set('kbn-system-api', 'true') + .set('kbn-system-request', 'true') .set('Cookie', sessionCookie.cookieString()) .send() .expect(200); diff --git a/x-pack/test/functional/apps/transform/creation_saved_search.ts b/x-pack/test/functional/apps/transform/creation_saved_search.ts index 4528a2c84d9de..333a53a98c82b 100644 --- a/x-pack/test/functional/apps/transform/creation_saved_search.ts +++ b/x-pack/test/functional/apps/transform/creation_saved_search.ts @@ -17,7 +17,8 @@ export default function({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const transform = getService('transform'); - describe('creation_saved_search', function() { + // flaky test, see #55179 + describe.skip('creation_saved_search', function() { this.tags(['smoke']); before(async () => { await esArchiver.load('ml/farequote'); diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts index 95371b5b501f5..6d83e0bbf1df7 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts @@ -204,7 +204,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); - it('renders the active alert instances', async () => { + it.skip('renders the active alert instances', async () => { const testBeganAt = moment().utc(); // Verify content diff --git a/x-pack/test/kerberos_api_integration/apis/security/kerberos_login.ts b/x-pack/test/kerberos_api_integration/apis/security/kerberos_login.ts index 570d7026cf99e..55853f8b0fbde 100644 --- a/x-pack/test/kerberos_api_integration/apis/security/kerberos_login.ts +++ b/x-pack/test/kerberos_api_integration/apis/security/kerberos_login.ts @@ -199,7 +199,7 @@ export default function({ getService }: FtrProviderContext) { const systemAPIResponse = await supertest .get('/internal/security/me') .set('kbn-xsrf', 'xxx') - .set('kbn-system-api', 'true') + .set('kbn-system-request', 'true') .set('Cookie', sessionCookie.cookieString()) .expect(200); diff --git a/x-pack/test/oidc_api_integration/apis/authorization_code_flow/oidc_auth.js b/x-pack/test/oidc_api_integration/apis/authorization_code_flow/oidc_auth.js index 094537fd61436..abb65e46263ab 100644 --- a/x-pack/test/oidc_api_integration/apis/authorization_code_flow/oidc_auth.js +++ b/x-pack/test/oidc_api_integration/apis/authorization_code_flow/oidc_auth.js @@ -285,7 +285,7 @@ export default function({ getService }) { const systemAPIResponse = await supertest .get('/internal/security/me') .set('kbn-xsrf', 'xxx') - .set('kbn-system-api', 'true') + .set('kbn-system-request', 'true') .set('Cookie', sessionCookie.cookieString()) .expect(200); diff --git a/x-pack/test/pki_api_integration/apis/security/pki_auth.ts b/x-pack/test/pki_api_integration/apis/security/pki_auth.ts index 1ae7488fcf379..6cb92585de36e 100644 --- a/x-pack/test/pki_api_integration/apis/security/pki_auth.ts +++ b/x-pack/test/pki_api_integration/apis/security/pki_auth.ts @@ -242,7 +242,7 @@ export default function({ getService }: FtrProviderContext) { .ca(CA_CERT) .pfx(FIRST_CLIENT_CERT) .set('kbn-xsrf', 'xxx') - .set('kbn-system-api', 'true') + .set('kbn-system-request', 'true') .set('Cookie', sessionCookie.cookieString()) .expect(200); diff --git a/x-pack/test/saml_api_integration/apis/security/saml_login.ts b/x-pack/test/saml_api_integration/apis/security/saml_login.ts index 376f63a400516..93f7b2262e364 100644 --- a/x-pack/test/saml_api_integration/apis/security/saml_login.ts +++ b/x-pack/test/saml_api_integration/apis/security/saml_login.ts @@ -330,7 +330,7 @@ export default function({ getService }: FtrProviderContext) { const systemAPIResponse = await supertest .get('/internal/security/me') .set('kbn-xsrf', 'xxx') - .set('kbn-system-api', 'true') + .set('kbn-system-request', 'true') .set('Cookie', sessionCookie.cookieString()) .expect(200);