diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d567f267afa9d..610681e83798e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -13,9 +13,21 @@ /src/plugins/kibana_react/ @elastic/kibana-app-arch /src/plugins/kibana_utils/ @elastic/kibana-app-arch /src/plugins/navigation/ @elastic/kibana-app-arch +/src/plugins/share/ @elastic/kibana-app-arch /src/plugins/ui_actions/ @elastic/kibana-app-arch /src/plugins/visualizations/ @elastic/kibana-app-arch /x-pack/plugins/advanced_ui_actions/ @elastic/kibana-app-arch +/src/legacy/core_plugins/data/ @elastic/kibana-app-arch +/src/legacy/core_plugins/embeddable_api/ @elastic/kibana-app-arch +/src/legacy/core_plugins/interpreter/ @elastic/kibana-app-arch +/src/legacy/core_plugins/kibana_react/ @elastic/kibana-app-arch +/src/legacy/core_plugins/kibana/public/management/ @elastic/kibana-app-arch +/src/legacy/core_plugins/kibana/server/field_formats/ @elastic/kibana-app-arch +/src/legacy/core_plugins/kibana/server/routes/api/management/ @elastic/kibana-app-arch +/src/legacy/core_plugins/kibana/server/routes/api/suggestions/ @elastic/kibana-app-arch +/src/legacy/core_plugins/visualizations/ @elastic/kibana-app-arch +/src/legacy/server/index_patterns/ @elastic/kibana-app-arch +/src/legacy/server/url_shortening/ @elastic/kibana-app-arch # APM /x-pack/legacy/plugins/apm/ @elastic/apm-ui diff --git a/Jenkinsfile b/Jenkinsfile index 6030f2b4a021d..261ba00096818 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -12,6 +12,7 @@ stage("Kibana Pipeline") { // This stage is just here to help the BlueOcean UI a 'kibana-intake-agent': kibanaPipeline.legacyJobRunner('kibana-intake'), 'x-pack-intake-agent': kibanaPipeline.legacyJobRunner('x-pack-intake'), 'kibana-oss-agent': kibanaPipeline.withWorkers('kibana-oss-tests', { kibanaPipeline.buildOss() }, [ + 'oss-firefoxSmoke': kibanaPipeline.getPostBuildWorker('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), @@ -24,11 +25,11 @@ stage("Kibana Pipeline") { // This stage is just here to help the BlueOcean UI a 'oss-ciGroup10': kibanaPipeline.getOssCiGroupWorker(10), 'oss-ciGroup11': kibanaPipeline.getOssCiGroupWorker(11), 'oss-ciGroup12': kibanaPipeline.getOssCiGroupWorker(12), - 'oss-firefoxSmoke': kibanaPipeline.getPostBuildWorker('firefoxSmoke', { runbld('./test/scripts/jenkins_firefox_smoke.sh', 'Execute kibana-firefoxSmoke') }), 'oss-accessibility': kibanaPipeline.getPostBuildWorker('accessibility', { runbld('./test/scripts/jenkins_accessibility.sh', 'Execute kibana-accessibility') }), - 'oss-visualRegression': kibanaPipeline.getPostBuildWorker('visualRegression', { runbld('./test/scripts/jenkins_visual_regression.sh', 'Execute kibana-visualRegression') }), + // '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', { 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), @@ -39,9 +40,8 @@ stage("Kibana Pipeline") { // This stage is just here to help the BlueOcean UI a 'xpack-ciGroup8': kibanaPipeline.getXpackCiGroupWorker(8), 'xpack-ciGroup9': kibanaPipeline.getXpackCiGroupWorker(9), 'xpack-ciGroup10': kibanaPipeline.getXpackCiGroupWorker(10), - 'xpack-firefoxSmoke': kibanaPipeline.getPostBuildWorker('xpack-firefoxSmoke', { runbld('./test/scripts/jenkins_xpack_firefox_smoke.sh', 'Execute xpack-firefoxSmoke') }), 'xpack-accessibility': kibanaPipeline.getPostBuildWorker('xpack-accessibility', { runbld('./test/scripts/jenkins_xpack_accessibility.sh', 'Execute xpack-accessibility') }), - 'xpack-visualRegression': kibanaPipeline.getPostBuildWorker('xpack-visualRegression', { runbld('./test/scripts/jenkins_xpack_visual_regression.sh', 'Execute xpack-visualRegression') }), + // 'xpack-visualRegression': kibanaPipeline.getPostBuildWorker('xpack-visualRegression', { runbld('./test/scripts/jenkins_xpack_visual_regression.sh', 'Execute xpack-visualRegression') }), ]), ]) } diff --git a/config/kibana.yml b/config/kibana.yml index 47482f9e6d59f..0780841ca057e 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -51,7 +51,7 @@ # Optional settings that provide the paths to the PEM-format SSL certificate and key files. # These files are used to verify the identity of Kibana to Elasticsearch and are required when -# xpack.ssl.verification_mode in Elasticsearch is set to either certificate or full. +# xpack.security.http.ssl.client_authentication in Elasticsearch is set to required. #elasticsearch.ssl.certificate: /path/to/your/client.crt #elasticsearch.ssl.key: /path/to/your/client.key diff --git a/docs/setup/docker.asciidoc b/docs/setup/docker.asciidoc index 0827cf5c73009..f3e7273adedee 100644 --- a/docs/setup/docker.asciidoc +++ b/docs/setup/docker.asciidoc @@ -9,7 +9,7 @@ https://github.com/elastic/kibana-docker/tree/{branch}[GitHub]. These images are free to use under the Elastic license. They contain open source and free commercial features and access to paid commercial features. -{xpack-ref}/license-management.html[Start a 30-day trial] to try out all of the +{stack-ov}/license-management.html[Start a 30-day trial] to try out all of the paid commercial features. See the https://www.elastic.co/subscriptions[Subscriptions] page for information about Elastic license levels. diff --git a/docs/setup/install/deb.asciidoc b/docs/setup/install/deb.asciidoc index 9cd83f44885cb..62ab661d9a66c 100644 --- a/docs/setup/install/deb.asciidoc +++ b/docs/setup/install/deb.asciidoc @@ -7,7 +7,7 @@ Kibana on any Debian-based system such as Debian and Ubuntu. This package is free to use under the Elastic license. It contains open source and free commercial features and access to paid commercial features. -{xpack-ref}/license-management.html[Start a 30-day trial] to try out all of the +{stack-ov}/license-management.html[Start a 30-day trial] to try out all of the paid commercial features. See the https://www.elastic.co/subscriptions[Subscriptions] page for information about Elastic license levels. diff --git a/docs/setup/install/rpm.asciidoc b/docs/setup/install/rpm.asciidoc index ddff35c61b5bb..77a16e67cf2a4 100644 --- a/docs/setup/install/rpm.asciidoc +++ b/docs/setup/install/rpm.asciidoc @@ -11,7 +11,7 @@ such as SLES 11 and CentOS 5. Please see <> instead. This package is free to use under the Elastic license. It contains open source and free commercial features and access to paid commercial features. -{xpack-ref}/license-management.html[Start a 30-day trial] to try out all of the +{stack-ov}/license-management.html[Start a 30-day trial] to try out all of the paid commercial features. See the https://www.elastic.co/subscriptions[Subscriptions] page for information about Elastic license levels. diff --git a/docs/setup/install/targz.asciidoc b/docs/setup/install/targz.asciidoc index e3104520292ff..d2143f39fba50 100644 --- a/docs/setup/install/targz.asciidoc +++ b/docs/setup/install/targz.asciidoc @@ -6,7 +6,7 @@ are the easiest formats to use when trying out Kibana. These packages are free to use under the Elastic license. They contain open source and free commercial features and access to paid commercial features. -{xpack-ref}/license-management.html[Start a 30-day trial] to try out all of the +{stack-ov}/license-management.html[Start a 30-day trial] to try out all of the paid commercial features. See the https://www.elastic.co/subscriptions[Subscriptions] page for information about Elastic license levels. diff --git a/docs/setup/install/windows.asciidoc b/docs/setup/install/windows.asciidoc index b9f0224e67699..db55451f01aae 100644 --- a/docs/setup/install/windows.asciidoc +++ b/docs/setup/install/windows.asciidoc @@ -5,7 +5,7 @@ Kibana can be installed on Windows using the `.zip` package. This package is free to use under the Elastic license. It contains open source and free commercial features and access to paid commercial features. -{xpack-ref}/license-management.html[Start a 30-day trial] to try out all of the +{stack-ov}/license-management.html[Start a 30-day trial] to try out all of the paid commercial features. See the https://www.elastic.co/subscriptions[Subscriptions] page for information about Elastic license levels. diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index f2c06a3737c7c..5605ed5c56688 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -258,6 +258,12 @@ running behind a proxy. Use the `server.rewriteBasePath` setting to tell Kibana if it should remove the basePath from requests it receives, and to prevent a deprecation warning at startup. This setting cannot end in a slash (`/`). +[[server-compression]]`server.compression.enabled:`:: *Default: `true`* Set to `false` to disable HTTP compression for all responses. + +`server.compression.referrerWhitelist:`:: *Default: none* Specifies an array of trusted hostnames, such as the Kibana host, or a reverse +proxy sitting in front of it. This determines whether HTTP compression may be used for responses, based on the request's `Referer` header. +This setting may not be used when `server.compression.enabled` is set to `false`. + [[server-cors]]`server.cors:`:: *Default: `false`* Set to `true` to enable CORS support. This setting is required to configure `server.cors.origin`. `server.cors.origin:`:: *Default: none* Specifies origins. "origin" must be an array. To use this setting, you must set `server.cors` to `true`. To accept all origins, use `server.cors.origin: ["*"]`. diff --git a/docs/user/monitoring/images/monitoring-logstash-node.jpg b/docs/user/monitoring/images/monitoring-logstash-node.jpg deleted file mode 100644 index e9c3d15e4818e..0000000000000 Binary files a/docs/user/monitoring/images/monitoring-logstash-node.jpg and /dev/null differ diff --git a/docs/user/monitoring/images/monitoring-logstash-node.png b/docs/user/monitoring/images/monitoring-logstash-node.png new file mode 100644 index 0000000000000..750e683b72aa5 Binary files /dev/null and b/docs/user/monitoring/images/monitoring-logstash-node.png differ diff --git a/docs/user/monitoring/images/monitoring-logstash-nodes.jpg b/docs/user/monitoring/images/monitoring-logstash-nodes.jpg deleted file mode 100644 index 62443179e332f..0000000000000 Binary files a/docs/user/monitoring/images/monitoring-logstash-nodes.jpg and /dev/null differ diff --git a/docs/user/monitoring/images/monitoring-logstash-nodes.png b/docs/user/monitoring/images/monitoring-logstash-nodes.png new file mode 100644 index 0000000000000..559a69ae9b191 Binary files /dev/null and b/docs/user/monitoring/images/monitoring-logstash-nodes.png differ diff --git a/docs/user/monitoring/images/monitoring-logstash-overview.jpg b/docs/user/monitoring/images/monitoring-logstash-overview.jpg deleted file mode 100644 index 07a453d71d2eb..0000000000000 Binary files a/docs/user/monitoring/images/monitoring-logstash-overview.jpg and /dev/null differ diff --git a/docs/user/monitoring/images/monitoring-logstash-overview.png b/docs/user/monitoring/images/monitoring-logstash-overview.png new file mode 100644 index 0000000000000..7b7ea3baaa02d Binary files /dev/null and b/docs/user/monitoring/images/monitoring-logstash-overview.png differ diff --git a/docs/user/monitoring/logstash-details.asciidoc b/docs/user/monitoring/logstash-details.asciidoc index 7f2dac9c47f50..1433a6a036ca8 100644 --- a/docs/user/monitoring/logstash-details.asciidoc +++ b/docs/user/monitoring/logstash-details.asciidoc @@ -9,16 +9,19 @@ If you are monitoring Logstash nodes, click **Overview** in the Logstash section of the *Stack Monitoring* page in {kib}. You can view the overall health of the Logstash nodes. -image::user/monitoring/images/monitoring-logstash-overview.jpg["Logstash Overview",link="images/monitoring-logstash-overview.jpg"] +[role="screenshot"] +image::user/monitoring/images/monitoring-logstash-overview.png["Logstash Overview",link="images/monitoring-logstash-overview.png"] To view Logstash node metrics, click **Nodes**. The Nodes section shows the status of each Logstash node. -image::user/monitoring/images/monitoring-logstash-nodes.jpg["Logstash Nodes",link="images/monitoring-logstash-nodes.jpg"] +[role="screenshot"] +image::user/monitoring/images/monitoring-logstash-nodes.png["Logstash Nodes",link="images/monitoring-logstash-nodes.png"] Click the name of a node to view its statistics over time. -image::user/monitoring/images/monitoring-logstash-node.jpg["Logstash Node View",link="images/monitoring-logstash-node.jpg"] +[role="screenshot"] +image::user/monitoring/images/monitoring-logstash-node.png["Logstash Node View",link="images/monitoring-logstash-node.png"] For more information, see {logstash-ref}/monitoring-logstash.html[Monitoring Logstash]. diff --git a/src/core/server/http/__snapshots__/http_config.test.ts.snap b/src/core/server/http/__snapshots__/http_config.test.ts.snap index 57d9db5e8c1e4..6c690f9da70c3 100644 --- a/src/core/server/http/__snapshots__/http_config.test.ts.snap +++ b/src/core/server/http/__snapshots__/http_config.test.ts.snap @@ -1,17 +1,35 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`accepts valid hostnames: valid host names 1`] = ` +exports[`accepts valid hostnames 1`] = ` Object { - "host1": "www.example.com", - "host2": "8.8.8.8", - "host3": "::1", - "host4": "localhost", + "host": "www.example.com", +} +`; + +exports[`accepts valid hostnames 2`] = ` +Object { + "host": "8.8.8.8", +} +`; + +exports[`accepts valid hostnames 3`] = ` +Object { + "host": "::1", +} +`; + +exports[`accepts valid hostnames 4`] = ` +Object { + "host": "localhost", } `; exports[`has defaults for config 1`] = ` Object { "autoListen": true, + "compression": Object { + "enabled": true, + }, "cors": false, "host": "localhost", "keepaliveTimeout": 120000, @@ -82,3 +100,18 @@ exports[`with TLS throws if TLS is enabled but \`certificate\` is not specified exports[`with TLS throws if TLS is enabled but \`key\` is not specified 1`] = `"[ssl]: must specify [certificate] and [key] when ssl is enabled"`; exports[`with TLS throws if TLS is enabled but \`redirectHttpFromPort\` is equal to \`port\` 1`] = `"Kibana does not accept http traffic to [port] when ssl is enabled (only https is allowed), so [ssl.redirectHttpFromPort] cannot be configured to the same value. Both are [1234]."`; + +exports[`with compression accepts valid referrer whitelist 1`] = ` +Array [ + "www.example.com", + "8.8.8.8", + "::1", + "localhost", +] +`; + +exports[`with compression throws if invalid referrer whitelist 1`] = `"[compression.referrerWhitelist.0]: value is [asdf$%^] but it must be a valid hostname (see RFC 1123)."`; + +exports[`with compression throws if invalid referrer whitelist 2`] = `"[compression.referrerWhitelist]: array size is [0], but cannot be smaller than [1]"`; + +exports[`with compression throws if referrer whitelist is specified and compression is disabled 1`] = `"cannot use [compression.referrerWhitelist] when [compression.enabled] is set to false"`; diff --git a/src/core/server/http/cookie_sesson_storage.test.ts b/src/core/server/http/cookie_session_storage.test.ts similarity index 99% rename from src/core/server/http/cookie_sesson_storage.test.ts rename to src/core/server/http/cookie_session_storage.test.ts index bf0585ad280d5..0e4f3972fe9dc 100644 --- a/src/core/server/http/cookie_sesson_storage.test.ts +++ b/src/core/server/http/cookie_session_storage.test.ts @@ -57,6 +57,7 @@ configService.atPath.mockReturnValue( ssl: { verificationMode: 'none', }, + compression: { enabled: true }, } as any) ); diff --git a/src/core/server/http/http_config.test.ts b/src/core/server/http/http_config.test.ts index 2b627c265dbba..1ee7e13d5e851 100644 --- a/src/core/server/http/http_config.test.ts +++ b/src/core/server/http/http_config.test.ts @@ -21,6 +21,9 @@ import { config, HttpConfig } from '.'; import { Env } from '../config'; import { getEnvOptions } from '../config/__mocks__/env'; +const validHostnames = ['www.example.com', '8.8.8.8', '::1', 'localhost']; +const invalidHostname = 'asdf$%^'; + test('has defaults for config', () => { const httpSchema = config.schema; const obj = {}; @@ -28,18 +31,16 @@ test('has defaults for config', () => { }); test('accepts valid hostnames', () => { - const { host: host1 } = config.schema.validate({ host: 'www.example.com' }); - const { host: host2 } = config.schema.validate({ host: '8.8.8.8' }); - const { host: host3 } = config.schema.validate({ host: '::1' }); - const { host: host4 } = config.schema.validate({ host: 'localhost' }); - - expect({ host1, host2, host3, host4 }).toMatchSnapshot('valid host names'); + for (const val of validHostnames) { + const { host } = config.schema.validate({ host: val }); + expect({ host }).toMatchSnapshot(); + } }); test('throws if invalid hostname', () => { const httpSchema = config.schema; const obj = { - host: 'asdf$%^', + host: invalidHostname, }; expect(() => httpSchema.validate(obj)).toThrowErrorMatchingSnapshot(); }); @@ -296,3 +297,44 @@ describe('with TLS', () => { expect(httpConfig.ssl.rejectUnauthorized).toBe(true); }); }); + +describe('with compression', () => { + test('accepts valid referrer whitelist', () => { + const { + compression: { referrerWhitelist }, + } = config.schema.validate({ + compression: { + referrerWhitelist: validHostnames, + }, + }); + + expect(referrerWhitelist).toMatchSnapshot(); + }); + + test('throws if invalid referrer whitelist', () => { + const httpSchema = config.schema; + const invalidHostnames = { + compression: { + referrerWhitelist: [invalidHostname], + }, + }; + const emptyArray = { + compression: { + referrerWhitelist: [], + }, + }; + expect(() => httpSchema.validate(invalidHostnames)).toThrowErrorMatchingSnapshot(); + expect(() => httpSchema.validate(emptyArray)).toThrowErrorMatchingSnapshot(); + }); + + test('throws if referrer whitelist is specified and compression is disabled', () => { + const httpSchema = config.schema; + const obj = { + compression: { + enabled: false, + referrerWhitelist: validHostnames, + }, + }; + expect(() => httpSchema.validate(obj)).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/src/core/server/http/http_config.ts b/src/core/server/http/http_config.ts index c4a61aaf83ac7..89676380610a9 100644 --- a/src/core/server/http/http_config.ts +++ b/src/core/server/http/http_config.ts @@ -72,12 +72,26 @@ export const config = { socketTimeout: schema.number({ defaultValue: 120000, }), + compression: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + referrerWhitelist: schema.maybe( + schema.arrayOf( + schema.string({ + hostname: true, + }), + { minSize: 1 } + ) + ), + }), }, { validate: rawConfig => { if (!rawConfig.basePath && rawConfig.rewriteBasePath) { return 'cannot use [rewriteBasePath] when [basePath] is not specified'; } + if (!rawConfig.compression.enabled && rawConfig.compression.referrerWhitelist) { + return 'cannot use [compression.referrerWhitelist] when [compression.enabled] is set to false'; + } if ( rawConfig.ssl.enabled && @@ -109,6 +123,7 @@ export class HttpConfig { public publicDir: string; public defaultRoute?: string; public ssl: SslConfig; + public compression: { enabled: boolean; referrerWhitelist?: string[] }; /** * @internal @@ -126,5 +141,6 @@ export class HttpConfig { this.publicDir = env.staticFilesDir; this.ssl = new SslConfig(rawConfig.ssl || {}); this.defaultRoute = rawConfig.defaultRoute; + this.compression = rawConfig.compression; } } diff --git a/src/core/server/http/http_server.test.ts b/src/core/server/http/http_server.test.ts index df47ffdc1176b..27d9f530050be 100644 --- a/src/core/server/http/http_server.test.ts +++ b/src/core/server/http/http_server.test.ts @@ -53,6 +53,7 @@ beforeEach(() => { maxPayload: new ByteSizeValue(1024), port: 10002, ssl: { enabled: false }, + compression: { enabled: true }, } as HttpConfig; configWithSSL = { @@ -578,6 +579,90 @@ test('exposes route details of incoming request to a route handler', async () => }); }); +describe('conditional compression', () => { + async function setupServer(innerConfig: HttpConfig) { + const { registerRouter, server: innerServer } = await server.setup(innerConfig); + const router = new Router('', logger, enhanceWithContext); + // we need the large body here so that compression would normally be used + const largeRequest = { + body: 'hello'.repeat(500), + headers: { 'Content-Type': 'text/html; charset=UTF-8' }, + }; + router.get({ path: '/', validate: false }, (_context, _req, res) => res.ok(largeRequest)); + registerRouter(router); + await server.start(); + return innerServer.listener; + } + + test('with `compression.enabled: true`', async () => { + const listener = await setupServer(config); + + const response = await supertest(listener) + .get('/') + .set('accept-encoding', 'gzip'); + + expect(response.header).toHaveProperty('content-encoding', 'gzip'); + }); + + test('with `compression.enabled: false`', async () => { + const listener = await setupServer({ + ...config, + compression: { enabled: false }, + }); + + const response = await supertest(listener) + .get('/') + .set('accept-encoding', 'gzip'); + + expect(response.header).not.toHaveProperty('content-encoding'); + }); + + describe('with defined `compression.referrerWhitelist`', () => { + let listener: Server; + beforeEach(async () => { + listener = await setupServer({ + ...config, + compression: { enabled: true, referrerWhitelist: ['foo'] }, + }); + }); + + test('enables compression for no referer', async () => { + const response = await supertest(listener) + .get('/') + .set('accept-encoding', 'gzip'); + + expect(response.header).toHaveProperty('content-encoding', 'gzip'); + }); + + test('enables compression for whitelisted referer', async () => { + const response = await supertest(listener) + .get('/') + .set('accept-encoding', 'gzip') + .set('referer', 'http://foo:1234'); + + expect(response.header).toHaveProperty('content-encoding', 'gzip'); + }); + + test('disables compression for non-whitelisted referer', async () => { + const response = await supertest(listener) + .get('/') + .set('accept-encoding', 'gzip') + .set('referer', 'http://bar:1234'); + + expect(response.header).not.toHaveProperty('content-encoding'); + }); + + test('disables compression for invalid referer', async () => { + const response = await supertest(listener) + .get('/') + .set('accept-encoding', 'gzip') + .set('referer', 'http://asdf$%^'); + + expect(response.header).not.toHaveProperty('content-encoding'); + }); + }); +}); + test('exposes route details of incoming request to a route handler (POST + payload options)', async () => { const { registerRouter, server: innerServer } = await server.setup(config); diff --git a/src/core/server/http/http_server.ts b/src/core/server/http/http_server.ts index a587eed1f54ec..f77184fb79ab6 100644 --- a/src/core/server/http/http_server.ts +++ b/src/core/server/http/http_server.ts @@ -18,6 +18,7 @@ */ import { Request, Server } from 'hapi'; +import url from 'url'; import { Logger, LoggerFactory } from '../logging'; import { HttpConfig } from './http_config'; @@ -96,6 +97,7 @@ export class HttpServer { const basePathService = new BasePath(config.basePath); this.setupBasePathRewrite(config, basePathService); + this.setupConditionalCompression(config); return { registerRouter: this.registerRouter.bind(this), @@ -187,6 +189,33 @@ export class HttpServer { }); } + private setupConditionalCompression(config: HttpConfig) { + if (this.server === undefined) { + throw new Error('Server is not created yet'); + } + + const { enabled, referrerWhitelist: list } = config.compression; + if (!enabled) { + this.log.debug('HTTP compression is disabled'); + this.server.ext('onRequest', (request, h) => { + request.info.acceptEncoding = ''; + return h.continue; + }); + } else if (list) { + this.log.debug(`HTTP compression is only enabled for any referrer in the following: ${list}`); + this.server.ext('onRequest', (request, h) => { + const { referrer } = request.info; + if (referrer !== '') { + const { hostname } = url.parse(referrer); + if (!hostname || !list.includes(hostname)) { + request.info.acceptEncoding = ''; + } + } + return h.continue; + }); + } + } + private registerOnPostAuth(fn: OnPostAuthHandler) { if (this.server === undefined) { throw new Error('Server is not created yet'); diff --git a/src/core/server/http/http_tools.test.ts b/src/core/server/http/http_tools.test.ts index bed0ac8d77094..7195717d9d3fc 100644 --- a/src/core/server/http/http_tools.test.ts +++ b/src/core/server/http/http_tools.test.ts @@ -90,6 +90,7 @@ describe('timeouts', () => { host: '127.0.0.1', maxPayload: new ByteSizeValue(1024), ssl: {}, + compression: { enabled: true }, } as HttpConfig); registerRouter(router); diff --git a/src/core/server/http/integration_tests/lifecycle.test.ts b/src/core/server/http/integration_tests/lifecycle.test.ts index 7c4a0097456ca..d5f9343f3e981 100644 --- a/src/core/server/http/integration_tests/lifecycle.test.ts +++ b/src/core/server/http/integration_tests/lifecycle.test.ts @@ -51,6 +51,7 @@ configService.atPath.mockReturnValue( ssl: { enabled: false, }, + compression: { enabled: true }, } as any) ); diff --git a/src/core/server/http/integration_tests/router.test.ts b/src/core/server/http/integration_tests/router.test.ts index 481d8e1bbf49b..463f941ba84b4 100644 --- a/src/core/server/http/integration_tests/router.test.ts +++ b/src/core/server/http/integration_tests/router.test.ts @@ -51,6 +51,7 @@ configService.atPath.mockReturnValue( ssl: { enabled: false, }, + compression: { enabled: true }, } as any) ); diff --git a/src/core/server/legacy/config/__snapshots__/legacy_object_to_config_adapter.test.ts.snap b/src/core/server/legacy/config/__snapshots__/legacy_object_to_config_adapter.test.ts.snap index e59312cf8a948..172feec674677 100644 --- a/src/core/server/legacy/config/__snapshots__/legacy_object_to_config_adapter.test.ts.snap +++ b/src/core/server/legacy/config/__snapshots__/legacy_object_to_config_adapter.test.ts.snap @@ -4,6 +4,9 @@ exports[`#get correctly handles server config.: default 1`] = ` Object { "autoListen": true, "basePath": "/abc", + "compression": Object { + "enabled": true, + }, "cors": false, "host": "host", "keepaliveTimeout": 5000, @@ -23,6 +26,9 @@ exports[`#get correctly handles server config.: disabled ssl 1`] = ` Object { "autoListen": true, "basePath": "/abc", + "compression": Object { + "enabled": true, + }, "cors": false, "host": "host", "keepaliveTimeout": 5000, diff --git a/src/core/server/legacy/config/legacy_object_to_config_adapter.test.ts b/src/core/server/legacy/config/legacy_object_to_config_adapter.test.ts index 202a1e471af9b..201f761701a35 100644 --- a/src/core/server/legacy/config/legacy_object_to_config_adapter.test.ts +++ b/src/core/server/legacy/config/legacy_object_to_config_adapter.test.ts @@ -74,6 +74,7 @@ describe('#get', () => { port: 1234, rewriteBasePath: false, ssl: { enabled: true, keyPassphrase: 'some-phrase', someNewValue: 'new' }, + compression: { enabled: true }, someNotSupportedValue: 'val', }, }); @@ -90,6 +91,7 @@ describe('#get', () => { port: 1234, rewriteBasePath: false, ssl: { enabled: false, certificate: 'cert', key: 'key' }, + compression: { enabled: true }, someNotSupportedValue: 'val', }, }); diff --git a/src/core/server/legacy/config/legacy_object_to_config_adapter.ts b/src/core/server/legacy/config/legacy_object_to_config_adapter.ts index 0901458866fb3..6f0757dece165 100644 --- a/src/core/server/legacy/config/legacy_object_to_config_adapter.ts +++ b/src/core/server/legacy/config/legacy_object_to_config_adapter.ts @@ -70,6 +70,7 @@ export class LegacyObjectToConfigAdapter extends ObjectToConfigAdapter { ssl: configValue.ssl, keepaliveTimeout: configValue.keepaliveTimeout, socketTimeout: configValue.socketTimeout, + compression: configValue.compression, }; } diff --git a/src/legacy/core_plugins/data/public/index.scss b/src/legacy/core_plugins/data/public/index.scss index 94f02fe2d6049..78673525a5483 100644 --- a/src/legacy/core_plugins/data/public/index.scss +++ b/src/legacy/core_plugins/data/public/index.scss @@ -2,8 +2,4 @@ @import './query/query_bar/index'; -@import 'src/plugins/data/public/ui/filter_bar/index'; - -@import 'src/plugins/data/public/ui/typeahead/index'; - -@import './search/search_bar/index'; +@import '../../../../plugins/data/public/ui/index' \ No newline at end of file diff --git a/src/legacy/core_plugins/data/public/search/search_bar/_index.scss b/src/legacy/core_plugins/data/public/search/search_bar/_index.scss deleted file mode 100644 index 0c42a9426e41e..0000000000000 --- a/src/legacy/core_plugins/data/public/search/search_bar/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'components/saved_query_management/index'; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx index 0605b156a9669..b4fd3f4578544 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx @@ -38,7 +38,6 @@ const mockTimeHistory = { jest.mock('../../../../../../../plugins/data/public', () => { return { FilterBar: () =>
, - createSavedQueryService: () => {}, }; }); @@ -106,6 +105,11 @@ function wrapSearchBarInContext(testProps: any) { notifications: startMock.notifications, http: startMock.http, storage: createMockStorage(), + data: { + query: { + savedQueries: {}, + }, + }, }; return ( diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index da08165289afc..b24ea763ace75 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -26,8 +26,6 @@ import { get, isEqual } from 'lodash'; import { IndexPattern } from '../../../../../data/public'; import { QueryBarTopRow } from '../../../query'; -import { SavedQueryMeta, SaveQueryForm } from './saved_query_management/save_query_form'; -import { SavedQueryManagementComponent } from './saved_query_management/saved_query_management_component'; import { withKibana, @@ -40,10 +38,11 @@ import { esFilters, TimeHistoryContract, FilterBar, - SavedQueryService, - createSavedQueryService, SavedQuery, SavedQueryAttributes, + SavedQueryMeta, + SaveQueryForm, + SavedQueryManagementComponent, } from '../../../../../../../plugins/data/public'; interface SearchBarInjectedDeps { @@ -112,8 +111,8 @@ class SearchBarUI extends Component { showAutoRefreshOnly: false, }; - private savedQueryService!: SavedQueryService; private services = this.props.kibana.services; + private savedQueryService = this.services.data.query.savedQueries; public filterBarRef: Element | null = null; public filterBarWrapperRef: Element | null = null; @@ -368,9 +367,6 @@ class SearchBarUI extends Component { this.setFilterBarHeight(); this.ro.observe(this.filterBarRef); } - if (this.services.savedObjects) { - this.savedQueryService = createSavedQueryService(this.services.savedObjects.client); - } } public componentDidUpdate() { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html index 0b842fbfaeddc..4f41ab5d4fad6 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html @@ -12,7 +12,6 @@ show-filter-bar="showFilterBar()" show-save-query="showSaveQuery" - filters="model.filters" query="model.query" saved-query="savedQuery" screen-title="screenTitle" diff --git a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_index_pattern_title.tsx b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_index_pattern_title.tsx index 23679c4db5a7f..fb5c3c8d45ce8 100644 --- a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_index_pattern_title.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_index_pattern_title.tsx @@ -20,7 +20,7 @@ import React from 'react'; import { EuiToolTip, EuiFlexItem, EuiFlexGroup, EuiTitle, EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; - +import { i18n } from '@kbn/i18n'; export interface DiscoverIndexPatternTitleProps { /** * determines whether the change link is displayed @@ -65,18 +65,19 @@ export function DiscoverIndexPatternTitle({ } > onChange()} - > - ( - - ) - + iconSide="right" + iconType="arrowDown" + color="text" + /> )} diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/color_rules.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/color_rules.js index fcd80ae8844c0..d649777b56438 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/color_rules.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/color_rules.js @@ -46,7 +46,7 @@ class ColorRulesUI extends Component { const part = {}; part[name] = cast(_.get(e, '[0].value', _.get(e, 'target.value'))); if (part[name] === 'undefined') part[name] = undefined; - if (part[name] === NaN) part[name] = undefined; + if (isNaN(part[name])) part[name] = undefined; handleChange(_.assign({}, item, part)); }; } diff --git a/src/legacy/server/config/schema.js b/src/legacy/server/config/schema.js index 3f9b897730f51..9e06fa178c5d6 100644 --- a/src/legacy/server/config/schema.js +++ b/src/legacy/server/config/schema.js @@ -106,6 +106,7 @@ export default () => Joi.object({ maxPayloadBytes: HANDLED_IN_NEW_PLATFORM, socketTimeout: HANDLED_IN_NEW_PLATFORM, ssl: HANDLED_IN_NEW_PLATFORM, + compression: HANDLED_IN_NEW_PLATFORM, }).default(), uiSettings: HANDLED_IN_NEW_PLATFORM, diff --git a/src/plugins/data/public/index_patterns/index.ts b/src/plugins/data/public/index_patterns/index.ts index 7492f9372e44a..a22a8b8d85813 100644 --- a/src/plugins/data/public/index_patterns/index.ts +++ b/src/plugins/data/public/index_patterns/index.ts @@ -17,12 +17,12 @@ * under the License. */ -import { IndexPatternMissingIndices } from './errors'; import { ILLEGAL_CHARACTERS_KEY, CONTAINS_SPACES_KEY, ILLEGAL_CHARACTERS_VISIBLE, ILLEGAL_CHARACTERS, + IndexPatternMissingIndices, validateIndexPattern, } from './lib'; import { getRoutes, getFromSavedObject } from './utils'; diff --git a/src/plugins/data/public/index_patterns/errors.ts b/src/plugins/data/public/index_patterns/lib/errors.ts similarity index 95% rename from src/plugins/data/public/index_patterns/errors.ts rename to src/plugins/data/public/index_patterns/lib/errors.ts index 3eb43eaf460cc..12efab7a2ca40 100644 --- a/src/plugins/data/public/index_patterns/errors.ts +++ b/src/plugins/data/public/index_patterns/lib/errors.ts @@ -19,7 +19,7 @@ /* eslint-disable */ -import { KbnError } from '../../../kibana_utils/public'; +import { KbnError } from '../../../../kibana_utils/public'; /** * Tried to call a method that relies on SearchSource having an indexPattern assigned diff --git a/src/plugins/data/public/index_patterns/lib/index.ts b/src/plugins/data/public/index_patterns/lib/index.ts index 0f667e846af91..626881b721c33 100644 --- a/src/plugins/data/public/index_patterns/lib/index.ts +++ b/src/plugins/data/public/index_patterns/lib/index.ts @@ -20,3 +20,4 @@ export { getTitle } from './get_title'; export * from './types'; export { validateIndexPattern } from './validate_index_pattern'; +export { IndexPatternMissingIndices } from './errors'; diff --git a/src/plugins/data/public/suggestions_provider/value_suggestions.test.ts b/src/plugins/data/public/suggestions_provider/value_suggestions.test.ts index 02aaaaf6f4689..9089105b4e3a8 100644 --- a/src/plugins/data/public/suggestions_provider/value_suggestions.test.ts +++ b/src/plugins/data/public/suggestions_provider/value_suggestions.test.ts @@ -17,9 +17,6 @@ * under the License. */ -// TODO: remove when index patterns are moved here. -jest.mock('ui/new_platform'); - import { stubIndexPattern, stubFields } from '../stubs'; import { getSuggestionsProvider } from './value_suggestions'; import { IUiSettingsClient } from 'kibana/public'; diff --git a/src/plugins/data/public/ui/_index.scss b/src/plugins/data/public/ui/_index.scss new file mode 100644 index 0000000000000..1fc673d5450c2 --- /dev/null +++ b/src/plugins/data/public/ui/_index.scss @@ -0,0 +1,6 @@ + +@import './filter_bar/index'; + +@import './typeahead/index'; + +@import './saved_query_management/index'; diff --git a/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_editor_utils.test.ts b/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_editor_utils.test.ts index 2cc7f16cfe261..fb3fbc10d7455 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_editor_utils.test.ts +++ b/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_editor_utils.test.ts @@ -36,8 +36,6 @@ import { import { existsOperator, isBetweenOperator, isOneOfOperator, isOperator } from './filter_operators'; -jest.mock('ui/new_platform'); - describe('Filter editor utils', () => { describe('getFieldFromFilter', () => { it('should return the field from the filter', () => { diff --git a/src/plugins/data/public/ui/index.ts b/src/plugins/data/public/ui/index.ts index 6fb8e260dd720..d549ceabde86d 100644 --- a/src/plugins/data/public/ui/index.ts +++ b/src/plugins/data/public/ui/index.ts @@ -21,5 +21,7 @@ export { SuggestionsComponent } from './typeahead/suggestions_component'; export { IndexPatternSelect } from './index_pattern_select'; export { FilterBar } from './filter_bar'; export { applyFiltersPopover } from './apply_filters'; -// temp export +// temp export - will be removed as final components are migrated to NP export { QueryLanguageSwitcher } from './query_string_input/language_switcher'; +export { SavedQueryManagementComponent } from './saved_query_management'; +export { SaveQueryForm, SavedQueryMeta } from './saved_query_form'; diff --git a/src/plugins/data/public/ui/saved_query_form/index.ts b/src/plugins/data/public/ui/saved_query_form/index.ts new file mode 100644 index 0000000000000..c52b6c92ef6d3 --- /dev/null +++ b/src/plugins/data/public/ui/saved_query_form/index.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 { SavedQueryMeta, SaveQueryForm } from '../saved_query_form/save_query_form'; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx b/src/plugins/data/public/ui/saved_query_form/save_query_form.tsx similarity index 98% rename from src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx rename to src/plugins/data/public/ui/saved_query_form/save_query_form.tsx index 4515c71baa267..f9a0ae4e803c4 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx +++ b/src/plugins/data/public/ui/saved_query_form/save_query_form.tsx @@ -35,11 +35,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { sortBy, isEqual } from 'lodash'; -import { - SavedQuery, - SavedQueryAttributes, - SavedQueryService, -} from '../../../../../../../../plugins/data/public'; +import { SavedQuery, SavedQueryAttributes, SavedQueryService } from '../..'; interface Props { savedQuery?: SavedQueryAttributes; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/_index.scss b/src/plugins/data/public/ui/saved_query_management/_index.scss similarity index 100% rename from src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/_index.scss rename to src/plugins/data/public/ui/saved_query_management/_index.scss diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/_saved_query_list_item.scss b/src/plugins/data/public/ui/saved_query_management/_saved_query_list_item.scss similarity index 100% rename from src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/_saved_query_list_item.scss rename to src/plugins/data/public/ui/saved_query_management/_saved_query_list_item.scss diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/_saved_query_management_component.scss b/src/plugins/data/public/ui/saved_query_management/_saved_query_management_component.scss similarity index 100% rename from src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/_saved_query_management_component.scss rename to src/plugins/data/public/ui/saved_query_management/_saved_query_management_component.scss diff --git a/src/plugins/data/public/ui/saved_query_management/index.ts b/src/plugins/data/public/ui/saved_query_management/index.ts new file mode 100644 index 0000000000000..be5ea0cd7c8e7 --- /dev/null +++ b/src/plugins/data/public/ui/saved_query_management/index.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 { SavedQueryManagementComponent } from './saved_query_management_component'; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx b/src/plugins/data/public/ui/saved_query_management/saved_query_list_item.tsx similarity index 98% rename from src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx rename to src/plugins/data/public/ui/saved_query_management/saved_query_list_item.tsx index bee32c347e6f4..09eeb8359dde7 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx +++ b/src/plugins/data/public/ui/saved_query_management/saved_query_list_item.tsx @@ -22,7 +22,7 @@ import { EuiListGroupItem, EuiConfirmModal, EuiOverlayMask, EuiIconTip } from '@ import React, { Fragment, useState } from 'react'; import classNames from 'classnames'; import { i18n } from '@kbn/i18n'; -import { SavedQuery } from '../../../../../../../../plugins/data/public'; +import { SavedQuery } from '../..'; interface Props { savedQuery: SavedQuery; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx b/src/plugins/data/public/ui/saved_query_management/saved_query_management_component.tsx similarity index 99% rename from src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx rename to src/plugins/data/public/ui/saved_query_management/saved_query_management_component.tsx index 65ed92c40741b..2a11531ee336d 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx +++ b/src/plugins/data/public/ui/saved_query_management/saved_query_management_component.tsx @@ -35,7 +35,7 @@ import { import { i18n } from '@kbn/i18n'; import React, { FunctionComponent, useEffect, useState, Fragment } from 'react'; import { sortBy } from 'lodash'; -import { SavedQuery, SavedQueryService } from '../../../../../../../../plugins/data/public'; +import { SavedQuery, SavedQueryService } from '../..'; import { SavedQueryListItem } from './saved_query_list_item'; const perPage = 50; diff --git a/test/api_integration/apis/core/index.js b/test/api_integration/apis/core/index.js index d617b2ad07351..7c4047ac1f537 100644 --- a/test/api_integration/apis/core/index.js +++ b/test/api_integration/apis/core/index.js @@ -16,21 +16,55 @@ * specific language governing permissions and limitations * under the License. */ +import expect from '@kbn/expect'; export default function ({ getService }) { const supertest = getService('supertest'); - describe('core request context', () => { - it('provides access to elasticsearch', async () => ( - await supertest - .get('/requestcontext/elasticsearch') - .expect(200, 'Elasticsearch: true') - )); + describe('core', () => { + describe('request context', () => { + it('provides access to elasticsearch', async () => ( + await supertest + .get('/requestcontext/elasticsearch') + .expect(200, 'Elasticsearch: true') + )); - it('provides access to SavedObjects client', async () => ( - await supertest - .get('/requestcontext/savedobjectsclient') - .expect(200, 'SavedObjects client: {"page":1,"per_page":20,"total":0,"saved_objects":[]}') - )); + it('provides access to SavedObjects client', async () => ( + await supertest + .get('/requestcontext/savedobjectsclient') + .expect(200, 'SavedObjects client: {"page":1,"per_page":20,"total":0,"saved_objects":[]}') + )); + }); + + describe('compression', () => { + it(`uses compression when there isn't a referer`, async () => { + await supertest + .get('/app/kibana') + .set('accept-encoding', 'gzip') + .then(response => { + expect(response.headers).to.have.property('content-encoding', 'gzip'); + }); + }); + + it(`uses compression when there is a whitelisted referer`, async () => { + await supertest + .get('/app/kibana') + .set('accept-encoding', 'gzip') + .set('referer', 'https://some-host.com') + .then(response => { + expect(response.headers).to.have.property('content-encoding', 'gzip'); + }); + }); + + it(`doesn't use compression when there is a non-whitelisted referer`, async () => { + await supertest + .get('/app/kibana') + .set('accept-encoding', 'gzip') + .set('referer', 'https://other.some-host.com') + .then(response => { + expect(response.headers).not.to.have.property('content-encoding'); + }); + }); + }); }); } diff --git a/test/api_integration/apis/index.js b/test/api_integration/apis/index.js index 9f2672959390c..de36ee678b10e 100644 --- a/test/api_integration/apis/index.js +++ b/test/api_integration/apis/index.js @@ -34,5 +34,6 @@ export default function ({ loadTestFile }) { loadTestFile(require.resolve('./status')); loadTestFile(require.resolve('./stats')); loadTestFile(require.resolve('./ui_metric')); + loadTestFile(require.resolve('./core')); }); } diff --git a/test/api_integration/config.js b/test/api_integration/config.js index d14630f932bf6..bd385b1ac8b96 100644 --- a/test/api_integration/config.js +++ b/test/api_integration/config.js @@ -40,6 +40,7 @@ export default async function ({ readConfigFile }) { '--optimize.enabled=false', '--elasticsearch.healthCheck.delay=3600000', '--server.xsrf.disableProtection=true', + '--server.compression.referrerWhitelist=["some-host.com"]', ], }, }; diff --git a/vars/kibanaPipeline.groovy b/vars/kibanaPipeline.groovy index 90df352e18a00..0058f21f2356b 100644 --- a/vars/kibanaPipeline.groovy +++ b/vars/kibanaPipeline.groovy @@ -11,6 +11,10 @@ def withWorkers(name, preWorkerClosure = {}, workerClosures = [:]) { nextWorker++ return { + // This delay helps smooth out CPU load caused by ES/Kibana instances starting up at the same time + def delay = (workerNumber-1)*20 + sleep(delay) + workerClosure(workerNumber) } } diff --git a/x-pack/legacy/plugins/apm/index.ts b/x-pack/legacy/plugins/apm/index.ts index 1784ed22a2b4d..91745246687d9 100644 --- a/x-pack/legacy/plugins/apm/index.ts +++ b/x-pack/legacy/plugins/apm/index.ts @@ -31,7 +31,7 @@ export const apm: LegacyPluginInitializer = kibana => { order: 8100 }, styleSheetPaths: resolve(__dirname, 'public/index.scss'), - home: ['plugins/apm/register_feature'], + home: ['plugins/apm/legacy_register_feature'], // TODO: get proper types injectDefaultVars(server: Server) { @@ -43,7 +43,6 @@ export const apm: LegacyPluginInitializer = kibana => { apmServiceMapEnabled: config.get('xpack.apm.serviceMapEnabled') }; }, - hacks: ['plugins/apm/hacks/toggle_app_link_in_nav'], savedObjectSchemas: { 'apm-services-telemetry': { isNamespaceAgnostic: true diff --git a/x-pack/legacy/plugins/apm/public/components/app/Home/Home.test.tsx b/x-pack/legacy/plugins/apm/public/components/app/Home/Home.test.tsx index 7a23c9f7de842..272e2561b7fd7 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Home/Home.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Home/Home.test.tsx @@ -7,15 +7,26 @@ import { shallow } from 'enzyme'; import React from 'react'; import { Home } from '../Home'; - -jest.mock('ui/new_platform'); +import { MockPluginContextWrapper } from '../../../utils/testHelpers'; describe('Home component', () => { it('should render services', () => { - expect(shallow()).toMatchSnapshot(); + expect( + shallow( + + + + ) + ).toMatchSnapshot(); }); it('should render traces', () => { - expect(shallow()).toMatchSnapshot(); + expect( + shallow( + + + + ) + ).toMatchSnapshot(); }); }); diff --git a/x-pack/legacy/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap b/x-pack/legacy/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap index d876cada08d69..664a71c934a4e 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap +++ b/x-pack/legacy/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap @@ -1,129 +1,35 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Home component should render services 1`] = ` -
- - - - -

- APM -

-
-
- - - - Settings - - - - - - -
-
- - - - Services - - - - - Traces - - - - - -
+ + + `; exports[`Home component should render traces 1`] = ` -
- - - - -

- APM -

-
-
- - - - Settings - - - - - - -
-
- - - - Services - - - - - Traces - - - - - -
+ + + `; diff --git a/x-pack/legacy/plugins/apm/public/components/app/Home/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/Home/index.tsx index f562f158f9bf8..4096d69673636 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Home/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Home/index.tsx @@ -13,7 +13,6 @@ import { EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { npStart } from 'ui/new_platform'; import React from 'react'; import { $ElementType } from 'utility-types'; import { ApmHeader } from '../../shared/ApmHeader'; @@ -26,46 +25,54 @@ import { EuiTabLink } from '../../shared/EuiTabLink'; import { SettingsLink } from '../../shared/Links/apm/SettingsLink'; import { ServiceMapLink } from '../../shared/Links/apm/ServiceMapLink'; import { ServiceMap } from '../ServiceMap'; +import { usePlugins } from '../../../new-platform/plugin'; -const homeTabs = [ - { - link: ( - - {i18n.translate('xpack.apm.home.servicesTabLabel', { - defaultMessage: 'Services' - })} - - ), - render: () => , - name: 'services' - }, - { - link: ( - - {i18n.translate('xpack.apm.home.tracesTabLabel', { - defaultMessage: 'Traces' - })} - - ), - render: () => , - name: 'traces' +function getHomeTabs({ + apmServiceMapEnabled = false +}: { + apmServiceMapEnabled: boolean; +}) { + const homeTabs = [ + { + link: ( + + {i18n.translate('xpack.apm.home.servicesTabLabel', { + defaultMessage: 'Services' + })} + + ), + render: () => , + name: 'services' + }, + { + link: ( + + {i18n.translate('xpack.apm.home.tracesTabLabel', { + defaultMessage: 'Traces' + })} + + ), + render: () => , + name: 'traces' + } + ]; + + if (apmServiceMapEnabled) { + homeTabs.push({ + link: ( + + {i18n.translate('xpack.apm.home.serviceMapTabLabel', { + defaultMessage: 'Service Map' + })} + + ), + render: () => , + name: 'service-map' + }); } -]; -if (npStart.core.injectedMetadata.getInjectedVar('apmServiceMapEnabled')) { - homeTabs.push({ - link: ( - - {i18n.translate('xpack.apm.home.serviceMapTabLabel', { - defaultMessage: 'Service Map' - })} - - ), - render: () => , - name: 'service-map' - }); + return homeTabs; } - const SETTINGS_LINK_LABEL = i18n.translate('xpack.apm.settingsLinkLabel', { defaultMessage: 'Settings' }); @@ -75,6 +82,9 @@ interface Props { } export function Home({ tab }: Props) { + const { apm } = usePlugins(); + const { apmServiceMapEnabled } = apm.config; + const homeTabs = getHomeTabs({ apmServiceMapEnabled }); const selectedTab = homeTabs.find( homeTab => homeTab.name === tab ) as $ElementType; diff --git a/x-pack/legacy/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.tsx b/x-pack/legacy/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.tsx index 9d42108f73669..6568c9151bfd9 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.tsx @@ -9,8 +9,11 @@ import React from 'react'; import { LegacyCoreStart } from 'src/core/public'; import { useKibanaCore } from '../../../../../observability/public'; import { getAPMHref } from '../../shared/Links/apm/APMLink'; -import { Breadcrumb, ProvideBreadcrumbs } from './ProvideBreadcrumbs'; -import { routes } from './route_config'; +import { + Breadcrumb, + ProvideBreadcrumbs, + BreadcrumbRoute +} from './ProvideBreadcrumbs'; interface Props { location: Location; @@ -49,7 +52,11 @@ class UpdateBreadcrumbsComponent extends React.Component { } } -export function UpdateBreadcrumbs() { +interface UpdateBreadcrumbsProps { + routes: BreadcrumbRoute[]; +} + +export function UpdateBreadcrumbs({ routes }: UpdateBreadcrumbsProps) { const core = useKibanaCore(); return ( - + ); expect(coreMock.chrome.setBreadcrumbs).toHaveBeenCalledTimes(1); diff --git a/x-pack/legacy/plugins/apm/public/components/app/Main/route_config/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/Main/route_config/index.tsx index 4769fe6400ac1..a410febce5695 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Main/route_config/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Main/route_config/index.tsx @@ -7,7 +7,6 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { Redirect, RouteComponentProps } from 'react-router-dom'; -import { npStart } from 'ui/new_platform'; import { SERVICE_NODE_NAME_MISSING } from '../../../../../common/service_nodes'; import { ErrorGroupDetails } from '../../ErrorGroupDetails'; import { ServiceDetails } from '../../ServiceDetails'; @@ -42,172 +41,180 @@ const renderAsRedirectTo = (to: string) => { ); }; -export const routes: BreadcrumbRoute[] = [ - { - exact: true, - path: '/', - render: renderAsRedirectTo('/services'), - breadcrumb: 'APM', - name: RouteName.HOME - }, - { - exact: true, - path: '/services', - component: () => , - breadcrumb: i18n.translate('xpack.apm.breadcrumb.servicesTitle', { - defaultMessage: 'Services' - }), - name: RouteName.SERVICES - }, - { - exact: true, - path: '/traces', - component: () => , - breadcrumb: i18n.translate('xpack.apm.breadcrumb.tracesTitle', { - defaultMessage: 'Traces' - }), - name: RouteName.TRACES - }, - { - exact: true, - path: '/settings', - render: renderAsRedirectTo('/settings/agent-configuration'), - breadcrumb: i18n.translate('xpack.apm.breadcrumb.listSettingsTitle', { - defaultMessage: 'Settings' - }), - name: RouteName.SETTINGS - }, - { - exact: true, - path: '/settings/apm-indices', - component: () => ( - - - - ), - breadcrumb: i18n.translate('xpack.apm.breadcrumb.settings.indicesTitle', { - defaultMessage: 'Indices' - }), - name: RouteName.INDICES - }, - { - exact: true, - path: '/settings/agent-configuration', - component: () => ( - - - - ), - breadcrumb: i18n.translate( - 'xpack.apm.breadcrumb.settings.agentConfigurationTitle', - { - defaultMessage: 'Agent Configuration' - } - ), - name: RouteName.AGENT_CONFIGURATION - }, - { - exact: true, - path: '/services/:serviceName', - breadcrumb: ({ match }) => match.params.serviceName, - render: (props: RouteComponentProps) => - renderAsRedirectTo( - `/services/${props.match.params.serviceName}/transactions` - )(props), - name: RouteName.SERVICE - }, - // errors - { - exact: true, - path: '/services/:serviceName/errors/:groupId', - component: ErrorGroupDetails, - breadcrumb: ({ match }) => match.params.groupId, - name: RouteName.ERROR - }, - { - exact: true, - path: '/services/:serviceName/errors', - component: () => , - breadcrumb: i18n.translate('xpack.apm.breadcrumb.errorsTitle', { - defaultMessage: 'Errors' - }), - name: RouteName.ERRORS - }, - // transactions - { - exact: true, - path: '/services/:serviceName/transactions', - component: () => , - breadcrumb: i18n.translate('xpack.apm.breadcrumb.transactionsTitle', { - defaultMessage: 'Transactions' - }), - name: RouteName.TRANSACTIONS - }, - // metrics - { - exact: true, - path: '/services/:serviceName/metrics', - component: () => , - breadcrumb: metricsBreadcrumb, - name: RouteName.METRICS - }, - // service nodes, only enabled for java agents for now - { - exact: true, - path: '/services/:serviceName/nodes', - component: () => , - breadcrumb: i18n.translate('xpack.apm.breadcrumb.nodesTitle', { - defaultMessage: 'JVMs' - }), - name: RouteName.SERVICE_NODES - }, - // node metrics - { - exact: true, - path: '/services/:serviceName/nodes/:serviceNodeName/metrics', - component: () => , - breadcrumb: ({ location }) => { - const { serviceNodeName } = resolveUrlParams(location, {}); - - if (serviceNodeName === SERVICE_NODE_NAME_MISSING) { - return UNIDENTIFIED_SERVICE_NODES_LABEL; - } - - return serviceNodeName || ''; +export function getRoutes({ + apmServiceMapEnabled +}: { + apmServiceMapEnabled: boolean; +}): BreadcrumbRoute[] { + const routes: BreadcrumbRoute[] = [ + { + exact: true, + path: '/', + render: renderAsRedirectTo('/services'), + breadcrumb: 'APM', + name: RouteName.HOME }, - name: RouteName.SERVICE_NODE_METRICS - }, - { - exact: true, - path: '/services/:serviceName/transactions/view', - component: TransactionDetails, - breadcrumb: ({ location }) => { - const query = toQuery(location.search); - return query.transactionName as string; + { + exact: true, + path: '/services', + component: () => , + breadcrumb: i18n.translate('xpack.apm.breadcrumb.servicesTitle', { + defaultMessage: 'Services' + }), + name: RouteName.SERVICES + }, + { + exact: true, + path: '/traces', + component: () => , + breadcrumb: i18n.translate('xpack.apm.breadcrumb.tracesTitle', { + defaultMessage: 'Traces' + }), + name: RouteName.TRACES }, - name: RouteName.TRANSACTION_NAME - } -]; - -if (npStart.core.injectedMetadata.getInjectedVar('apmServiceMapEnabled')) { - routes.push( { exact: true, - path: '/service-map', - component: () => , - breadcrumb: i18n.translate('xpack.apm.breadcrumb.serviceMapTitle', { - defaultMessage: 'Service Map' + path: '/settings', + render: renderAsRedirectTo('/settings/agent-configuration'), + breadcrumb: i18n.translate('xpack.apm.breadcrumb.listSettingsTitle', { + defaultMessage: 'Settings' }), - name: RouteName.SERVICE_MAP + name: RouteName.SETTINGS }, { exact: true, - path: '/services/:serviceName/service-map', - component: () => , - breadcrumb: i18n.translate('xpack.apm.breadcrumb.serviceMapTitle', { - defaultMessage: 'Service Map' + path: '/settings/apm-indices', + component: () => ( + + + + ), + breadcrumb: i18n.translate('xpack.apm.breadcrumb.settings.indicesTitle', { + defaultMessage: 'Indices' }), - name: RouteName.SINGLE_SERVICE_MAP + name: RouteName.INDICES + }, + { + exact: true, + path: '/settings/agent-configuration', + component: () => ( + + + + ), + breadcrumb: i18n.translate( + 'xpack.apm.breadcrumb.settings.agentConfigurationTitle', + { + defaultMessage: 'Agent Configuration' + } + ), + name: RouteName.AGENT_CONFIGURATION + }, + { + exact: true, + path: '/services/:serviceName', + breadcrumb: ({ match }) => match.params.serviceName, + render: (props: RouteComponentProps) => + renderAsRedirectTo( + `/services/${props.match.params.serviceName}/transactions` + )(props), + name: RouteName.SERVICE + }, + // errors + { + exact: true, + path: '/services/:serviceName/errors/:groupId', + component: ErrorGroupDetails, + breadcrumb: ({ match }) => match.params.groupId, + name: RouteName.ERROR + }, + { + exact: true, + path: '/services/:serviceName/errors', + component: () => , + breadcrumb: i18n.translate('xpack.apm.breadcrumb.errorsTitle', { + defaultMessage: 'Errors' + }), + name: RouteName.ERRORS + }, + // transactions + { + exact: true, + path: '/services/:serviceName/transactions', + component: () => , + breadcrumb: i18n.translate('xpack.apm.breadcrumb.transactionsTitle', { + defaultMessage: 'Transactions' + }), + name: RouteName.TRANSACTIONS + }, + // metrics + { + exact: true, + path: '/services/:serviceName/metrics', + component: () => , + breadcrumb: metricsBreadcrumb, + name: RouteName.METRICS + }, + // service nodes, only enabled for java agents for now + { + exact: true, + path: '/services/:serviceName/nodes', + component: () => , + breadcrumb: i18n.translate('xpack.apm.breadcrumb.nodesTitle', { + defaultMessage: 'JVMs' + }), + name: RouteName.SERVICE_NODES + }, + // node metrics + { + exact: true, + path: '/services/:serviceName/nodes/:serviceNodeName/metrics', + component: () => , + breadcrumb: ({ location }) => { + const { serviceNodeName } = resolveUrlParams(location, {}); + + if (serviceNodeName === SERVICE_NODE_NAME_MISSING) { + return UNIDENTIFIED_SERVICE_NODES_LABEL; + } + + return serviceNodeName || ''; + }, + name: RouteName.SERVICE_NODE_METRICS + }, + { + exact: true, + path: '/services/:serviceName/transactions/view', + component: TransactionDetails, + breadcrumb: ({ location }) => { + const query = toQuery(location.search); + return query.transactionName as string; + }, + name: RouteName.TRANSACTION_NAME } - ); + ]; + + if (apmServiceMapEnabled) { + routes.push( + { + exact: true, + path: '/service-map', + component: () => , + breadcrumb: i18n.translate('xpack.apm.breadcrumb.serviceMapTitle', { + defaultMessage: 'Service Map' + }), + name: RouteName.SERVICE_MAP + }, + { + exact: true, + path: '/services/:serviceName/service-map', + component: () => , + breadcrumb: i18n.translate('xpack.apm.breadcrumb.serviceMapTitle', { + defaultMessage: 'Service Map' + }), + name: RouteName.SINGLE_SERVICE_MAP + } + ); + } + + return routes; } diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx index 1fc83217f2a1c..82b641b060259 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx @@ -7,7 +7,6 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { EuiTabs, EuiSpacer } from '@elastic/eui'; -import { npStart } from 'ui/new_platform'; import { ErrorGroupOverview } from '../ErrorGroupOverview'; import { TransactionOverview } from '../TransactionOverview'; import { ServiceMetrics } from '../ServiceMetrics'; @@ -22,6 +21,7 @@ import { ServiceNodeOverview } from '../ServiceNodeOverview'; import { useAgentName } from '../../../hooks/useAgentName'; import { ServiceMap } from '../ServiceMap'; import { ServiceMapLink } from '../../shared/Links/apm/ServiceMapLink'; +import { usePlugins } from '../../../new-platform/plugin'; interface Props { tab: 'transactions' | 'errors' | 'metrics' | 'nodes' | 'service-map'; @@ -31,6 +31,8 @@ export function ServiceDetailTabs({ tab }: Props) { const { urlParams } = useUrlParams(); const { serviceName } = urlParams; const { agentName } = useAgentName(); + const { apm } = usePlugins(); + const { apmServiceMapEnabled } = apm.config; if (!serviceName) { // this never happens, urlParams type is not accurate enough @@ -105,7 +107,7 @@ export function ServiceDetailTabs({ tab }: Props) { name: 'service-map' }; - if (npStart.core.injectedMetadata.getInjectedVar('apmServiceMapEnabled')) { + if (apmServiceMapEnabled) { tabs.push(serviceMapTab); } diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx index 18964531958f7..c598a2fb1da2c 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx @@ -36,6 +36,7 @@ import { IUrlParams } from '../../../../context/UrlParamsContext/types'; import { KibanaLink } from '../../../shared/Links/KibanaLink'; import { createErrorGroupWatch, Schedule } from './createErrorGroupWatch'; import { ElasticDocsLink } from '../../../shared/Links/ElasticDocsLink'; +import { PluginsContext } from '../../../../new-platform/plugin'; type ScheduleKey = keyof Schedule; @@ -149,9 +150,13 @@ export class WatcherFlyout extends Component< this.setState({ slackUrl: event.target.value }); }; - public createWatch = () => { - const core = this.context; + public createWatch = ({ + apmIndexPatternTitle + }: { + apmIndexPatternTitle: string; + }) => () => { const { serviceName } = this.props.urlParams; + const core = this.context; if (!serviceName) { return; @@ -186,10 +191,6 @@ export class WatcherFlyout extends Component< unit: 'h' }; - const apmIndexPatternTitle = core.injectedMetadata.getInjectedVar( - 'apmIndexPatternTitle' - ) as string; - return createErrorGroupWatch({ http: core.http, emails, @@ -613,20 +614,26 @@ export class WatcherFlyout extends Component< - - {i18n.translate( - 'xpack.apm.serviceDetails.enableErrorReportsPanel.createWatchButtonLabel', - { - defaultMessage: 'Create watch' - } - )} - + + {({ apm }) => { + return ( + + {i18n.translate( + 'xpack.apm.serviceDetails.enableErrorReportsPanel.createWatchButtonLabel', + { + defaultMessage: 'Create watch' + } + )} + + ); + }} + diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx index 6323599436ca8..7ced0b6fdd566 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx @@ -22,6 +22,7 @@ import { import { useFetcher } from '../../../../hooks/useFetcher'; import { useCallApmApi } from '../../../../hooks/useCallApmApi'; import { APMClient } from '../../../../services/rest/createCallApmApi'; +import { clearCache } from '../../../../services/rest/callApi'; import { useKibanaCore } from '../../../../../../observability/public'; const APM_INDEX_LABELS = [ @@ -80,6 +81,8 @@ async function saveApmIndices({ body: apmIndices } }); + + clearCache(); } export function ApmIndices() { diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/ElasticDocsLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/ElasticDocsLink.tsx index 53a9fc8bafbd1..1888e1d04c2cb 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/ElasticDocsLink.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/ElasticDocsLink.tsx @@ -6,10 +6,7 @@ import React from 'react'; import { EuiLink, EuiLinkAnchorProps } from '@elastic/eui'; -import { metadata } from 'ui/metadata'; - -// TODO: metadata should be read from a useContext hook in new platform -const STACK_VERSION = metadata.branch; +import { usePlugins } from '../../../new-platform/plugin'; // union type constisting of valid guide sections that we link to type DocsSection = '/apm/get-started' | '/x-pack' | '/apm/server'; @@ -20,6 +17,8 @@ interface Props extends EuiLinkAnchorProps { } export function ElasticDocsLink({ section, path, ...rest }: Props) { - const href = `https://www.elastic.co/guide/en${section}/${STACK_VERSION}${path}`; + const { apm } = usePlugins(); + const { stackVersion } = apm; + const href = `https://www.elastic.co/guide/en${section}/${stackVersion}${path}`; return ; } diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/__test__/ErrorMetadata.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/__test__/ErrorMetadata.test.tsx index 0c84adbf22060..c3913e43cbd62 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/__test__/ErrorMetadata.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/__test__/ErrorMetadata.test.tsx @@ -10,9 +10,14 @@ import { render } from '@testing-library/react'; import { APMError } from '../../../../../../typings/es_schemas/ui/APMError'; import { expectTextsInDocument, - expectTextsNotInDocument + expectTextsNotInDocument, + MockPluginContextWrapper } from '../../../../../utils/testHelpers'; +const renderOptions = { + wrapper: MockPluginContextWrapper +}; + function getError() { return ({ labels: { someKey: 'labels value' }, @@ -38,7 +43,7 @@ function getError() { describe('ErrorMetadata', () => { it('should render a error with all sections', () => { const error = getError(); - const output = render(); + const output = render(, renderOptions); // sections expectTextsInDocument(output, [ @@ -57,7 +62,7 @@ describe('ErrorMetadata', () => { it('should render a error with all included dot notation keys', () => { const error = getError(); - const output = render(); + const output = render(, renderOptions); // included keys expectTextsInDocument(output, [ @@ -79,7 +84,7 @@ describe('ErrorMetadata', () => { it('should render a error with all included values', () => { const error = getError(); - const output = render(); + const output = render(, renderOptions); // included values expectTextsInDocument(output, [ @@ -104,7 +109,7 @@ describe('ErrorMetadata', () => { it('should render a error with only the required sections', () => { const error = {} as APMError; - const output = render(); + const output = render(, renderOptions); // required sections should be found expectTextsInDocument(output, ['Labels', 'User']); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx index 3c851252666e0..438fe57218cc9 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx @@ -10,9 +10,14 @@ import { SpanMetadata } from '..'; import { Span } from '../../../../../../typings/es_schemas/ui/Span'; import { expectTextsInDocument, - expectTextsNotInDocument + expectTextsNotInDocument, + MockPluginContextWrapper } from '../../../../../utils/testHelpers'; +const renderOptions = { + wrapper: MockPluginContextWrapper +}; + describe('SpanMetadata', () => { describe('render', () => { it('renders', () => { @@ -29,7 +34,7 @@ describe('SpanMetadata', () => { id: '7efbc7056b746fcb' } } as unknown) as Span; - const output = render(); + const output = render(, renderOptions); expectTextsInDocument(output, ['Service', 'Agent']); }); }); @@ -53,7 +58,7 @@ describe('SpanMetadata', () => { type: 'external' } } as unknown) as Span; - const output = render(); + const output = render(, renderOptions); expectTextsInDocument(output, ['Service', 'Agent', 'Span']); }); }); @@ -76,7 +81,7 @@ describe('SpanMetadata', () => { type: 'external' } } as unknown) as Span; - const output = render(); + const output = render(, renderOptions); expectTextsInDocument(output, ['Service', 'Agent']); expectTextsNotInDocument(output, ['Span']); }); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx index 1e06648f21eea..1ea20ecd64562 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx @@ -10,9 +10,14 @@ import { render } from '@testing-library/react'; import { Transaction } from '../../../../../../typings/es_schemas/ui/Transaction'; import { expectTextsInDocument, - expectTextsNotInDocument + expectTextsNotInDocument, + MockPluginContextWrapper } from '../../../../../utils/testHelpers'; +const renderOptions = { + wrapper: MockPluginContextWrapper +}; + function getTransaction() { return ({ labels: { someKey: 'labels value' }, @@ -38,7 +43,10 @@ function getTransaction() { describe('TransactionMetadata', () => { it('should render a transaction with all sections', () => { const transaction = getTransaction(); - const output = render(); + const output = render( + , + renderOptions + ); // sections expectTextsInDocument(output, [ @@ -57,7 +65,10 @@ describe('TransactionMetadata', () => { it('should render a transaction with all included dot notation keys', () => { const transaction = getTransaction(); - const output = render(); + const output = render( + , + renderOptions + ); // included keys expectTextsInDocument(output, [ @@ -82,7 +93,10 @@ describe('TransactionMetadata', () => { it('should render a transaction with all included values', () => { const transaction = getTransaction(); - const output = render(); + const output = render( + , + renderOptions + ); // included values expectTextsInDocument(output, [ @@ -107,7 +121,10 @@ describe('TransactionMetadata', () => { it('should render a transaction with only the required sections', () => { const transaction = {} as Transaction; - const output = render(); + const output = render( + , + renderOptions + ); // required sections should be found expectTextsInDocument(output, ['Labels', 'User']); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/MetadataTable.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/MetadataTable.test.tsx index fbdd6bad3457d..bed25fcc64012 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/MetadataTable.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/MetadataTable.test.tsx @@ -7,9 +7,16 @@ import React from 'react'; import { render } from '@testing-library/react'; import { MetadataTable } from '..'; -import { expectTextsInDocument } from '../../../../utils/testHelpers'; +import { + expectTextsInDocument, + MockPluginContextWrapper +} from '../../../../utils/testHelpers'; import { SectionsWithRows } from '../helper'; +const renderOptions = { + wrapper: MockPluginContextWrapper +}; + describe('MetadataTable', () => { it('shows sections', () => { const sectionsWithRows = ([ @@ -25,7 +32,10 @@ describe('MetadataTable', () => { ] } ] as unknown) as SectionsWithRows; - const output = render(); + const output = render( + , + renderOptions + ); expectTextsInDocument(output, [ 'Foo', 'No data available', @@ -45,7 +55,10 @@ describe('MetadataTable', () => { required: true } ] as unknown) as SectionsWithRows; - const output = render(); + const output = render( + , + renderOptions + ); expectTextsInDocument(output, ['Foo', 'No data available']); }); }); diff --git a/x-pack/legacy/plugins/apm/public/context/MatchedRouteContext.tsx b/x-pack/legacy/plugins/apm/public/context/MatchedRouteContext.tsx index bfa49fbcbc35a..b0a587421dfa3 100644 --- a/x-pack/legacy/plugins/apm/public/context/MatchedRouteContext.tsx +++ b/x-pack/legacy/plugins/apm/public/context/MatchedRouteContext.tsx @@ -3,16 +3,21 @@ * 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, { useMemo } from 'react'; +import React, { useMemo, ReactChild } from 'react'; import { matchPath } from 'react-router-dom'; -import { routes } from '../components/app/Main/route_config'; import { useLocation } from '../hooks/useLocation'; +import { BreadcrumbRoute } from '../components/app/Main/ProvideBreadcrumbs'; -export const MatchedRouteContext = React.createContext>( - [] -); +export const MatchedRouteContext = React.createContext([]); -export const MatchedRouteProvider: React.FC = ({ children }) => { +interface MatchedRouteProviderProps { + children: ReactChild; + routes: BreadcrumbRoute[]; +} +export function MatchedRouteProvider({ + children, + routes +}: MatchedRouteProviderProps) { const { pathname } = useLocation(); const contextValue = useMemo(() => { @@ -21,9 +26,9 @@ export const MatchedRouteProvider: React.FC = ({ children }) => { path: route.path }); }); - }, [pathname]); + }, [pathname, routes]); return ( ); -}; +} diff --git a/x-pack/legacy/plugins/apm/public/hacks/toggle_app_link_in_nav.ts b/x-pack/legacy/plugins/apm/public/legacy_register_feature.js similarity index 60% rename from x-pack/legacy/plugins/apm/public/hacks/toggle_app_link_in_nav.ts rename to x-pack/legacy/plugins/apm/public/legacy_register_feature.js index 295b101777541..6e98d0784bee1 100644 --- a/x-pack/legacy/plugins/apm/public/hacks/toggle_app_link_in_nav.ts +++ b/x-pack/legacy/plugins/apm/public/legacy_register_feature.js @@ -5,10 +5,12 @@ */ import { npStart } from 'ui/new_platform'; +import { FeatureCatalogueRegistryProvider } from 'ui/registry/feature_catalogue'; +import { featureCatalogueEntry } from './new-platform/featureCatalogueEntry'; const { core } = npStart; const apmUiEnabled = core.injectedMetadata.getInjectedVar('apmUiEnabled'); -if (apmUiEnabled === false) { - core.chrome.navLinks.update('apm', { hidden: true }); +if (apmUiEnabled) { + FeatureCatalogueRegistryProvider.register(() => featureCatalogueEntry); } diff --git a/x-pack/legacy/plugins/apm/public/new-platform/featureCatalogueEntry.ts b/x-pack/legacy/plugins/apm/public/new-platform/featureCatalogueEntry.ts new file mode 100644 index 0000000000000..7a150de6d5d02 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/new-platform/featureCatalogueEntry.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { FeatureCatalogueCategory } from '../../../../../../src/plugins/home/public'; + +export const featureCatalogueEntry = { + id: 'apm', + title: 'APM', + description: i18n.translate('xpack.apm.apmDescription', { + defaultMessage: + 'Automatically collect in-depth performance metrics and ' + + 'errors from inside your applications.' + }), + icon: 'apmApp', + path: '/app/apm', + showOnHomePage: true, + category: FeatureCatalogueCategory.DATA +}; diff --git a/x-pack/legacy/plugins/apm/public/new-platform/getConfigFromInjectedMetadata.ts b/x-pack/legacy/plugins/apm/public/new-platform/getConfigFromInjectedMetadata.ts new file mode 100644 index 0000000000000..daf6dadbaf966 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/new-platform/getConfigFromInjectedMetadata.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { npStart } from 'ui/new_platform'; +import { ConfigSchema } from './plugin'; + +const { core } = npStart; + +export function getConfigFromInjectedMetadata(): ConfigSchema { + const { + apmIndexPatternTitle, + apmServiceMapEnabled, + apmUiEnabled + } = core.injectedMetadata.getInjectedVars(); + + return { + apmIndexPatternTitle, + apmServiceMapEnabled, + apmUiEnabled + } as ConfigSchema; +} diff --git a/x-pack/legacy/plugins/apm/public/new-platform/index.tsx b/x-pack/legacy/plugins/apm/public/new-platform/index.tsx index 9dce4bcdd828c..0674dc48316f4 100644 --- a/x-pack/legacy/plugins/apm/public/new-platform/index.tsx +++ b/x-pack/legacy/plugins/apm/public/new-platform/index.tsx @@ -10,4 +10,4 @@ import { ApmPlugin, ApmPluginSetup, ApmPluginStart } from './plugin'; export const plugin: PluginInitializer< ApmPluginSetup, ApmPluginStart -> = _core => new ApmPlugin(); +> = pluginInitializerContext => new ApmPlugin(pluginInitializerContext); diff --git a/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx b/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx index b5986610d3048..6477f9a9dd41d 100644 --- a/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx +++ b/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx @@ -8,11 +8,13 @@ import React, { useContext, createContext } from 'react'; import ReactDOM from 'react-dom'; import { Router, Route, Switch } from 'react-router-dom'; import styled from 'styled-components'; +import { HomePublicPluginSetup } from '../../../../../../src/plugins/home/public'; import { CoreStart, LegacyCoreStart, Plugin, - CoreSetup + CoreSetup, + PluginInitializerContext } from '../../../../../../src/core/public'; import { DataPublicPluginStart } from '../../../../../../src/plugins/data/public'; import { KibanaCoreContextProvider } from '../../../observability/public'; @@ -23,12 +25,17 @@ import { px, unit, units } from '../style/variables'; import { LoadingIndicatorProvider } from '../context/LoadingIndicatorContext'; import { LicenseProvider } from '../context/LicenseContext'; import { UpdateBreadcrumbs } from '../components/app/Main/UpdateBreadcrumbs'; -import { routes } from '../components/app/Main/route_config'; +import { getRoutes } from '../components/app/Main/route_config'; import { ScrollToTopOnPathChange } from '../components/app/Main/ScrollToTopOnPathChange'; import { MatchedRouteProvider } from '../context/MatchedRouteContext'; import { createStaticIndexPattern } from '../services/rest/index_pattern'; import { setHelpExtension } from './setHelpExtension'; import { setReadonlyBadge } from './updateBadge'; +import { featureCatalogueEntry } from './featureCatalogueEntry'; +import { getConfigFromInjectedMetadata } from './getConfigFromInjectedMetadata'; +import { toggleAppLinkInNav } from './toggleAppLinkInNav'; +import { BreadcrumbRoute } from '../components/app/Main/ProvideBreadcrumbs'; +import { stackVersionFromLegacyMetadata } from './stackVersionFromLegacyMetadata'; export const REACT_APP_ROOT_ID = 'react-apm-root'; @@ -37,10 +44,10 @@ const MainContainer = styled.main` padding: ${px(units.plus)}; `; -const App = () => { +const App = ({ routes }: { routes: BreadcrumbRoute[] }) => { return ( - + {routes.map((route, i) => ( @@ -53,14 +60,30 @@ const App = () => { export type ApmPluginSetup = void; export type ApmPluginStart = void; -export type ApmPluginSetupDeps = {}; // eslint-disable-line @typescript-eslint/consistent-type-definitions + +export interface ApmPluginSetupDeps { + home: HomePublicPluginSetup; +} export interface ApmPluginStartDeps { data: DataPublicPluginStart; } -const PluginsContext = createContext({} as ApmPluginStartDeps); +export interface ConfigSchema { + apmIndexPatternTitle: string; + apmServiceMapEnabled: boolean; + apmUiEnabled: boolean; +} +// These are to be used until we switch over all our context handling to +// kibana_react +export const PluginsContext = createContext< + ApmPluginStartDeps & { apm: { config: ConfigSchema; stackVersion: string } } +>( + {} as ApmPluginStartDeps & { + apm: { config: ConfigSchema; stackVersion: string }; + } +); export function usePlugins() { return useContext(PluginsContext); } @@ -73,27 +96,56 @@ export class ApmPlugin ApmPluginSetupDeps, ApmPluginStartDeps > { + constructor( + // @ts-ignore Not using initializerContext now, but will be once NP + // migration is complete. + private readonly initializerContext: PluginInitializerContext + ) {} + // Take the DOM element as the constructor, so we can mount the app. - public setup(_core: CoreSetup, _plugins: ApmPluginSetupDeps) {} + public setup(_core: CoreSetup, plugins: ApmPluginSetupDeps) { + plugins.home.featureCatalogue.register(featureCatalogueEntry); + } public start(core: CoreStart, plugins: ApmPluginStartDeps) { const i18nCore = core.i18n; + // Once we're actually an NP plugin we'll get the config from the + // initializerContext like: + // + // const config = this.initializerContext.config.get(); + // + // Until then we use a shim to get it from legacy injectedMetadata: + const config = getConfigFromInjectedMetadata(); + + // Once we're actually an NP plugin we'll get the package info from the + // initializerContext like: + // + // const stackVersion = this.initializerContext.env.packageInfo.branch + // + // Until then we use a shim to get it from legacy metadata: + const stackVersion = stackVersionFromLegacyMetadata; + + const pluginsForContext = { ...plugins, apm: { config, stackVersion } }; + + const routes = getRoutes(config); + // render APM feedback link in global help menu setHelpExtension(core); setReadonlyBadge(core); + toggleAppLinkInNav(core, config); ReactDOM.render( - + - + - + diff --git a/x-pack/legacy/plugins/canvas/public/components/editor/index.ts b/x-pack/legacy/plugins/apm/public/new-platform/stackVersionFromLegacyMetadata.ts similarity index 70% rename from x-pack/legacy/plugins/canvas/public/components/editor/index.ts rename to x-pack/legacy/plugins/apm/public/new-platform/stackVersionFromLegacyMetadata.ts index d7215da20e4a0..3d43b8e39a122 100644 --- a/x-pack/legacy/plugins/canvas/public/components/editor/index.ts +++ b/x-pack/legacy/plugins/apm/public/new-platform/stackVersionFromLegacyMetadata.ts @@ -4,4 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export { Editor } from './editor'; +import { metadata } from 'ui/metadata'; + +export const stackVersionFromLegacyMetadata = metadata.branch; diff --git a/x-pack/legacy/plugins/canvas/public/lib/documentation_links.js b/x-pack/legacy/plugins/apm/public/new-platform/toggleAppLinkInNav.ts similarity index 52% rename from x-pack/legacy/plugins/canvas/public/lib/documentation_links.js rename to x-pack/legacy/plugins/apm/public/new-platform/toggleAppLinkInNav.ts index 351e388609e5b..010e196da9bb3 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/documentation_links.js +++ b/x-pack/legacy/plugins/apm/public/new-platform/toggleAppLinkInNav.ts @@ -4,8 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; +import { CoreStart } from 'kibana/public'; -export const documentationLinks = { - canvas: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/canvas.html`, -}; +export function toggleAppLinkInNav(core: CoreStart, { apmUiEnabled = false }) { + if (apmUiEnabled === false) { + core.chrome.navLinks.update('apm', { hidden: true }); + } +} diff --git a/x-pack/legacy/plugins/apm/public/register_feature.js b/x-pack/legacy/plugins/apm/public/register_feature.js deleted file mode 100644 index 8994fac17e914..0000000000000 --- a/x-pack/legacy/plugins/apm/public/register_feature.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { npStart } from 'ui/new_platform'; -import { i18n } from '@kbn/i18n'; -import { - FeatureCatalogueRegistryProvider, - FeatureCatalogueCategory -} from 'ui/registry/feature_catalogue'; - -const { core } = npStart; -const apmUiEnabled = core.injectedMetadata.getInjectedVar('apmUiEnabled'); - -if (apmUiEnabled) { - FeatureCatalogueRegistryProvider.register(() => { - return { - id: 'apm', - title: 'APM', - description: i18n.translate('xpack.apm.apmDescription', { - defaultMessage: - 'Automatically collect in-depth performance metrics and ' + - 'errors from inside your applications.' - }), - icon: 'apmApp', - path: '/app/apm', - showOnHomePage: true, - category: FeatureCatalogueCategory.DATA - }; - }); -} diff --git a/x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx b/x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx index 8f609f41b079d..b5cee4a78b01c 100644 --- a/x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx +++ b/x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx @@ -11,7 +11,7 @@ import enzymeToJson from 'enzyme-to-json'; import { Location } from 'history'; import moment from 'moment'; import { Moment } from 'moment-timezone'; -import React from 'react'; +import React, { FunctionComponent, ReactNode } from 'react'; import { render, waitForElement } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; import { MemoryRouter } from 'react-router-dom'; @@ -19,6 +19,11 @@ import { APMConfig } from '../../../../../plugins/apm/server'; import { LocationProvider } from '../context/LocationContext'; import { PromiseReturnType } from '../../typings/common'; import { ESFilter } from '../../typings/elasticsearch'; +import { + PluginsContext, + ConfigSchema, + ApmPluginStartDeps +} from '../new-platform/plugin'; export function toJson(wrapper: ReactWrapper) { return enzymeToJson(wrapper, { @@ -179,3 +184,23 @@ export async function inspectSearchParams( } export type SearchParamsMock = PromiseReturnType; + +export const MockPluginContextWrapper: FunctionComponent<{}> = ({ + children +}: { + children?: ReactNode; +}) => { + return ( + + {children} + + ); +}; diff --git a/x-pack/legacy/plugins/canvas/.storybook/config.js b/x-pack/legacy/plugins/canvas/.storybook/config.js index 3e1152dc3a611..d3cd8e8057ae8 100644 --- a/x-pack/legacy/plugins/canvas/.storybook/config.js +++ b/x-pack/legacy/plugins/canvas/.storybook/config.js @@ -4,11 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ +import React from 'react'; import { configure, addDecorator, addParameters } from '@storybook/react'; import { withKnobs } from '@storybook/addon-knobs/react'; import { withInfo } from '@storybook/addon-info'; import { create } from '@storybook/theming'; +import { coreMock } from 'src/core/public/mocks'; +import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; + // If we're running Storyshots, be sure to register the require context hook. // Otherwise, add the other decorators. if (process.env.NODE_ENV === 'test') { @@ -33,6 +37,11 @@ if (process.env.NODE_ENV === 'test') { addDecorator(withKnobs); } +// Add New Platform Context for any stories that need it +addDecorator(fn => ( + {fn()} +)); + function loadStories() { require('./dll_contexts'); diff --git a/x-pack/legacy/plugins/canvas/index.js b/x-pack/legacy/plugins/canvas/index.js index e3448d2051eea..8e742de6de944 100644 --- a/x-pack/legacy/plugins/canvas/index.js +++ b/x-pack/legacy/plugins/canvas/index.js @@ -22,7 +22,7 @@ export function canvas(kibana) { description: 'Data driven workpads', icon: 'plugins/canvas/icon.svg', euiIconType: 'canvasApp', - main: 'plugins/canvas/app', + main: 'plugins/canvas/legacy_start', }, interpreter: [ 'plugins/canvas/browser_functions', diff --git a/x-pack/legacy/plugins/canvas/public/angular/config/index.js b/x-pack/legacy/plugins/canvas/public/angular/config/index.js index 8a8f3e3cfce51..020bcf1f7686e 100644 --- a/x-pack/legacy/plugins/canvas/public/angular/config/index.js +++ b/x-pack/legacy/plugins/canvas/public/angular/config/index.js @@ -4,5 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -import './state_management'; // Requires 6.2.0+ -import './location_provider'; +export * from './state_management'; // Requires 6.2.0+ +export * from './location_provider'; diff --git a/x-pack/legacy/plugins/canvas/public/angular/config/location_provider.js b/x-pack/legacy/plugins/canvas/public/angular/config/location_provider.js deleted file mode 100644 index 40b2081ad007d..0000000000000 --- a/x-pack/legacy/plugins/canvas/public/angular/config/location_provider.js +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; - -// disable angular's location provider -const app = uiModules.get('apps/canvas'); -app.config($locationProvider => { - $locationProvider.html5Mode({ - enabled: false, - requireBase: false, - rewriteLinks: false, - }); -}); diff --git a/x-pack/legacy/plugins/canvas/public/angular/config/location_provider.ts b/x-pack/legacy/plugins/canvas/public/angular/config/location_provider.ts new file mode 100644 index 0000000000000..547e354d7fde9 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/angular/config/location_provider.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ILocationProvider } from 'angular'; +import { CoreStart, CanvasStartDeps } from '../../plugin'; + +export function initLocationProvider(coreStart: CoreStart, plugins: CanvasStartDeps) { + // disable angular's location provider + const app = plugins.__LEGACY.uiModules.get('apps/canvas'); + + app.config(($locationProvider: ILocationProvider) => { + $locationProvider.html5Mode({ + enabled: false, + requireBase: false, + rewriteLinks: false, + }); + }); +} diff --git a/x-pack/legacy/plugins/canvas/public/angular/config/state_management.js b/x-pack/legacy/plugins/canvas/public/angular/config/state_management.js index e6c1da418b6fc..4347765748c5d 100644 --- a/x-pack/legacy/plugins/canvas/public/angular/config/state_management.js +++ b/x-pack/legacy/plugins/canvas/public/angular/config/state_management.js @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { uiModules } from 'ui/modules'; - -// disable the kibana state management -const app = uiModules.get('apps/canvas'); -app.config(stateManagementConfigProvider => { - stateManagementConfigProvider.disable(); -}); +export function initStateManagement(coreStart, plugins) { + // disable the kibana state management + const app = plugins.__LEGACY.uiModules.get('apps/canvas'); + app.config(stateManagementConfigProvider => { + stateManagementConfigProvider.disable(); + }); +} diff --git a/x-pack/legacy/plugins/canvas/public/angular/controllers/canvas.tsx b/x-pack/legacy/plugins/canvas/public/angular/controllers/canvas.tsx index 4738d35836358..0dfa1ceecdbc5 100644 --- a/x-pack/legacy/plugins/canvas/public/angular/controllers/canvas.tsx +++ b/x-pack/legacy/plugins/canvas/public/angular/controllers/canvas.tsx @@ -8,8 +8,8 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { I18nProvider } from '@kbn/i18n/react'; import { Provider } from 'react-redux'; import { Store } from 'redux'; -import chrome from 'ui/chrome'; -import { UICapabilities } from 'ui/capabilities'; +import { CanvasStartDeps, CoreStart } from '../../plugin'; +import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public'; // @ts-ignore Untyped local import { App } from '../../components/app'; @@ -17,35 +17,38 @@ import { AngularStrings } from '../../../i18n'; const { CanvasRootController: strings } = AngularStrings; -export function CanvasRootController( - canvasStore: Store, - $scope: any, // Untyped in Kibana - $element: any, // Untyped in Kibana - uiCapabilities: UICapabilities -) { - const domNode = $element[0]; +export function CanvasRootControllerFactory(coreStart: CoreStart, plugins: CanvasStartDeps) { + return function CanvasRootController( + canvasStore: Store, + $scope: any, // Untyped in Kibana + $element: any // Untyped in Kibana + ) { + const domNode = $element[0]; - // set the read-only badge when appropriate - chrome.badge.set( - uiCapabilities.canvas.save - ? undefined - : { - text: strings.getReadOnlyBadgeText(), - tooltip: strings.getReadOnlyBadgeTooltip(), - iconType: 'glasses', - } - ); + // set the read-only badge when appropriate + coreStart.chrome.setBadge( + coreStart.application.capabilities.canvas.save + ? undefined + : { + text: strings.getReadOnlyBadgeText(), + tooltip: strings.getReadOnlyBadgeTooltip(), + iconType: 'glasses', + } + ); - render( - - - - - , - domNode - ); + render( + + + + + + + , + domNode + ); - $scope.$on('$destroy', () => { - unmountComponentAtNode(domNode); - }); + $scope.$on('$destroy', () => { + unmountComponentAtNode(domNode); + }); + }; } diff --git a/x-pack/legacy/plugins/canvas/public/angular/controllers/index.ts b/x-pack/legacy/plugins/canvas/public/angular/controllers/index.ts index 0d14bb4d9d4b5..01fe3dda9c971 100644 --- a/x-pack/legacy/plugins/canvas/public/angular/controllers/index.ts +++ b/x-pack/legacy/plugins/canvas/public/angular/controllers/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { CanvasRootController } from './canvas'; +export { CanvasRootControllerFactory } from './canvas'; diff --git a/x-pack/legacy/plugins/canvas/public/angular/services/index.js b/x-pack/legacy/plugins/canvas/public/angular/services/index.js index f15372a7a483a..b472b9bbac993 100755 --- a/x-pack/legacy/plugins/canvas/public/angular/services/index.js +++ b/x-pack/legacy/plugins/canvas/public/angular/services/index.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -import './store'; +export * from './store'; diff --git a/x-pack/legacy/plugins/canvas/public/angular/services/store.js b/x-pack/legacy/plugins/canvas/public/angular/services/store.js index d008a9af9b174..f6f9d8922b99e 100644 --- a/x-pack/legacy/plugins/canvas/public/angular/services/store.js +++ b/x-pack/legacy/plugins/canvas/public/angular/services/store.js @@ -4,27 +4,28 @@ * you may not use this file except in compliance with the Elastic License. */ -import { uiModules } from 'ui/modules'; import { createStore } from '../../state/store'; import { getInitialState } from '../../state/initial_state'; -const app = uiModules.get('apps/canvas'); -app.service('canvasStore', (kbnVersion, basePath, reportingBrowserType, serverFunctions) => { - const initialState = getInitialState(); +export function initStore(coreStart, plugins) { + const app = plugins.__LEGACY.uiModules.get('apps/canvas'); + app.service('canvasStore', (kbnVersion, basePath, reportingBrowserType, serverFunctions) => { + const initialState = getInitialState(); - // Set the defaults from Kibana plugin - initialState.app = { - kbnVersion, - basePath, - reportingBrowserType, - serverFunctions, - ready: false, - }; + // Set the defaults from Kibana plugin + initialState.app = { + kbnVersion, + basePath, + reportingBrowserType, + serverFunctions, + ready: false, + }; - const store = createStore(initialState); + const store = createStore(initialState); - // TODO: debugging, remove this - window.canvasStore = store; + // TODO: debugging, remove this + window.canvasStore = store; - return store; -}); + return store; + }); +} diff --git a/x-pack/legacy/plugins/canvas/public/app.js b/x-pack/legacy/plugins/canvas/public/app.js deleted file mode 100644 index 760bb7a46f955..0000000000000 --- a/x-pack/legacy/plugins/canvas/public/app.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import 'ui/autoload/all'; -import './angular/config'; -import './angular/services'; -import React from 'react'; -import ReactDOM from 'react-dom'; -import { i18n } from '@kbn/i18n'; -import chrome from 'ui/chrome'; -import { documentationLinks } from './lib/documentation_links'; -import { CanvasRootController } from './angular/controllers'; - -// Import the uiExports that the application uses -import 'uiExports/visTypes'; -import 'uiExports/visResponseHandlers'; -import 'uiExports/visRequestHandlers'; -import 'uiExports/visEditorTypes'; -import 'uiExports/savedObjectTypes'; -import 'uiExports/spyModes'; -import 'uiExports/embeddableFactories'; -import 'uiExports/interpreter'; - -// load application code -import './lib/load_expression_types'; -import './lib/load_transitions'; -import 'uiExports/canvas'; - -import { HelpMenu } from './components/help_menu/help_menu'; - -// load the application -chrome.setRootController('canvas', CanvasRootController); - -// add Canvas docs to help menu in global nav -chrome.helpExtension.set({ - appName: i18n.translate('xpack.canvas.helpMenu.appName', { - defaultMessage: 'Canvas', - }), - links: [ - { - linkType: 'documentation', - href: documentationLinks.canvas, - }, - ], - content: domNode => { - ReactDOM.render(, domNode); - }, -}); diff --git a/x-pack/legacy/plugins/canvas/public/components/app/track_route_change.js b/x-pack/legacy/plugins/canvas/public/components/app/track_route_change.js index fb8bd68d5401e..e837f5200a159 100644 --- a/x-pack/legacy/plugins/canvas/public/components/app/track_route_change.js +++ b/x-pack/legacy/plugins/canvas/public/components/app/track_route_change.js @@ -5,16 +5,15 @@ */ import { get } from 'lodash'; -import chrome from 'ui/chrome'; -import { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url'; import { getWindow } from '../../lib/get_window'; import { CANVAS_APP } from '../../../common/lib/constants'; +import { getCoreStart, getStartPlugins } from '../../legacy'; export function trackRouteChange() { - const basePath = chrome.getBasePath(); + const basePath = getCoreStart().http.basePath.get(); // storage.set(LOCALSTORAGE_LASTPAGE, pathname); - chrome.trackSubUrlForApp( + getStartPlugins().__LEGACY.trackSubUrlForApp( CANVAS_APP, - absoluteToParsedUrl(get(getWindow(), 'location.href'), basePath) + getStartPlugins().__LEGACY.absoluteToParsedUrl(get(getWindow(), 'location.href'), basePath) ); } diff --git a/x-pack/legacy/plugins/canvas/public/components/editor/editor.tsx b/x-pack/legacy/plugins/canvas/public/components/editor/editor.tsx index 110502b48348a..c79d837a1662d 100644 --- a/x-pack/legacy/plugins/canvas/public/components/editor/editor.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/editor/editor.tsx @@ -19,9 +19,9 @@ import 'monaco-editor/esm/vs/editor/contrib/suggest/suggestController.js'; // Ne import 'monaco-editor/esm/vs/editor/contrib/hover/hover.js'; // Needed for hover import 'monaco-editor/esm/vs/editor/contrib/parameterHints/parameterHints.js'; // Needed for signature -import { theme } from './editor_theme'; +import { LIGHT_THEME, DARK_THEME } from './editor_theme'; -interface Props { +export interface Props { /** Width of editor. Defaults to 100%. */ width?: string | number; @@ -79,6 +79,11 @@ interface Props { * Function called after the editor is mounted in the view */ editorDidMount?: EditorDidMount; + + /** + * Should the editor use the dark theme + */ + useDarkTheme?: boolean; } export class Editor extends React.Component { @@ -115,7 +120,7 @@ export class Editor extends React.Component { }); // Register the theme - monaco.editor.defineTheme('euiColors', theme); + monaco.editor.defineTheme('euiColors', this.props.useDarkTheme ? DARK_THEME : LIGHT_THEME); }; _editorDidMount = ( diff --git a/x-pack/legacy/plugins/canvas/public/components/editor/editor_theme.ts b/x-pack/legacy/plugins/canvas/public/components/editor/editor_theme.ts index 836f65456a975..04d4369ed4e20 100644 --- a/x-pack/legacy/plugins/canvas/public/components/editor/editor_theme.ts +++ b/x-pack/legacy/plugins/canvas/public/components/editor/editor_theme.ts @@ -8,87 +8,91 @@ import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'; import darkTheme from '@elastic/eui/dist/eui_theme_dark.json'; import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; -import chrome from 'ui/chrome'; - // NOTE: For talk around where this theme information will ultimately live, // please see this discuss issue: https://github.com/elastic/kibana/issues/43814 -const IS_DARK_THEME = chrome.getUiSettingsClient().get('theme:darkMode'); +export function createTheme( + euiTheme: typeof darkTheme | typeof lightTheme, + selectionBackgroundColor: string +): monacoEditor.editor.IStandaloneThemeData { + return { + base: 'vs', + inherit: true, + rules: [ + { + token: '', + foreground: euiTheme.euiColorDarkestShade, + background: euiTheme.euiColorEmptyShade, + }, + { token: 'invalid', foreground: euiTheme.euiColorAccent }, + { token: 'emphasis', fontStyle: 'italic' }, + { token: 'strong', fontStyle: 'bold' }, -const themeName = IS_DARK_THEME ? darkTheme : lightTheme; + { token: 'variable', foreground: euiTheme.euiColorPrimary }, + { token: 'variable.predefined', foreground: euiTheme.euiColorSecondary }, + { token: 'constant', foreground: euiTheme.euiColorAccent }, + { token: 'comment', foreground: euiTheme.euiColorMediumShade }, + { token: 'number', foreground: euiTheme.euiColorAccent }, + { token: 'number.hex', foreground: euiTheme.euiColorAccent }, + { token: 'regexp', foreground: euiTheme.euiColorDanger }, + { token: 'annotation', foreground: euiTheme.euiColorMediumShade }, + { token: 'type', foreground: euiTheme.euiColorVis0 }, -export const theme: monacoEditor.editor.IStandaloneThemeData = { - base: 'vs', - inherit: true, - rules: [ - { - token: '', - foreground: themeName.euiColorDarkestShade, - background: themeName.euiColorEmptyShade, - }, - { token: 'invalid', foreground: themeName.euiColorAccent }, - { token: 'emphasis', fontStyle: 'italic' }, - { token: 'strong', fontStyle: 'bold' }, + { token: 'delimiter', foreground: euiTheme.euiColorDarkestShade }, + { token: 'delimiter.html', foreground: euiTheme.euiColorDarkShade }, + { token: 'delimiter.xml', foreground: euiTheme.euiColorPrimary }, - { token: 'variable', foreground: themeName.euiColorPrimary }, - { token: 'variable.predefined', foreground: themeName.euiColorSecondary }, - { token: 'constant', foreground: themeName.euiColorAccent }, - { token: 'comment', foreground: themeName.euiColorMediumShade }, - { token: 'number', foreground: themeName.euiColorAccent }, - { token: 'number.hex', foreground: themeName.euiColorAccent }, - { token: 'regexp', foreground: themeName.euiColorDanger }, - { token: 'annotation', foreground: themeName.euiColorMediumShade }, - { token: 'type', foreground: themeName.euiColorVis0 }, + { token: 'tag', foreground: euiTheme.euiColorDanger }, + { token: 'tag.id.jade', foreground: euiTheme.euiColorPrimary }, + { token: 'tag.class.jade', foreground: euiTheme.euiColorPrimary }, + { token: 'meta.scss', foreground: euiTheme.euiColorAccent }, + { token: 'metatag', foreground: euiTheme.euiColorSecondary }, + { token: 'metatag.content.html', foreground: euiTheme.euiColorDanger }, + { token: 'metatag.html', foreground: euiTheme.euiColorMediumShade }, + { token: 'metatag.xml', foreground: euiTheme.euiColorMediumShade }, + { token: 'metatag.php', fontStyle: 'bold' }, - { token: 'delimiter', foreground: themeName.euiColorDarkestShade }, - { token: 'delimiter.html', foreground: themeName.euiColorDarkShade }, - { token: 'delimiter.xml', foreground: themeName.euiColorPrimary }, + { token: 'key', foreground: euiTheme.euiColorWarning }, + { token: 'string.key.json', foreground: euiTheme.euiColorDanger }, + { token: 'string.value.json', foreground: euiTheme.euiColorPrimary }, - { token: 'tag', foreground: themeName.euiColorDanger }, - { token: 'tag.id.jade', foreground: themeName.euiColorPrimary }, - { token: 'tag.class.jade', foreground: themeName.euiColorPrimary }, - { token: 'meta.scss', foreground: themeName.euiColorAccent }, - { token: 'metatag', foreground: themeName.euiColorSecondary }, - { token: 'metatag.content.html', foreground: themeName.euiColorDanger }, - { token: 'metatag.html', foreground: themeName.euiColorMediumShade }, - { token: 'metatag.xml', foreground: themeName.euiColorMediumShade }, - { token: 'metatag.php', fontStyle: 'bold' }, + { token: 'attribute.name', foreground: euiTheme.euiColorDanger }, + { token: 'attribute.name.css', foreground: euiTheme.euiColorSecondary }, + { token: 'attribute.value', foreground: euiTheme.euiColorPrimary }, + { token: 'attribute.value.number', foreground: euiTheme.euiColorWarning }, + { token: 'attribute.value.unit', foreground: euiTheme.euiColorWarning }, + { token: 'attribute.value.html', foreground: euiTheme.euiColorPrimary }, + { token: 'attribute.value.xml', foreground: euiTheme.euiColorPrimary }, - { token: 'key', foreground: themeName.euiColorWarning }, - { token: 'string.key.json', foreground: themeName.euiColorDanger }, - { token: 'string.value.json', foreground: themeName.euiColorPrimary }, + { token: 'string', foreground: euiTheme.euiColorDanger }, + { token: 'string.html', foreground: euiTheme.euiColorPrimary }, + { token: 'string.sql', foreground: euiTheme.euiColorDanger }, + { token: 'string.yaml', foreground: euiTheme.euiColorPrimary }, - { token: 'attribute.name', foreground: themeName.euiColorDanger }, - { token: 'attribute.name.css', foreground: themeName.euiColorSecondary }, - { token: 'attribute.value', foreground: themeName.euiColorPrimary }, - { token: 'attribute.value.number', foreground: themeName.euiColorWarning }, - { token: 'attribute.value.unit', foreground: themeName.euiColorWarning }, - { token: 'attribute.value.html', foreground: themeName.euiColorPrimary }, - { token: 'attribute.value.xml', foreground: themeName.euiColorPrimary }, + { token: 'keyword', foreground: euiTheme.euiColorPrimary }, + { token: 'keyword.json', foreground: euiTheme.euiColorPrimary }, + { token: 'keyword.flow', foreground: euiTheme.euiColorWarning }, + { token: 'keyword.flow.scss', foreground: euiTheme.euiColorPrimary }, - { token: 'string', foreground: themeName.euiColorDanger }, - { token: 'string.html', foreground: themeName.euiColorPrimary }, - { token: 'string.sql', foreground: themeName.euiColorDanger }, - { token: 'string.yaml', foreground: themeName.euiColorPrimary }, + { token: 'operator.scss', foreground: euiTheme.euiColorDarkShade }, + { token: 'operator.sql', foreground: euiTheme.euiColorMediumShade }, + { token: 'operator.swift', foreground: euiTheme.euiColorMediumShade }, + { token: 'predefined.sql', foreground: euiTheme.euiColorMediumShade }, + ], + colors: { + 'editor.foreground': euiTheme.euiColorDarkestShade, + 'editor.background': euiTheme.euiColorEmptyShade, + 'editorLineNumber.foreground': euiTheme.euiColorDarkShade, + 'editorLineNumber.activeForeground': euiTheme.euiColorDarkShade, + 'editorIndentGuide.background': euiTheme.euiColorLightShade, + 'editor.selectionBackground': selectionBackgroundColor, + 'editorWidget.border': euiTheme.euiColorLightShade, + 'editorWidget.background': euiTheme.euiColorLightestShade, + }, + }; +} - { token: 'keyword', foreground: themeName.euiColorPrimary }, - { token: 'keyword.json', foreground: themeName.euiColorPrimary }, - { token: 'keyword.flow', foreground: themeName.euiColorWarning }, - { token: 'keyword.flow.scss', foreground: themeName.euiColorPrimary }, +const DARK_THEME = createTheme(darkTheme, '#343551'); +const LIGHT_THEME = createTheme(lightTheme, '#E3E4ED'); - { token: 'operator.scss', foreground: themeName.euiColorDarkShade }, - { token: 'operator.sql', foreground: themeName.euiColorMediumShade }, - { token: 'operator.swift', foreground: themeName.euiColorMediumShade }, - { token: 'predefined.sql', foreground: themeName.euiColorMediumShade }, - ], - colors: { - 'editor.foreground': themeName.euiColorDarkestShade, - 'editor.background': themeName.euiColorEmptyShade, - 'editorLineNumber.foreground': themeName.euiColorDarkShade, - 'editorLineNumber.activeForeground': themeName.euiColorDarkShade, - 'editorIndentGuide.background': themeName.euiColorLightShade, - 'editor.selectionBackground': `${IS_DARK_THEME ? '#343551' : '#E3E4ED'}`, - 'editorWidget.border': themeName.euiColorLightShade, - 'editorWidget.background': themeName.euiColorLightestShade, - }, -}; +export { DARK_THEME, LIGHT_THEME }; diff --git a/x-pack/legacy/plugins/canvas/public/components/editor/index.tsx b/x-pack/legacy/plugins/canvas/public/components/editor/index.tsx new file mode 100644 index 0000000000000..11c5c710b6e69 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/editor/index.tsx @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { useUiSetting } from '../../../../../../../src/plugins/kibana_react/public'; +import { Editor as BaseEditor, Props } from './editor'; + +export const Editor: React.FunctionComponent = props => { + const darkMode = useUiSetting('theme:darkMode'); + + return ; +}; diff --git a/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/flyout.tsx b/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/flyout.tsx index 0f03657e970bb..d09ef53f65fbb 100644 --- a/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/flyout.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/flyout.tsx @@ -6,13 +6,14 @@ import React from 'react'; +import { EuiFlyout, EuiFlyoutHeader, EuiFlyoutBody, EuiTitle } from '@elastic/eui'; import { SavedObjectFinder, SavedObjectMetaData, -} from 'ui/saved_objects/components/saved_object_finder'; -import { EuiFlyout, EuiFlyoutHeader, EuiFlyoutBody, EuiTitle } from '@elastic/eui'; +} from '../../../../../../../src/plugins/kibana_react/public/saved_objects'; // eslint-disable-line import { start } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; import { ComponentStrings } from '../../../i18n'; +import { CoreStart } from '../../../../../../../src/core/public'; const { AddEmbeddableFlyout: strings } = ComponentStrings; @@ -20,6 +21,8 @@ export interface Props { onClose: () => void; onSelect: (id: string, embeddableType: string) => void; availableEmbeddables: string[]; + savedObjects: CoreStart['savedObjects']; + uiSettings: CoreStart['uiSettings']; } export class AddEmbeddableFlyout extends React.Component { @@ -66,6 +69,8 @@ export class AddEmbeddableFlyout extends React.Component { savedObjectMetaData={availableSavedObjects} showFilter={true} noItemsMessage={strings.getNoItemsText()} + savedObjects={this.props.savedObjects} + uiSettings={this.props.uiSettings} /> diff --git a/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/index.tsx b/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/index.tsx index 612406c30f88e..c54c56e1561ca 100644 --- a/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/index.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/index.tsx @@ -14,6 +14,8 @@ import { AddEmbeddableFlyout, Props } from './flyout'; import { addElement } from '../../state/actions/elements'; import { getSelectedPage } from '../../state/selectors/workpad'; import { EmbeddableTypes } from '../../../canvas_plugin_src/expression_types/embeddable'; +import { WithKibanaProps } from '../../index'; +import { withKibana } from '../../../../../../../src/plugins/kibana_react/public'; const allowedEmbeddables = { [EmbeddableTypes.map]: (id: string) => { @@ -68,10 +70,10 @@ const mergeProps = ( }; }; -export class EmbeddableFlyoutPortal extends React.Component { +export class EmbeddableFlyoutPortal extends React.Component { el?: HTMLElement; - constructor(props: Props) { + constructor(props: Props & WithKibanaProps) { super(props); this.el = document.createElement('div'); @@ -97,6 +99,8 @@ export class EmbeddableFlyoutPortal extends React.Component { , this.el ); @@ -104,6 +108,7 @@ export class EmbeddableFlyoutPortal extends React.Component { } } -export const AddEmbeddablePanel = compose void }>( - connect(mapStateToProps, mapDispatchToProps, mergeProps) +export const AddEmbeddablePanel = compose void }>( + connect(mapStateToProps, mapDispatchToProps, mergeProps), + withKibana )(EmbeddableFlyoutPortal); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/index.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/index.ts index 756e84baffe54..1cc56eabaebd5 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/index.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/index.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import chrome from 'ui/chrome'; import { connect } from 'react-redux'; import { compose, withProps } from 'recompose'; // @ts-ignore Untyped local @@ -31,7 +30,9 @@ import { API_ROUTE_SHAREABLE_ZIP } from '../../../../../common/lib/constants'; import { renderFunctionNames } from '../../../../../shareable_runtime/supported_renderers'; import { ComponentStrings } from '../../../../../i18n'; +import { withKibana } from '../../../../../../../../../src/plugins/kibana_react/public/'; import { OnCloseFn } from '../workpad_export'; +import { WithKibanaProps } from '../../../../index'; const { WorkpadHeaderWorkpadExport: strings } = ComponentStrings; const getUnsupportedRenderers = (state: State) => { @@ -61,8 +62,15 @@ interface Props { export const ShareWebsiteFlyout = compose>( connect(mapStateToProps), + withKibana, withProps( - ({ unsupportedRenderers, renderedWorkpad, onClose, workpad }: Props): ComponentProps => ({ + ({ + unsupportedRenderers, + renderedWorkpad, + onClose, + workpad, + kibana, + }: Props & WithKibanaProps): ComponentProps => ({ unsupportedRenderers, onClose, onCopy: () => { @@ -74,10 +82,10 @@ export const ShareWebsiteFlyout = compose downloadRenderedWorkpad(renderedWorkpad); return; case 'shareRuntime': - downloadRuntime(); + downloadRuntime(kibana.services.http.basePath.get()); return; case 'shareZip': - const basePath = chrome.getBasePath(); + const basePath = kibana.services.http.basePath.get(); arrayBufferFetch .post(`${basePath}${API_ROUTE_SHAREABLE_ZIP}`, JSON.stringify(renderedWorkpad)) .then(blob => downloadZippedRuntime(blob.data)) diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts index f677f84362da7..a8ae785adafc1 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts @@ -24,8 +24,11 @@ import { getPdfUrl, createPdf } from './utils'; import { State, CanvasWorkpad } from '../../../../types'; // @ts-ignore Untyped local. import { fetch, arrayBufferFetch } from '../../../../common/lib/fetch'; +import { withKibana } from '../../../../../../../../src/plugins/kibana_react/public/'; +import { WithKibanaProps } from '../../../index'; import { ComponentStrings } from '../../../../i18n'; + const { WorkpadHeaderWorkpadExport: strings } = ComponentStrings; const mapStateToProps = (state: State) => ({ @@ -53,12 +56,17 @@ interface Props { export const WorkpadExport = compose( connect(mapStateToProps), + withKibana, withProps( - ({ workpad, pageCount, enabled }: Props): ComponentProps => ({ + ({ workpad, pageCount, enabled, kibana }: Props & WithKibanaProps): ComponentProps => ({ enabled, getExportUrl: type => { if (type === 'pdf') { - const { createPdfUri } = getPdfUrl(workpad, { pageCount }); + const { createPdfUri } = getPdfUrl( + workpad, + { pageCount }, + kibana.services.http.basePath.prepend + ); return getAbsoluteUrl(createPdfUri); } @@ -79,7 +87,7 @@ export const WorkpadExport = compose( onExport: type => { switch (type) { case 'pdf': - return createPdf(workpad, { pageCount }) + return createPdf(workpad, { pageCount }, kibana.services.http.basePath.prepend) .then(({ data }: { data: { job: { id: string } } }) => { notify.info(strings.getExportPDFMessage(), { title: strings.getExportPDFTitle(workpad.name), diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts index b06bf5a74b528..f0ca5fac1d271 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts @@ -5,7 +5,6 @@ */ import rison from 'rison-node'; -import chrome from 'ui/chrome'; // @ts-ignore Untyped local. import { fetch } from '../../../../common/lib/fetch'; import { CanvasWorkpad } from '../../../../types'; @@ -17,7 +16,9 @@ interface PageCount { pageCount: number; } -type Arguments = [CanvasWorkpad, PageCount]; +type AddBasePath = (url: string) => string; + +type Arguments = [CanvasWorkpad, PageCount, AddBasePath]; interface PdfUrlData { createPdfUri: string; @@ -26,9 +27,10 @@ interface PdfUrlData { export function getPdfUrl( { id, name: title, width, height }: CanvasWorkpad, - { pageCount }: PageCount + { pageCount }: PageCount, + addBasePath: (path: string) => string ): PdfUrlData { - const reportingEntry = chrome.addBasePath('/api/reporting/generate'); + const reportingEntry = addBasePath('/api/reporting/generate'); const canvasEntry = '/app/canvas#'; // The viewport in Reporting by specifying the dimensions. In order for things to work, diff --git a/x-pack/legacy/plugins/canvas/public/index.ts b/x-pack/legacy/plugins/canvas/public/index.ts new file mode 100644 index 0000000000000..b8358bfe022e6 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/index.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + PluginInitializer, + PluginInitializerContext, + CoreStart, +} from '../../../../../src/core/public'; +import { CanvasSetup, CanvasStart, CanvasSetupDeps, CanvasStartDeps, CanvasPlugin } from './plugin'; + +export const plugin: PluginInitializer< + CanvasSetup, + CanvasStart, + CanvasSetupDeps, + CanvasStartDeps +> = (initializerContext: PluginInitializerContext) => { + return new CanvasPlugin(); +}; + +export interface WithKibanaProps { + kibana: { + services: CoreStart & CanvasStartDeps; + }; +} + +// These are your public types & static code +export { CanvasSetup, CanvasStart }; diff --git a/x-pack/legacy/plugins/canvas/public/legacy.ts b/x-pack/legacy/plugins/canvas/public/legacy.ts new file mode 100644 index 0000000000000..49b88ee60921a --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/legacy.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { npSetup, npStart } from 'ui/new_platform'; +import { CanvasStartDeps } from './plugin'; // eslint-disable-line import/order + +// @ts-ignore Untyped Kibana Lib +import chrome, { loadingCount } from 'ui/chrome'; // eslint-disable-line import/order +// @ts-ignore Untyped Module +import { uiModules } from 'ui/modules'; // eslint-disable-line import/order +import { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url'; // eslint-disable-line import/order +import { Storage } from '../../../../../src/plugins/kibana_utils/public'; // eslint-disable-line import/order +// @ts-ignore Untyped Kibana Lib +import { formatMsg } from 'ui/notify/lib/format_msg'; // eslint-disable-line import/order + +const shimCoreSetup = { + ...npSetup.core, +}; +const shimCoreStart = { + ...npStart.core, +}; +const shimSetupPlugins = {}; +const shimStartPlugins: CanvasStartDeps = { + ...npStart.plugins, + __LEGACY: { + // ToDo: Copy directly into canvas + absoluteToParsedUrl, + // ToDo: Copy directly into canvas + formatMsg, + // ToDo: Remove in favor of core.application.register + setRootController: chrome.setRootController, + storage: Storage, + // ToDo: Won't be a part of New Platform. Will need to handle internally + trackSubUrlForApp: chrome.trackSubUrlForApp, + uiModules, + }, +}; + +// These methods are intended to be a replacement for import from 'ui/whatever' +// These will go away once all of this one plugin start/setup properly +// injects wherever these need to go. +export function getCoreSetup() { + return shimCoreSetup; +} + +export function getCoreStart() { + return shimCoreStart; +} + +export function getSetupPlugins() { + return shimSetupPlugins; +} + +export function getStartPlugins() { + return shimStartPlugins; +} diff --git a/x-pack/legacy/plugins/canvas/public/legacy_start.ts b/x-pack/legacy/plugins/canvas/public/legacy_start.ts new file mode 100644 index 0000000000000..49ec7acd6375d --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/legacy_start.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// TODO: These are legacy imports. We should work to have all of these come from New Platform +// Import the uiExports that the application uses +// These will go away as these plugins are converted to NP +import 'ui/autoload/all'; +import 'uiExports/visTypes'; +import 'uiExports/visResponseHandlers'; +import 'uiExports/visRequestHandlers'; +import 'uiExports/visEditorTypes'; +import 'uiExports/savedObjectTypes'; +import 'uiExports/spyModes'; +import 'uiExports/embeddableFactories'; +import 'uiExports/interpreter'; + +// load application code +import 'uiExports/canvas'; + +import { PluginInitializerContext } from '../../../../../src/core/public'; +import { plugin } from './'; +import { getCoreStart, getStartPlugins, getSetupPlugins, getCoreSetup } from './legacy'; +const pluginInstance = plugin({} as PluginInitializerContext); + +// Setup and Startup the plugin +export const setup = pluginInstance.setup(getCoreSetup(), getSetupPlugins()); +export const start = pluginInstance.start(getCoreStart(), getStartPlugins()); diff --git a/x-pack/legacy/plugins/canvas/public/lib/breadcrumbs.js b/x-pack/legacy/plugins/canvas/public/lib/breadcrumbs.js deleted file mode 100644 index dc90402d7f958..0000000000000 --- a/x-pack/legacy/plugins/canvas/public/lib/breadcrumbs.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import chrome from 'ui/chrome'; - -export const getBaseBreadcrumb = () => ({ - text: 'Canvas', - href: '#/', -}); - -export const getWorkpadBreadcrumb = ({ name = 'Workpad', id } = {}) => { - const output = { text: name }; - if (id != null) { - output.href = `#/workpad/${id}`; - } - return output; -}; - -export const setBreadcrumb = paths => { - chrome.breadcrumbs.set(Array.isArray(paths) ? paths : [paths]); -}; diff --git a/x-pack/legacy/plugins/canvas/public/lib/breadcrumbs.ts b/x-pack/legacy/plugins/canvas/public/lib/breadcrumbs.ts new file mode 100644 index 0000000000000..834d5868c35ea --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/lib/breadcrumbs.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ChromeBreadcrumb } from '../../../../../../src/core/public'; +import { getCoreStart } from '../legacy'; + +export const getBaseBreadcrumb = () => ({ + text: 'Canvas', + href: '#/', +}); + +export const getWorkpadBreadcrumb = ({ + name = 'Workpad', + id, +}: { name?: string; id?: string } = {}) => { + const output: ChromeBreadcrumb = { text: name }; + if (id != null) { + output.href = `#/workpad/${id}`; + } + return output; +}; + +export const setBreadcrumb = (paths: ChromeBreadcrumb | ChromeBreadcrumb[]) => { + const setBreadCrumbs = getCoreStart().chrome.setBreadcrumbs; + setBreadCrumbs(Array.isArray(paths) ? paths : [paths]); +}; diff --git a/x-pack/legacy/plugins/canvas/public/lib/clipboard.js b/x-pack/legacy/plugins/canvas/public/lib/clipboard.js index f9d68769c9c3a..1fd14f086c949 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/clipboard.js +++ b/x-pack/legacy/plugins/canvas/public/lib/clipboard.js @@ -4,10 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Storage } from '../../../../../../src/plugins/kibana_utils/public'; import { LOCALSTORAGE_CLIPBOARD } from '../../common/lib/constants'; import { getWindow } from './get_window'; -const storage = new Storage(getWindow().localStorage); +let storage = null; + +export const initClipboard = function(Storage) { + storage = new Storage(getWindow().localStorage); +}; + export const setClipboardData = data => storage.set(LOCALSTORAGE_CLIPBOARD, JSON.stringify(data)); export const getClipboardData = () => storage.get(LOCALSTORAGE_CLIPBOARD); diff --git a/x-pack/legacy/plugins/canvas/public/lib/custom_element_service.ts b/x-pack/legacy/plugins/canvas/public/lib/custom_element_service.ts index ee5059cc69b0d..478e2f8f18cf5 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/custom_element_service.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/custom_element_service.ts @@ -4,30 +4,31 @@ * you may not use this file except in compliance with the Elastic License. */ -// @ts-ignore unconverted Elastic lib -import chrome from 'ui/chrome'; import { AxiosPromise } from 'axios'; // @ts-ignore unconverted local file import { API_ROUTE_CUSTOM_ELEMENT } from '../../common/lib/constants'; // @ts-ignore unconverted local file import { fetch } from '../../common/lib/fetch'; import { CustomElement } from '../../types'; +import { getCoreStart } from '../legacy'; -const basePath = chrome.getBasePath(); -const apiPath = `${basePath}${API_ROUTE_CUSTOM_ELEMENT}`; +const getApiPath = function() { + const basePath = getCoreStart().http.basePath.get(); + return `${basePath}${API_ROUTE_CUSTOM_ELEMENT}`; +}; export const create = (customElement: CustomElement): AxiosPromise => - fetch.post(apiPath, customElement); + fetch.post(getApiPath(), customElement); export const get = (customElementId: string): Promise => fetch - .get(`${apiPath}/${customElementId}`) + .get(`${getApiPath()}/${customElementId}`) .then(({ data: element }: { data: CustomElement }) => element); export const update = (id: string, element: CustomElement): AxiosPromise => - fetch.put(`${apiPath}/${id}`, element); + fetch.put(`${getApiPath()}/${id}`, element); -export const remove = (id: string): AxiosPromise => fetch.delete(`${apiPath}/${id}`); +export const remove = (id: string): AxiosPromise => fetch.delete(`${getApiPath()}/${id}`); export const find = async ( searchTerm: string @@ -35,7 +36,7 @@ export const find = async ( const validSearchTerm = typeof searchTerm === 'string' && searchTerm.length > 0; return fetch - .get(`${apiPath}/find?name=${validSearchTerm ? searchTerm : ''}&perPage=10000`) + .get(`${getApiPath()}/find?name=${validSearchTerm ? searchTerm : ''}&perPage=10000`) .then( ({ data: customElements }: { data: { total: number; customElements: CustomElement[] } }) => customElements diff --git a/x-pack/legacy/plugins/canvas/public/lib/documentation_links.ts b/x-pack/legacy/plugins/canvas/public/lib/documentation_links.ts new file mode 100644 index 0000000000000..a43653f420ce0 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/lib/documentation_links.ts @@ -0,0 +1,13 @@ +/* + * 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 { getCoreStart } from '../legacy'; + +export const getDocumentationLinks = () => ({ + canvas: `${getCoreStart().docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${ + getCoreStart().docLinks.DOC_LINK_VERSION + }/canvas.html`, +}); diff --git a/x-pack/legacy/plugins/canvas/public/lib/download_workpad.ts b/x-pack/legacy/plugins/canvas/public/lib/download_workpad.ts index 7bcf0ac948613..e4866641fd9e1 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/download_workpad.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/download_workpad.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ import fileSaver from 'file-saver'; -import chrome from 'ui/chrome'; import { API_ROUTE_SHAREABLE_RUNTIME_DOWNLOAD } from '../../common/lib/constants'; import { ErrorStrings } from '../../i18n'; // @ts-ignore untyped local @@ -37,9 +36,8 @@ export const downloadRenderedWorkpad = async (renderedWorkpad: CanvasRenderedWor } }; -export const downloadRuntime = async () => { +export const downloadRuntime = async (basePath: string) => { try { - const basePath = chrome.getBasePath(); const path = `${basePath}${API_ROUTE_SHAREABLE_RUNTIME_DOWNLOAD}`; window.open(path); return; diff --git a/x-pack/legacy/plugins/canvas/public/lib/es_service.ts b/x-pack/legacy/plugins/canvas/public/lib/es_service.ts index a4553369abdd1..2fa1bf94ad669 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/es_service.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/es_service.ts @@ -4,24 +4,32 @@ * you may not use this file except in compliance with the Elastic License. */ -import chrome from 'ui/chrome'; import { API_ROUTE } from '../../common/lib/constants'; // @ts-ignore untyped local import { fetch } from '../../common/lib/fetch'; import { ErrorStrings } from '../../i18n'; // @ts-ignore untyped local import { notify } from './notify'; +import { getCoreStart } from '../legacy'; const { esService: strings } = ErrorStrings; -const basePath = chrome.getBasePath(); -const apiPath = basePath + API_ROUTE; -const savedObjectsClient = chrome.getSavedObjectsClient(); -const AdvancedSettings = chrome.getUiSettingsClient(); +const getApiPath = function() { + const basePath = getCoreStart().http.basePath.get(); + return basePath + API_ROUTE; +}; + +const getSavedObjectsClient = function() { + return getCoreStart().savedObjects.client; +}; + +const getAdvancedSettings = function() { + return getCoreStart().uiSettings; +}; export const getFields = (index = '_all') => { return fetch - .get(`${apiPath}/es_fields?index=${index}`) + .get(`${getApiPath()}/es_fields?index=${index}`) .then(({ data: mapping }: { data: object }) => Object.keys(mapping) .filter(field => !field.startsWith('_')) // filters out meta fields @@ -35,7 +43,7 @@ export const getFields = (index = '_all') => { }; export const getIndices = () => - savedObjectsClient + getSavedObjectsClient() .find({ type: 'index-pattern', fields: ['title'], @@ -50,10 +58,10 @@ export const getIndices = () => .catch((err: Error) => notify.error(err, { title: strings.getIndicesFetchErrorMessage() })); export const getDefaultIndex = () => { - const defaultIndexId = AdvancedSettings.get('defaultIndex'); + const defaultIndexId = getAdvancedSettings().get('defaultIndex'); return defaultIndexId - ? savedObjectsClient + ? getSavedObjectsClient() .get('index-pattern', defaultIndexId) .then(defaultIndex => defaultIndex.attributes.title) .catch(err => notify.error(err, { title: strings.getDefaultIndexFetchErrorMessage() })) diff --git a/x-pack/legacy/plugins/canvas/public/lib/load_expression_types.js b/x-pack/legacy/plugins/canvas/public/lib/load_expression_types.js index e63b29eed2d58..fb23f9459d30b 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/load_expression_types.js +++ b/x-pack/legacy/plugins/canvas/public/lib/load_expression_types.js @@ -8,6 +8,8 @@ import { argTypeSpecs } from '../expression_types/arg_types'; import { datasourceSpecs } from '../expression_types/datasources'; import { argTypeRegistry, datasourceRegistry } from '../expression_types'; -// register default args, arg types, and expression types -argTypeSpecs.forEach(expFn => argTypeRegistry.register(expFn)); -datasourceSpecs.forEach(expFn => datasourceRegistry.register(expFn)); +export function loadExpressionTypes() { + // register default args, arg types, and expression types + argTypeSpecs.forEach(expFn => argTypeRegistry.register(expFn)); + datasourceSpecs.forEach(expFn => datasourceRegistry.register(expFn)); +} diff --git a/x-pack/legacy/plugins/canvas/public/lib/load_transitions.js b/x-pack/legacy/plugins/canvas/public/lib/load_transitions.js index e3fea75cedc80..915d63142bbf7 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/load_transitions.js +++ b/x-pack/legacy/plugins/canvas/public/lib/load_transitions.js @@ -7,4 +7,6 @@ import { transitions } from '../transitions'; import { transitionsRegistry } from './transitions_registry'; -transitions.forEach(spec => transitionsRegistry.register(spec)); +export function loadTransitions() { + transitions.forEach(spec => transitionsRegistry.register(spec)); +} diff --git a/x-pack/legacy/plugins/canvas/public/lib/loading_indicator.ts b/x-pack/legacy/plugins/canvas/public/lib/loading_indicator.ts index ee69aa24df55f..a95f4278b6a69 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/loading_indicator.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/loading_indicator.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -// @ts-ignore -import { loadingCount } from 'ui/chrome'; +import * as Rx from 'rxjs'; +import { CoreStart } from 'src/core/public'; let isActive = false; @@ -14,17 +14,22 @@ export interface LoadingIndicatorInterface { hide: () => void; } +const loadingCount$ = new Rx.BehaviorSubject(0); + +export const initLoadingIndicator = (addLoadingCount: CoreStart['http']['addLoadingCount']) => + addLoadingCount(loadingCount$); + export const loadingIndicator = { show: () => { if (!isActive) { - loadingCount.increment(); isActive = true; + loadingCount$.next(1); } }, hide: () => { if (isActive) { - loadingCount.decrement(); isActive = false; + loadingCount$.next(0); } }, }; diff --git a/x-pack/legacy/plugins/canvas/public/lib/notify.js b/x-pack/legacy/plugins/canvas/public/lib/notify.js index 928d49b7b61e0..64876a02a3c64 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/notify.js +++ b/x-pack/legacy/plugins/canvas/public/lib/notify.js @@ -5,8 +5,15 @@ */ import { get } from 'lodash'; -import { toastNotifications } from 'ui/notify'; -import { formatMsg } from 'ui/notify/lib/format_msg'; +import { getCoreStart, getStartPlugins } from '../legacy'; + +const getToastNotifications = function() { + return getCoreStart().notifications.toasts; +}; + +const formatMsg = function(...args) { + return getStartPlugins().__LEGACY.formatMsg(...args); +}; const getToast = (err, opts = {}) => { const errData = get(err, 'response') || err; @@ -31,15 +38,15 @@ export const notify = { * @param {Object} opts: option to override toast title or icon, see https://github.com/elastic/kibana/blob/master/src/legacy/ui/public/notify/toasts/TOAST_NOTIFICATIONS.md */ error(err, opts) { - toastNotifications.addDanger(getToast(err, opts)); + getToastNotifications().addDanger(getToast(err, opts)); }, warning(err, opts) { - toastNotifications.addWarning(getToast(err, opts)); + getToastNotifications().addWarning(getToast(err, opts)); }, info(err, opts) { - toastNotifications.add(getToast(err, opts)); + getToastNotifications().add(getToast(err, opts)); }, success(err, opts) { - toastNotifications.addSuccess(getToast(err, opts)); + getToastNotifications().addSuccess(getToast(err, opts)); }, }; diff --git a/x-pack/legacy/plugins/canvas/public/lib/workpad_service.js b/x-pack/legacy/plugins/canvas/public/lib/workpad_service.js index f1ed069c15d4d..b678a532ec084 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/workpad_service.js +++ b/x-pack/legacy/plugins/canvas/public/lib/workpad_service.js @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import chrome from 'ui/chrome'; import { API_ROUTE_WORKPAD, API_ROUTE_WORKPAD_ASSETS, @@ -12,18 +11,29 @@ import { DEFAULT_WORKPAD_CSS, } from '../../common/lib/constants'; import { fetch } from '../../common/lib/fetch'; +import { getCoreStart } from '../legacy'; -const basePath = chrome.getBasePath(); -const apiPath = `${basePath}${API_ROUTE_WORKPAD}`; -const apiPathAssets = `${basePath}${API_ROUTE_WORKPAD_ASSETS}`; -const apiPathStructures = `${basePath}${API_ROUTE_WORKPAD_STRUCTURES}`; +const getApiPath = function() { + const basePath = getCoreStart().http.basePath.get(); + return `${basePath}${API_ROUTE_WORKPAD}`; +}; + +const getApiPathStructures = function() { + const basePath = getCoreStart().http.basePath.get(); + return `${basePath}${API_ROUTE_WORKPAD_STRUCTURES}`; +}; + +const getApiPathAssets = function() { + const basePath = getCoreStart().http.basePath.get(); + return `${basePath}${API_ROUTE_WORKPAD_ASSETS}`; +}; export function create(workpad) { - return fetch.post(apiPath, { ...workpad, assets: workpad.assets || {} }); + return fetch.post(getApiPath(), { ...workpad, assets: workpad.assets || {} }); } export function get(workpadId) { - return fetch.get(`${apiPath}/${workpadId}`).then(({ data: workpad }) => { + return fetch.get(`${getApiPath()}/${workpadId}`).then(({ data: workpad }) => { // shim old workpads with new properties return { css: DEFAULT_WORKPAD_CSS, ...workpad }; }); @@ -31,25 +41,25 @@ export function get(workpadId) { // TODO: I think this function is never used. Look into and remove the corresponding route as well export function update(id, workpad) { - return fetch.put(`${apiPath}/${id}`, workpad); + return fetch.put(`${getApiPath()}/${id}`, workpad); } export function updateWorkpad(id, workpad) { - return fetch.put(`${apiPathStructures}/${id}`, workpad); + return fetch.put(`${getApiPathStructures()}/${id}`, workpad); } export function updateAssets(id, workpadAssets) { - return fetch.put(`${apiPathAssets}/${id}`, workpadAssets); + return fetch.put(`${getApiPathAssets()}/${id}`, workpadAssets); } export function remove(id) { - return fetch.delete(`${apiPath}/${id}`); + return fetch.delete(`${getApiPath()}/${id}`); } export function find(searchTerm) { const validSearchTerm = typeof searchTerm === 'string' && searchTerm.length > 0; return fetch - .get(`${apiPath}/find?name=${validSearchTerm ? searchTerm : ''}&perPage=10000`) + .get(`${getApiPath()}/find?name=${validSearchTerm ? searchTerm : ''}&perPage=10000`) .then(({ data: workpads }) => workpads); } diff --git a/x-pack/legacy/plugins/canvas/public/plugin.tsx b/x-pack/legacy/plugins/canvas/public/plugin.tsx new file mode 100644 index 0000000000000..5190e8521101b --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/plugin.tsx @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; +import { Chrome } from 'ui/chrome'; +import { IModule } from 'angular'; +import { i18n } from '@kbn/i18n'; +import { Storage } from '../../../../../src/plugins/kibana_utils/public'; +import { CoreSetup, CoreStart, Plugin } from '../../../../../src/core/public'; +// @ts-ignore: Untyped Local +import { initStateManagement, initLocationProvider } from './angular/config'; +import { CanvasRootControllerFactory } from './angular/controllers'; +// @ts-ignore: Untypled Local +import { initStore } from './angular/services'; +// @ts-ignore: untyped local component +import { HelpMenu } from './components/help_menu/help_menu'; +// @ts-ignore: untyped local +import { loadExpressionTypes } from './lib/load_expression_types'; +// @ts-ignore: untyped local +import { loadTransitions } from './lib/load_transitions'; +import { initLoadingIndicator } from './lib/loading_indicator'; +import { getDocumentationLinks } from './lib/documentation_links'; + +// @ts-ignore: untyped local +import { initClipboard } from './lib/clipboard'; + +export { CoreStart }; +/** + * These are the private interfaces for the services your plugin depends on. + * @internal + */ +// This interface will be built out as we require other plugins for setup +export interface CanvasSetupDeps {} // eslint-disable-line @typescript-eslint/no-empty-interface +export interface CanvasStartDeps { + __LEGACY: { + absoluteToParsedUrl: (url: string, basePath: string) => any; + formatMsg: any; + setRootController: Chrome['setRootController']; + storage: typeof Storage; + trackSubUrlForApp: Chrome['trackSubUrlForApp']; + uiModules: { + get: (module: string) => IModule; + }; + }; +} + +/** + * These are the interfaces with your public contracts. You should export these + * for other plugins to use in _their_ `SetupDeps`/`StartDeps` interfaces. + * @public + */ +// These interfaces are empty for now but will be populate as we need to export +// things for other plugins to use at startup or runtime +export interface CanvasSetup {} // eslint-disable-line @typescript-eslint/no-empty-interface +export interface CanvasStart {} // eslint-disable-line @typescript-eslint/no-empty-interface + +/** @internal */ +export class CanvasPlugin + implements Plugin { + public setup(core: CoreSetup, plugins: CanvasSetupDeps) { + // This is where any setup actions need to occur. + // Things like registering functions to the interpreter that need + // to be available everywhere, not just in Canvas + + return {}; + } + + public start(core: CoreStart, plugins: CanvasStartDeps) { + loadExpressionTypes(); + loadTransitions(); + + initStateManagement(core, plugins); + initLocationProvider(core, plugins); + initStore(core, plugins); + initClipboard(plugins.__LEGACY.storage); + initLoadingIndicator(core.http.addLoadingCount); + + const CanvasRootController = CanvasRootControllerFactory(core, plugins); + plugins.__LEGACY.setRootController('canvas', CanvasRootController); + core.chrome.setHelpExtension({ + appName: i18n.translate('xpack.canvas.helpMenu.appName', { + defaultMessage: 'Canvas', + }), + links: [ + { + linkType: 'documentation', + href: getDocumentationLinks().canvas, + }, + ], + content: domNode => () => { + ReactDOM.render(, domNode); + }, + }); + + return {}; + } +} diff --git a/x-pack/legacy/plugins/canvas/public/state/initial_state.js b/x-pack/legacy/plugins/canvas/public/state/initial_state.js index 4ca4713fa8fb7..40c017543147f 100644 --- a/x-pack/legacy/plugins/canvas/public/state/initial_state.js +++ b/x-pack/legacy/plugins/canvas/public/state/initial_state.js @@ -5,7 +5,7 @@ */ import { get } from 'lodash'; -import { capabilities } from 'ui/capabilities'; +import { getCoreStart } from '../legacy'; import { getDefaultWorkpad } from './defaults'; export const getInitialState = path => { @@ -13,7 +13,7 @@ export const getInitialState = path => { app: {}, // Kibana stuff in here assets: {}, // assets end up here transient: { - canUserWrite: capabilities.get().canvas.save, + canUserWrite: getCoreStart().application.capabilities.canvas.save, zoomScale: 1, elementStats: { total: 0, diff --git a/x-pack/legacy/plugins/canvas/public/state/reducers/workpad.js b/x-pack/legacy/plugins/canvas/public/state/reducers/workpad.js index 40e36a455705b..12733680ed32d 100644 --- a/x-pack/legacy/plugins/canvas/public/state/reducers/workpad.js +++ b/x-pack/legacy/plugins/canvas/public/state/reducers/workpad.js @@ -5,7 +5,7 @@ */ import { handleActions } from 'redux-actions'; -import { npStart } from 'ui/new_platform'; +import { getCoreStart } from '../../legacy'; import { getDefaultWorkpad } from '../defaults'; import { setWorkpad, @@ -22,7 +22,7 @@ import { APP_ROUTE_WORKPAD } from '../../../common/lib/constants'; export const workpadReducer = handleActions( { [setWorkpad]: (workpadState, { payload }) => { - npStart.core.chrome.recentlyAccessed.add( + getCoreStart().chrome.recentlyAccessed.add( `${APP_ROUTE_WORKPAD}/${payload.id}`, payload.name, payload.id @@ -39,7 +39,7 @@ export const workpadReducer = handleActions( }, [setName]: (workpadState, { payload }) => { - npStart.core.chrome.recentlyAccessed.add( + getCoreStart().chrome.recentlyAccessed.add( `${APP_ROUTE_WORKPAD}/${workpadState.id}`, payload, workpadState.id diff --git a/x-pack/legacy/plugins/canvas/scripts/lint.js b/x-pack/legacy/plugins/canvas/scripts/lint.js index f0f462b2041c5..effc654712e6c 100644 --- a/x-pack/legacy/plugins/canvas/scripts/lint.js +++ b/x-pack/legacy/plugins/canvas/scripts/lint.js @@ -4,4 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -require('./_helpers').runKibanaScript('eslint', ['x-pack/legacy/plugins/canvas/**/*.{js,jsx}']); +require('./_helpers').runKibanaScript('eslint', [ + 'x-pack/legacy/plugins/canvas/**/*.{js,jsx,ts,tsx}', +]); diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js index 24d9c45510aef..57a43f924b7e6 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js @@ -241,10 +241,17 @@ export class ESSearchSource extends AbstractESSource { const searchSource = await this._makeSearchSource(searchFilters, 0); searchSource.setField('aggs', { + totalEntities: { + cardinality: { + field: topHitsSplitField, + precision_threshold: 1, + } + }, entitySplit: { terms: { field: topHitsSplitField, - size: ES_SIZE_LIMIT + size: ES_SIZE_LIMIT, + shard_size: ES_SIZE_LIMIT, }, aggs: { entityHits: { @@ -256,24 +263,29 @@ export class ESSearchSource extends AbstractESSource { const resp = await this._runEsQuery(layerName, searchSource, registerCancelCallback, 'Elasticsearch document top hits request'); - let hasTrimmedResults = false; const allHits = []; const entityBuckets = _.get(resp, 'aggregations.entitySplit.buckets', []); + const totalEntities = _.get(resp, 'aggregations.totalEntities.value', 0); + // can not compare entityBuckets.length to totalEntities because totalEntities is an approximate + const areEntitiesTrimmed = entityBuckets.length >= ES_SIZE_LIMIT; + let areTopHitsTrimmed = false; entityBuckets.forEach(entityBucket => { const total = _.get(entityBucket, 'entityHits.hits.total', 0); const hits = _.get(entityBucket, 'entityHits.hits.hits', []); // Reverse hits list so top documents by sort are drawn on top allHits.push(...hits.reverse()); if (total > hits.length) { - hasTrimmedResults = true; + areTopHitsTrimmed = true; } }); return { hits: allHits, meta: { - areResultsTrimmed: hasTrimmedResults, + areResultsTrimmed: areEntitiesTrimmed || areTopHitsTrimmed, // used to force re-fetch when zooming in + areEntitiesTrimmed, entityCount: entityBuckets.length, + totalEntities, } }; } @@ -459,24 +471,28 @@ export class ESSearchSource extends AbstractESSource { } if (this._isTopHits()) { - const entitiesFoundMsg = i18n.translate('xpack.maps.esSearch.topHitsEntitiesCountMsg', { - defaultMessage: `Found {entityCount} entities.`, - values: { entityCount: meta.entityCount } - }); - if (meta.areResultsTrimmed) { - const trimmedMsg = i18n.translate('xpack.maps.esSearch.topHitsResultsTrimmedMsg', { - defaultMessage: `Results limited to most recent {topHitsSize} documents per entity.`, - values: { topHitsSize: this._descriptor.topHitsSize } + const entitiesFoundMsg = meta.areEntitiesTrimmed + ? i18n.translate('xpack.maps.esSearch.topHitsResultsTrimmedMsg', { + defaultMessage: `Results limited to first {entityCount} entities of ~{totalEntities}.`, + values: { + entityCount: meta.entityCount, + totalEntities: meta.totalEntities, + } + }) + : i18n.translate('xpack.maps.esSearch.topHitsEntitiesCountMsg', { + defaultMessage: `Found {entityCount} entities.`, + values: { entityCount: meta.entityCount } }); - return { - tooltipContent: `${entitiesFoundMsg} ${trimmedMsg}`, - areResultsTrimmed: false - }; - } + const docsPerEntityMsg = i18n.translate('xpack.maps.esSearch.topHitsSizeMsg', { + defaultMessage: `Showing top {topHitsSize} documents per entity.`, + values: { topHitsSize: this._descriptor.topHitsSize } + }); return { - tooltipContent: entitiesFoundMsg, - areResultsTrimmed: false + tooltipContent: `${entitiesFoundMsg} ${docsPerEntityMsg}`, + // Used to show trimmed icon in legend + // user only needs to be notified of trimmed results when entities are trimmed + areResultsTrimmed: meta.areEntitiesTrimmed }; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/_index.scss b/x-pack/legacy/plugins/maps/public/layers/styles/_index.scss index bd517f81517c2..9060a4a98a4bc 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/_index.scss +++ b/x-pack/legacy/plugins/maps/public/layers/styles/_index.scss @@ -1,3 +1,3 @@ @import './components/color_gradient'; -@import './components/static_dynamic_style_row'; +@import './vector/components/static_dynamic_style_row'; @import './vector/components/color/color_stops'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/_static_dynamic_style_row.scss b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/_static_dynamic_style_row.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/_static_dynamic_style_row.scss rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/_static_dynamic_style_row.scss diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js index a39db57dc4883..98b20cb3ad62e 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js @@ -6,18 +6,15 @@ import React from 'react'; -import { StaticDynamicStyleRow } from '../../../components/static_dynamic_style_row'; +import { StaticDynamicStyleRow } from '../static_dynamic_style_row'; import { DynamicColorSelection } from './dynamic_color_selection'; import { StaticColorSelection } from './static_color_selection'; -import { getVectorStyleLabel } from '../get_vector_style_label'; export function VectorStyleColorEditor(props) { return ( { @@ -37,7 +34,7 @@ export class StaticDynamicStyleRow extends React.Component { type: VectorStyle.STYLE_TYPE.STATIC, options, }; - this.props.handlePropertyChange(this.props.property, styleDescriptor); + this.props.handlePropertyChange(this.props.styleProperty.getStyleName(), styleDescriptor); }; _onDynamicStyleChange = options => { @@ -45,7 +42,7 @@ export class StaticDynamicStyleRow extends React.Component { type: VectorStyle.STYLE_TYPE.DYNAMIC, options, }; - this.props.handlePropertyChange(this.props.property, styleDescriptor); + this.props.handlePropertyChange(this.props.styleProperty.getStyleName(), styleDescriptor); }; _onTypeToggle = () => { @@ -100,7 +97,10 @@ export class StaticDynamicStyleRow extends React.Component { - + {this._renderStyleSelector()} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js index c8e4150fd2c26..3043d57c04037 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js @@ -15,7 +15,6 @@ import { OrientationEditor } from './orientation/orientation_editor'; import { getDefaultDynamicProperties, getDefaultStaticProperties, - vectorStyles, } from '../vector_style_defaults'; import { DEFAULT_FILL_COLORS, DEFAULT_LINE_COLORS } from '../../color_utils'; import { VECTOR_SHAPE_TYPES } from '../../../sources/vector_feature_types'; @@ -121,10 +120,9 @@ export class VectorStyleEditor extends Component { _renderFillColor() { return ( { const keywordFields = getKeywordFields(fields); + const textFields = getTextFields(fields); const numericalFields = getNumericalFields(fields); const ipFields = getIpFields(fields); const geoFields = getGeoFields(fields); @@ -148,6 +149,10 @@ async function combineFieldsAndAggs( case ML_JOB_AGGREGATION.LAT_LONG: geoFields.forEach(f => mix(f, a)); break; + case ML_JOB_AGGREGATION.INFO_CONTENT: + case ML_JOB_AGGREGATION.HIGH_INFO_CONTENT: + case ML_JOB_AGGREGATION.LOW_INFO_CONTENT: + textFields.forEach(f => mix(f, a)); case ML_JOB_AGGREGATION.DISTINCT_COUNT: case ML_JOB_AGGREGATION.HIGH_DISTINCT_COUNT: case ML_JOB_AGGREGATION.LOW_DISTINCT_COUNT: @@ -220,6 +225,10 @@ function getKeywordFields(fields: Field[]): Field[] { return fields.filter(f => f.type === ES_FIELD_TYPES.KEYWORD); } +function getTextFields(fields: Field[]): Field[] { + return fields.filter(f => f.type === ES_FIELD_TYPES.TEXT); +} + function getIpFields(fields: Field[]): Field[] { return fields.filter(f => f.type === ES_FIELD_TYPES.IP); } diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/hosts/selectors.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/hosts/selectors.ts index 1c900944752c4..ab2502676d6f7 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/lib/hosts/selectors.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/lib/hosts/selectors.ts @@ -16,6 +16,6 @@ export const ALL_HOSTS_WIDGET_DRAGGABLE_HOSTS = `${ALL_HOSTS_WIDGET} ${ALL_HOSTS /** Clicking this button displays the `Events` tab */ export const EVENTS_TAB_BUTTON = '[data-test-subj="navigation-events"]'; -export const NAVIGATION_HOSTS_ALL_HOSTS = '[data-test-subj="navigation-link-allHosts"]'; +export const NAVIGATION_HOSTS_ALL_HOSTS = '[data-test-subj="navigation-allHosts"]'; -export const NAVIGATION_HOSTS_ANOMALIES = '[data-test-subj="navigation-link-anomalies"]'; +export const NAVIGATION_HOSTS_ANOMALIES = '[data-test-subj="navigation-anomalies"]'; diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/url_state/url_state.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/url_state/url_state.spec.ts index b1867a437f7f4..21829a0c20228 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/url_state/url_state.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/url_state/url_state.spec.ts @@ -181,7 +181,7 @@ describe('url state', () => { cy.url().should('include', `query=(language:kuery,query:'source.ip:%20%2210.142.0.9%22%20')`); }); - it.skip('sets the url state when kql is set and check if href reflect this change', () => { + it('sets the url state when kql is set and check if href reflect this change', () => { loginAndWaitForPage(ABSOLUTE_DATE_RANGE.url); cy.get(KQL_INPUT, { timeout: 5000 }).type('source.ip: "10.142.0.9" {enter}'); cy.get(NAVIGATION_HOSTS) @@ -194,7 +194,7 @@ describe('url state', () => { ); }); - it.skip('sets KQL in host page and detail page and check if href match on breadcrumb, tabs and subTabs', () => { + it('sets KQL in host page and detail page and check if href match on breadcrumb, tabs and subTabs', () => { loginAndWaitForPage(ABSOLUTE_DATE_RANGE.urlHost); cy.get(KQL_INPUT, { timeout: 5000 }).type('host.name: "siem-kibana" {enter}'); cy.get(NAVIGATION_HOSTS_ALL_HOSTS, { timeout: 5000 }) diff --git a/x-pack/legacy/plugins/siem/index.test.ts b/x-pack/legacy/plugins/siem/index.test.ts deleted file mode 100644 index 5b7c488eb174c..0000000000000 --- a/x-pack/legacy/plugins/siem/index.test.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { getRequiredPlugins } from '.'; - -// This test is a temporary test which is so we do not accidentally check-in -// feature flags turned on from "alerting" and "actions". If those get -// turned on during a check-in it will cause everyone's Kibana to not start. -// Once alerting and actions are part of the plugins by default this test -// should be removed. -describe('siem plugin tests', () => { - describe('getRequiredPlugins', () => { - test('null settings returns regular kibana and elasticsearch plugins', () => { - expect(getRequiredPlugins(null, null)).toEqual(['kibana', 'elasticsearch']); - }); - - test('undefined settings returns regular kibana and elasticsearch plugins', () => { - expect(getRequiredPlugins(undefined, undefined)).toEqual(['kibana', 'elasticsearch']); - }); - - test('alertingFeatureEnabled being false returns regular kibana and elasticsearch plugins', () => { - expect(getRequiredPlugins('false', undefined)).toEqual(['kibana', 'elasticsearch']); - }); - - test('alertingFeatureEnabled being true returns action and alerts', () => { - expect(getRequiredPlugins('true', undefined)).toEqual([ - 'kibana', - 'elasticsearch', - 'alerting', - 'actions', - ]); - }); - - test('alertingFeatureEnabled being false but a string for siemIndex returns alerting and actions', () => { - expect(getRequiredPlugins('false', '.siem-signals-frank')).toEqual([ - 'kibana', - 'elasticsearch', - 'alerting', - 'actions', - ]); - }); - - test('alertingFeatureEnabled being true and a string for siemIndex returns alerting and actions', () => { - expect(getRequiredPlugins('true', '.siem-signals-frank')).toEqual([ - 'kibana', - 'elasticsearch', - 'alerting', - 'actions', - ]); - }); - - test('alertingFeatureEnabled being true and an empty string for siemIndex returns regular kibana and elasticsearch plugins', () => { - expect(getRequiredPlugins(undefined, '')).toEqual(['kibana', 'elasticsearch']); - }); - - test('alertingFeatureEnabled being true and a string of spaces for siemIndex returns regular kibana and elasticsearch plugins', () => { - expect(getRequiredPlugins(undefined, ' ')).toEqual(['kibana', 'elasticsearch']); - }); - - test('alertingFeatureEnabled being null and a string for siemIndex returns alerting and actions', () => { - expect(getRequiredPlugins(null, '.siem-signals-frank')).toEqual([ - 'kibana', - 'elasticsearch', - 'alerting', - 'actions', - ]); - }); - - test('alertingFeatureEnabled being undefined and a string for siemIndex returns alerting and actions', () => { - expect(getRequiredPlugins(undefined, '.siem-signals-frank')).toEqual([ - 'kibana', - 'elasticsearch', - 'alerting', - 'actions', - ]); - }); - }); -}); diff --git a/x-pack/legacy/plugins/siem/index.ts b/x-pack/legacy/plugins/siem/index.ts index 06aaec631be66..72b4ec588a5a4 100644 --- a/x-pack/legacy/plugins/siem/index.ts +++ b/x-pack/legacy/plugins/siem/index.ts @@ -28,36 +28,13 @@ import { } from './common/constants'; import { defaultIndexPattern } from './default_index_pattern'; -// This is VERY TEMPORARY as we need a way to turn on alerting and actions -// for the server without having to manually edit this file. Once alerting -// and actions has their enabled true by default this can be removed. -// 'alerting', 'actions' are hidden behind feature flags at the moment so if you turn -// these on without the feature flags turned on then Kibana will crash since we are a legacy plugin -// and legacy plugins cannot have optional requirements. -// This returns ['alerting', 'actions', 'kibana', 'elasticsearch'] iff alertingFeatureEnabled is true -// or if the developer signalsIndex is setup. Otherwise this returns ['kibana', 'elasticsearch'] -export const getRequiredPlugins = ( - alertingFeatureEnabled: string | null | undefined, - signalsIndex: string | null | undefined -) => { - const baseRequire = ['kibana', 'elasticsearch']; - if ( - (signalsIndex != null && signalsIndex.trim() !== '') || - (alertingFeatureEnabled && alertingFeatureEnabled.toLowerCase() === 'true') - ) { - return [...baseRequire, 'alerting', 'actions']; - } else { - return baseRequire; - } -}; - // eslint-disable-next-line @typescript-eslint/no-explicit-any export const siem = (kibana: any) => { return new kibana.Plugin({ id: APP_ID, configPrefix: 'xpack.siem', publicDir: resolve(__dirname, 'public'), - require: getRequiredPlugins(process.env.ALERTING_FEATURE_ENABLED, process.env.SIGNALS_INDEX), + require: ['kibana', 'elasticsearch', 'alerting', 'actions'], uiExports: { app: { description: i18n.translate('xpack.siem.securityDescription', { diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx index 97cf9522f488f..fbeb1a2090cfd 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx @@ -11,50 +11,52 @@ import { CONSTANTS } from '../url_state/constants'; import { SiemNavigationComponent } from './'; import { setBreadcrumbs } from './breadcrumbs'; import { navTabs } from '../../pages/home/home_navigations'; -import { TabNavigationProps } from './tab_navigation/types'; import { HostsTableType } from '../../store/hosts/model'; import { RouteSpyState } from '../../utils/route/types'; +import { SiemNavigationProps, SiemNavigationComponentProps } from './types'; jest.mock('./breadcrumbs', () => ({ setBreadcrumbs: jest.fn(), })); describe('SIEM Navigation', () => { - const mockProps: TabNavigationProps & RouteSpyState = { + const mockProps: SiemNavigationComponentProps & SiemNavigationProps & RouteSpyState = { pageName: 'hosts', pathName: '/hosts', detailName: undefined, search: '', tabName: HostsTableType.authentications, navTabs, - [CONSTANTS.timerange]: { - global: { - [CONSTANTS.timerange]: { - from: 1558048243696, - fromStr: 'now-24h', - kind: 'relative', - to: 1558134643697, - toStr: 'now', - }, - linkTo: ['timeline'], + urlState: { + [CONSTANTS.timerange]: { + global: { + [CONSTANTS.timerange]: { + from: 1558048243696, + fromStr: 'now-24h', + kind: 'relative', + to: 1558134643697, + toStr: 'now', + }, + linkTo: ['timeline'], + }, + timeline: { + [CONSTANTS.timerange]: { + from: 1558048243696, + fromStr: 'now-24h', + kind: 'relative', + to: 1558134643697, + toStr: 'now', + }, + linkTo: ['global'], + }, }, - timeline: { - [CONSTANTS.timerange]: { - from: 1558048243696, - fromStr: 'now-24h', - kind: 'relative', - to: 1558134643697, - toStr: 'now', - }, - linkTo: ['global'], + [CONSTANTS.appQuery]: { query: '', language: 'kuery' }, + [CONSTANTS.filters]: [], + [CONSTANTS.timeline]: { + id: '', + isOpen: false, }, }, - [CONSTANTS.appQuery]: { query: '', language: 'kuery' }, - [CONSTANTS.filters]: [], - [CONSTANTS.timeline]: { - id: '', - isOpen: false, - }, }; const wrapper = mount(); test('it calls setBreadcrumbs with correct path on mount', () => { diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx index 7209be4d715f3..748187a01fc8e 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx @@ -14,54 +14,42 @@ import { useRouteSpy } from '../../utils/route/use_route_spy'; import { makeMapStateToProps } from '../url_state/helpers'; import { setBreadcrumbs } from './breadcrumbs'; import { TabNavigation } from './tab_navigation'; -import { TabNavigationProps } from './tab_navigation/types'; -import { SiemNavigationComponentProps } from './types'; +import { SiemNavigationProps, SiemNavigationComponentProps } from './types'; -export const SiemNavigationComponent = React.memo( - ({ - query, - detailName, - display, - filters, - navTabs, - pageName, - pathName, - savedQuery, - search, - tabName, - timeline, - timerange, - }) => { +export const SiemNavigationComponent = React.memo< + SiemNavigationComponentProps & SiemNavigationProps & RouteSpyState +>( + ({ detailName, display, navTabs, pageName, pathName, search, tabName, urlState }) => { useEffect(() => { if (pathName) { setBreadcrumbs({ - query, + query: urlState.query, detailName, - filters, + filters: urlState.filters, navTabs, pageName, pathName, - savedQuery, + savedQuery: urlState.savedQuery, search, tabName, - timerange, - timeline, + timerange: urlState.timerange, + timeline: urlState.timeline, }); } - }, [query, pathName, search, filters, navTabs, savedQuery, timerange, timeline]); + }, [pathName, search, navTabs, urlState]); return ( ); }, @@ -69,12 +57,8 @@ export const SiemNavigationComponent = React.memo + React.ComponentClass >(connect(makeMapStateToProps))(SiemNavigationComponent); -export const SiemNavigation = React.memo(props => { +export const SiemNavigation = React.memo(props => { const [routeProps] = useRouteSpy(); - const stateNavReduxProps: RouteSpyState & SiemNavigationComponentProps = { + const stateNavReduxProps: RouteSpyState & SiemNavigationProps = { ...routeProps, ...props, }; diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts index 856651e6f97dc..1283691e65806 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts @@ -10,9 +10,9 @@ import { Timeline } from '../../url_state/types'; import { HostsTableType } from '../../../store/hosts/model'; import { esFilters, Query } from '../../../../../../../../src/plugins/data/public'; -import { SiemNavigationComponentProps } from '../types'; +import { SiemNavigationProps } from '../types'; -export interface TabNavigationProps extends SiemNavigationComponentProps { +export interface TabNavigationProps extends SiemNavigationProps { pathName: string; pageName: string; tabName: HostsTableType | undefined; diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/types.ts b/x-pack/legacy/plugins/siem/public/components/navigation/types.ts index a8e16c82fbf80..845642256be8a 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/navigation/types.ts @@ -4,13 +4,30 @@ * you may not use this file except in compliance with the Elastic License. */ -import { UrlStateType } from '../url_state/constants'; +import { esFilters, Query } from '../../../../../../../src/plugins/data/public'; +import { HostsTableType } from '../../store/hosts/model'; +import { UrlInputsModel } from '../../store/inputs/model'; +import { CONSTANTS, UrlStateType } from '../url_state/constants'; +import { Timeline } from '../url_state/types'; -export interface SiemNavigationComponentProps { +export interface SiemNavigationProps { display?: 'default' | 'condensed'; navTabs: Record; } +export interface SiemNavigationComponentProps { + pathName: string; + pageName: string; + tabName: HostsTableType | undefined; + urlState: { + [CONSTANTS.appQuery]?: Query; + [CONSTANTS.filters]?: esFilters.Filter[]; + [CONSTANTS.savedQuery]?: string; + [CONSTANTS.timerange]: UrlInputsModel; + [CONSTANTS.timeline]: Timeline; + }; +} + export type SearchNavTab = NavTab | { urlKey: UrlStateType; isDetailPage: boolean }; export interface NavTab { diff --git a/x-pack/legacy/plugins/siem/public/mock/test_providers.tsx b/x-pack/legacy/plugins/siem/public/mock/test_providers.tsx index d4a7bb1f425d4..e573afaafc2d2 100644 --- a/x-pack/legacy/plugins/siem/public/mock/test_providers.tsx +++ b/x-pack/legacy/plugins/siem/public/mock/test_providers.tsx @@ -58,6 +58,11 @@ const services = { storage: { get: () => {}, }, + data: { + query: { + savedQueries: {}, + }, + }, }; const localStorageMock = () => { diff --git a/x-pack/legacy/plugins/siem/server/kibana.index.ts b/x-pack/legacy/plugins/siem/server/kibana.index.ts index 2f1530a777042..3d73b9f4d90b0 100644 --- a/x-pack/legacy/plugins/siem/server/kibana.index.ts +++ b/x-pack/legacy/plugins/siem/server/kibana.index.ts @@ -42,21 +42,15 @@ export const initServerWithKibana = ( const libs = compose(kbnServer, mode); initServer(libs); - if ( - kbnServer.config().has('xpack.actions.enabled') && - kbnServer.config().get('xpack.actions.enabled') === true && - kbnServer.config().has('xpack.alerting.enabled') && - kbnServer.config().has('xpack.alerting.enabled') === true - ) { - logger.info( - 'Detected feature flags for actions and alerting and enabling detection engine API endpoints' - ); - createRulesRoute(kbnServer); - readRulesRoute(kbnServer); - updateRulesRoute(kbnServer); - deleteRulesRoute(kbnServer); - findRulesRoute(kbnServer); - } + + // Signals/Alerting Rules routes for + // routes such as ${DETECTION_ENGINE_RULES_URL} + // that have the REST endpoints of /api/detection_engine/rules + createRulesRoute(kbnServer); + readRulesRoute(kbnServer); + updateRulesRoute(kbnServer); + deleteRulesRoute(kbnServer); + findRulesRoute(kbnServer); const xpackMainPlugin = kbnServer.plugins.xpack_main; xpackMainPlugin.registerFeature({ diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/README.md b/x-pack/legacy/plugins/siem/server/lib/detection_engine/README.md index 4b1dbf62d0dd4..75757bbaa0c1f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/README.md +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/README.md @@ -1,62 +1,20 @@ -Temporary README.md for users and developers working on the backend detection engine -for how to get started. +README.md for developers working on the backend detection engine on how to get started +using the CURL scripts in the scripts folder. -# Setup for Users +The scripts rely on CURL and jq: +* [CURL](https://curl.haxx.se) +* [jq](https://stedolan.github.io/jq/) -If you're just a user and want to enable the REST interfaces and UI screens do the following. -NOTE: this is very temporary and once alerting and actions is enabled by default you will no -longer have to do these steps - -Set the environment variable ALERTING_FEATURE_ENABLED to be true in your .profile or your windows -global environment variable. - -```sh -export ALERTING_FEATURE_ENABLED=true -``` - -In your `kibana.yml` file enable alerting and actions like so: - -```sh -# Feature flag to turn on alerting -xpack.alerting.enabled: true - -# Feature flag to turn on actions which goes with alerting -xpack.actions.enabled: true -``` - -Start Kibana and you will see these messages indicating detection engine is activated like so: - -```sh -server log [11:39:05.561] [info][siem] Detected feature flags for actions and alerting and enabling detection engine API endpoints -``` - -If you see crashes like this: - -```ts - FATAL Error: Unmet requirement "alerting" for plugin "siem" -``` - -It is because Kibana is not picking up your changes from `kibana.yml` and not seeing that alerting and actions is enabled. - -# For Developers - -See these two other pages for references: -https://github.com/elastic/kibana/blob/master/x-pack/legacy/plugins/alerting/README.md -https://github.com/elastic/kibana/tree/master/x-pack/legacy/plugins/actions - -Since there is no UI yet and a lot of backend areas that are not created, you -should install the kbn-action and kbn-alert project from here: -https://github.com/pmuellr/kbn-action - -The scripts rely on CURL and jq, ensure both of these are installed: +Install curl and jq ```sh brew update brew install curl brew install jq ``` -Open up your .zshrc/.bashrc and add these lines with the variables filled in: +Open `$HOME/.zshrc` or `${HOME}.bashrc` depending on your SHELL output from `echo $SHELL` +and add these environment variables: ```sh export ELASTICSEARCH_USERNAME=${user} @@ -66,52 +24,30 @@ export KIBANA_URL=http://localhost:5601 export SIGNALS_INDEX=.siem-signals-${your user id} export TASK_MANAGER_INDEX=.kibana-task-manager-${your user id} export KIBANA_INDEX=.kibana-${your user id} - -# This is for the kbn-action and kbn-alert tool -export KBN_URLBASE=http://${user}:${password}@localhost:5601 ``` -source your .zhsrc/.bashrc or open a new terminal to ensure you get the new values set. - -Optional env var when set to true will utilize `reindex` api for reindexing -instead of the scroll and bulk index combination. +source `$HOME/.zshrc` or `${HOME}.bashrc` to ensure variables are set: ```sh -export USE_REINDEX_API=true -``` - -Add these lines to your `kibana.dev.yml` to turn on the feature toggles of alerting and actions: - -```sh -# Feature flag to turn on alerting -xpack.alerting.enabled: true - -# Feature flag to turn on actions which goes with alerting -xpack.actions.enabled: true +source ~/.zshrc ``` Restart Kibana and ensure that you are using `--no-base-path` as changing the base path is a feature but will get in the way of the CURL scripts written as is. You should see alerting and actions starting up like so afterwards ```sh -server log [22:05:22.277] [info][status][plugin:alerting@8.0.0] Status changed from uninitialized to green - Ready -server log [22:05:22.270] [info][status][plugin:actions@8.0.0] Status changed from uninitialized to green - Ready -``` - -You should also see the SIEM detect the feature flags and start the API endpoints for detection engine - -```sh -server log [11:39:05.561] [info][siem] Detected feature flags for actions and alerting and enabling detection engine API endpoints +server log [22:05:22.277] [info][status][plugin:alerting@8.0.0] Status changed from uninitialized to green - Ready +server log [22:05:22.270] [info][status][plugin:actions@8.0.0] Status changed from uninitialized to green - Ready ``` Go into your SIEM Advanced settings and underneath the setting of `siem:defaultSignalsIndex`, set that to the same -value as you did with the environment variable of SIGNALS_INDEX, which should be `.siem-signals-${your user id}` +value as you did with the environment variable of `${SIGNALS_INDEX}`, which should be `.siem-signals-${your user id}` ``` .siem-signals-${your user id} ``` -Open a terminal and go into the scripts folder `cd kibana/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts` and run: +Go to the scripts folder `cd kibana/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts` and run: ```sh ./hard_reset.sh @@ -124,7 +60,7 @@ which will: - Delete any existing alerts you have - Delete any existing alert tasks you have - Delete any existing signal mapping you might have had. -- Add the latest signal index and its mappings using your settings from `SIGNALS_INDEX` environment variable. +- Add the latest signal index and its mappings using your settings from `${SIGNALS_INDEX}` environment variable. - Posts the sample rule from `rules/root_or_admin_1.json` by replacing its `output_index` with your `SIGNALS_INDEX` environment variable - The sample rule checks for root or admin every 5 minutes and reports that as a signal if it is a positive hit @@ -181,21 +117,20 @@ You should see the new rules created like so: Every 5 minutes if you get positive hits you will see messages on info like so: ```sh -server log [09:54:59.013] [info][plugins][siem] Total signals found from signal rule "id: a556065c-0656-4ba1-ad64-a77ca9d2013b", "ruleId: rule-1": 10000 +server log [09:54:59.013] [info][plugins][siem] Total signals found from signal rule "id: a556065c-0656-4ba1-ad64-a77ca9d2013b", "ruleId: rule-1": 10000 ``` -Rules are space aware and default to the "default" space for these scripts if you do not export -the variable of SPACE_URL. For example, if you want to post rules to the space `test-space` you would -set your SPACE_URL to be: +Rules are [space aware](https://www.elastic.co/guide/en/kibana/master/xpack-spaces.html) and default +to the "default" (empty) URL space if you do not export the variable of `SPACE_URL`. Example, if you want to +post rules to `test-space` you set `SPACE_URL` to be: ```sh export SPACE_URL=/s/test-space ``` -So that the scripts prepend a `/s/test-space` in front of all the APIs to correctly create, modify, delete, and update -them from within that space. - -See the scripts folder and the tools for more command line fun. +The `${SPACE_URL}` is in front of all the APIs to correctly create, modify, delete, and update +them from within the defined space. If this variable is not defined the default which is the url of an +empty string will be used. Add the `.siem-signals-${your user id}` to your advanced SIEM settings to see any signals created which should update once every 5 minutes at this point. @@ -216,3 +151,7 @@ logging.events: ops: __no-ops__, } ``` + +See these two README.md's pages for more references on the alerting and actions API: +https://github.com/elastic/kibana/blob/master/x-pack/legacy/plugins/alerting/README.md +https://github.com/elastic/kibana/tree/master/x-pack/legacy/plugins/actions diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/README.md b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/README.md deleted file mode 100644 index 8d617a8de3fcd..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/README.md +++ /dev/null @@ -1,39 +0,0 @@ -A set of scripts for developers to utilize command line functionality of Kibana/Elastic -search which is not available in the DEV console for the detection engine. - -Before beginning ensure in your .zshrc/.bashrc you have your user, password, and url set: - -Open up your .zshrc/.bashrc and add these lines with the variables filled in: - -``` -export ELASTICSEARCH_USERNAME=${user} -export ELASTICSEARCH_PASSWORD=${password} -export ELASTICSEARCH_URL=https://${ip}:9200 -export KIBANA_URL=http://localhost:5601 -export SIGNALS_INDEX=.siem-signals-${your user id} -export TASK_MANAGER_INDEX=.kibana-task-manager-${your user id} -export KIBANA_INDEX=.kibana-${your user id} - -# This is for the kbn-action and kbn-alert tool -export KBN_URLBASE=http://${user}:${password}@localhost:5601 -``` - -And that you have the latest version of [NodeJS](https://nodejs.org/en/), -[CURL](https://curl.haxx.se), and [jq](https://stedolan.github.io/jq/) installed. - -If you have homebrew you can install using brew like so - -``` -brew install jq -``` - -After that you can execute scripts within this folder by first ensuring -your current working directory is `./scripts` and then running any scripts within -that folder. - -Example to add a rule to the system - -``` -cd ./scripts -./post_rule.sh ./rules/root_or_admin_1.json -``` diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/check_env_variables.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/check_env_variables.sh index c534b33d28413..c2406dc7f6231 100755 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/check_env_variables.sh +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/check_env_variables.sh @@ -11,36 +11,36 @@ set -e if [ -z "${ELASTICSEARCH_USERNAME}" ]; then - echo "Set ELASTICSEARCH_USERNAME in your enviornment" + echo "Set ELASTICSEARCH_USERNAME in your environment" exit 1 fi if [ -z "${ELASTICSEARCH_PASSWORD}" ]; then - echo "Set ELASTICSEARCH_PASSWORD in your enviornment" + echo "Set ELASTICSEARCH_PASSWORD in your environment" exit 1 fi if [ -z "${ELASTICSEARCH_URL}" ]; then - echo "Set ELASTICSEARCH_URL in your enviornment" + echo "Set ELASTICSEARCH_URL in your environment" exit 1 fi if [ -z "${KIBANA_URL}" ]; then - echo "Set KIBANA_URL in your enviornment" + echo "Set KIBANA_URL in your environment" exit 1 fi if [ -z "${SIGNALS_INDEX}" ]; then - echo "Set SIGNALS_INDEX in your enviornment" + echo "Set SIGNALS_INDEX in your environment" exit 1 fi if [ -z "${TASK_MANAGER_INDEX}" ]; then - echo "Set TASK_MANAGER_INDEX in your enviornment" + echo "Set TASK_MANAGER_INDEX in your environment" exit 1 fi if [ -z "${KIBANA_INDEX}" ]; then - echo "Set KIBANA_INDEX in your enviornment" + echo "Set KIBANA_INDEX in your environment" exit 1 fi diff --git a/x-pack/legacy/plugins/task_manager/index.ts b/x-pack/legacy/plugins/task_manager/index.ts index 0487b003bc1ef..2d9183bdcd797 100644 --- a/x-pack/legacy/plugins/task_manager/index.ts +++ b/x-pack/legacy/plugins/task_manager/index.ts @@ -40,7 +40,8 @@ export function taskManager(kibana: any) { .default(3000), index: Joi.string() .description('The name of the index used to store task information.') - .default('.kibana_task_manager'), + .default('.kibana_task_manager') + .invalid(['.tasks']), max_workers: Joi.number() .description( 'The maximum number of tasks that this Kibana instance will run simultaneously.' diff --git a/x-pack/plugins/apm/kibana.json b/x-pack/plugins/apm/kibana.json index 8161f6ee06bf8..40fee07742cf6 100644 --- a/x-pack/plugins/apm/kibana.json +++ b/x-pack/plugins/apm/kibana.json @@ -3,12 +3,7 @@ "server": true, "version": "8.0.0", "kibanaVersion": "kibana", - "configPath": [ - "xpack", - "apm" - ], + "configPath": ["xpack", "apm"], "ui": false, - "requiredPlugins": [ - "apm_oss" - ] + "requiredPlugins": ["apm_oss", "data", "home"] } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 4fd99b2c888a3..3b7f3ded77290 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2492,8 +2492,8 @@ "kbn.context.olderDocumentsWarning": "アンカーよりも古いドキュメントは {docCount} 件しか見つかりませんでした。", "kbn.context.olderDocumentsWarningZero": "アンカーよりも古いドキュメントは見つかりませんでした。", "kbn.discover.fieldChooser.fieldFilterFacetButtonLabel": "フィルタリングされたフィールド", - "kbn.discover.fieldChooser.indexPattern.changeLinkLabel": "変更", "kbn.discover.fieldChooser.indexPattern.changeLinkTooltip": "現在のインデックスパターンを変更", + "kbn.discover.fieldChooser.indexPattern.changeLinkAriaLabel": "現在のインデックスパターンを変更", "kbn.discover.fieldChooser.searchPlaceHolder": "検索フィールド", "kbn.discover.histogram.partialData.bucketTooltipText": "選択された時間範囲にはこのバケット全体は含まれていませんが、一部データが含まれている可能性があります。", "kbn.doc.failedToLocateIndexPattern": "ID {indexPatternId} に一致するインデックスパターンがありません", @@ -6406,7 +6406,6 @@ "xpack.maps.esSearch.featureCountMsg": "{count} 件のドキュメントが見つかりました。", "xpack.maps.esSearch.resultsTrimmedMsg": "結果は初めの {count} 件のドキュメントに制限されています。", "xpack.maps.esSearch.topHitsEntitiesCountMsg": "{entityCount} 件のエントリーを発見.", - "xpack.maps.esSearch.topHitsResultsTrimmedMsg": "結果は各エンティティにつき最も最近の {topHitsSize} 件のドキュメントに制限されています。", "xpack.maps.feature.appDescription": "Elasticsearch と Elastic Maps Service の地理空間データを閲覧します", "xpack.maps.featureRegistry.mapsFeatureName": "Maps", "xpack.maps.geoGrid.resolutionLabel": "グリッド解像度", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 52b67473c1b91..017aa7d91d74b 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2493,8 +2493,8 @@ "kbn.context.olderDocumentsWarning": "仅可以找到 {docCount} 个比定位标记旧的文档。", "kbn.context.olderDocumentsWarningZero": "找不到比定位标记旧的文档。", "kbn.discover.fieldChooser.fieldFilterFacetButtonLabel": "已筛选字段", - "kbn.discover.fieldChooser.indexPattern.changeLinkLabel": "更改", "kbn.discover.fieldChooser.indexPattern.changeLinkTooltip": "更改当前索引模式", + "kbn.discover.fieldChooser.indexPattern.changeLinkAriaLabel": "更改当前索引模式", "kbn.discover.fieldChooser.searchPlaceHolder": "搜索字段", "kbn.discover.histogram.partialData.bucketTooltipText": "选定的时间范围不包括此整个存储桶,其可能包含部分数据。", "kbn.doc.failedToLocateIndexPattern": "无索引模式匹配 ID {indexPatternId}", @@ -6408,7 +6408,6 @@ "xpack.maps.esSearch.featureCountMsg": "找到 {count} 个文档。", "xpack.maps.esSearch.resultsTrimmedMsg": "结果仅限于前 {count} 个文档。", "xpack.maps.esSearch.topHitsEntitiesCountMsg": "找到 {entityCount} 个实体。", - "xpack.maps.esSearch.topHitsResultsTrimmedMsg": "结果仅限于每个实体的最近 {topHitsSize} 个文档。", "xpack.maps.feature.appDescription": "从 Elasticsearch 和 Elastic 地图服务浏览地理空间数据", "xpack.maps.featureRegistry.mapsFeatureName": "Maps", "xpack.maps.geoGrid.resolutionLabel": "网格分辨率", diff --git a/x-pack/test/functional/apps/snapshot_restore/index.ts b/x-pack/test/functional/apps/snapshot_restore/index.ts index f589555fe876d..f9bfafed37529 100644 --- a/x-pack/test/functional/apps/snapshot_restore/index.ts +++ b/x-pack/test/functional/apps/snapshot_restore/index.ts @@ -8,7 +8,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('Snapshots app', function() { - this.tags('ciGroup1'); + this.tags(['ciGroup4', 'skipCloud']); loadTestFile(require.resolve('./home_page')); }); };