diff --git a/.ci/es-snapshots/Jenkinsfile_build_es b/.ci/es-snapshots/Jenkinsfile_build_es index a3470cd750738d..aafdf06433c6d7 100644 --- a/.ci/es-snapshots/Jenkinsfile_build_es +++ b/.ci/es-snapshots/Jenkinsfile_build_es @@ -25,7 +25,7 @@ def PROMOTE_WITHOUT_VERIFY = !!params.PROMOTE_WITHOUT_VERIFICATION timeout(time: 120, unit: 'MINUTES') { timestamps { ansiColor('xterm') { - node(workers.label('s')) { + node(workers.label('l')) { catchErrors { def VERSION def SNAPSHOT_ID @@ -154,9 +154,10 @@ def buildArchives(destination) { "NODE_NAME=", ]) { sh """ - ./gradlew -p distribution/archives assemble --parallel + ./gradlew -Dbuild.docker=true assemble --parallel mkdir -p ${destination} - find distribution/archives -type f \\( -name 'elasticsearch-*-*-*-*.tar.gz' -o -name 'elasticsearch-*-*-*-*.zip' \\) -not -path *no-jdk* -exec cp {} ${destination} \\; + find distribution -type f \\( -name 'elasticsearch-*-*-*-*.tar.gz' -o -name 'elasticsearch-*-*-*-*.zip' \\) -not -path *no-jdk* -not -path *build-context* -exec cp {} ${destination} \\; + docker images "docker.elastic.co/elasticsearch/elasticsearch" --format "{{.Tag}}" | xargs -n1 bash -c 'docker save docker.elastic.co/elasticsearch/elasticsearch:\${0} | gzip > ${destination}/elasticsearch-\${0}-docker-image.tar.gz' """ } } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a7345f4b2897b2..a0aeed7a34949d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -436,7 +436,7 @@ We are still to develop a proper process to accept any contributed translations. When writing a new component, create a sibling SASS file of the same name and import directly into the JS/TS component file. Doing so ensures the styles are never separated or lost on import and allows for better modularization (smaller individual plugin asset footprint). -Any JavaScript (or TypeScript) file that imports SASS (.scss) files will automatically build with the [EUI](https://elastic.github.io/eui/#/guidelines/sass) & Kibana invisibles (SASS variables, mixins, functions) from the [`styling_constants.scss` file](https://github.com/elastic/kibana/blob/master/src/legacy/ui/public/styles/_styling_constants.scss). However, any Legacy (file path includes `/legacy`) files will not. +All SASS (.scss) files will automatically build with the [EUI](https://elastic.github.io/eui/#/guidelines/sass) & Kibana invisibles (SASS variables, mixins, functions) from the [`globals_[theme].scss` file](src/legacy/ui/public/styles/_globals_v7light.scss). **Example:** @@ -679,15 +679,15 @@ Part of this process only applies to maintainers, since it requires access to Gi Kibana publishes [Release Notes](https://www.elastic.co/guide/en/kibana/current/release-notes.html) for major and minor releases. The Release Notes summarize what the PRs accomplish in language that is meaningful to users. To generate the Release Notes, the team runs a script against this repo to collect the merged PRs against the release. #### Create the Release Notes text -The text that appears in the Release Notes is pulled directly from your PR title, or a single paragraph of text that you specify in the PR description. +The text that appears in the Release Notes is pulled directly from your PR title, or a single paragraph of text that you specify in the PR description. To use a single paragraph of text, enter `Release note:` or a `## Release note` header in the PR description, followed by your text. For example, refer to this [PR](https://github.com/elastic/kibana/pull/65796) that uses the `## Release note` header. When you create the Release Notes text, use the following best practices: -* Use present tense. +* Use present tense. * Use sentence case. * When you create a feature PR, start with `Adds`. -* When you create an enhancement PR, start with `Improves`. +* When you create an enhancement PR, start with `Improves`. * When you create a bug fix PR, start with `Fixes`. * When you create a deprecation PR, start with `Deprecates`. diff --git a/packages/kbn-optimizer/README.md b/packages/kbn-optimizer/README.md index 9ff0f563442749..5d5c5e3b6eb748 100644 --- a/packages/kbn-optimizer/README.md +++ b/packages/kbn-optimizer/README.md @@ -42,6 +42,26 @@ When a directory is listed in the "extraPublicDirs" it will always be included i Any import in a bundle which resolves into another bundles "context" directory, ie `src/plugins/*`, must map explicitly to a "public dir" exported by that plugin. If the resolved import is not in the list of public dirs an error will be thrown and the optimizer will fail to build that bundle until the error is fixed. +## Themes + +SASS imports in bundles are automatically converted to CSS for one or more themes. In development we build the `v7light` and `v7dark` themes by default to improve build performance. When producing distributable bundles the default shifts to `*` so that the distributable bundles will include all themes, preventing the bundles from needing to be rebuilt when users change the active theme in Kibana's advanced settings. + +To customize the themes that are built for development you can specify the `KBN_OPTIMIZER_THEMES` environment variable to one or more theme tags, or use `*` to build styles for all themes. Unfortunately building more than one theme significantly impacts build performance, so try to be strategic about which themes you build. + +Currently supported theme tags: `v7light`, `v7dark`, `v8light`, `v8dark` + +Examples: +```sh +# start Kibana with only a single theme +KBN_OPTIMIZER_THEMES=v7light yarn start + +# start Kibana with dark themes for version 7 and 8 +KBN_OPTIMIZER_THEMES=v7dark,v8dark yarn start + +# start Kibana with all the themes +KBN_OPTIMIZER_THEMES=* yarn start +``` + ## API To run the optimizer from code, you can import the [`OptimizerConfig`][OptimizerConfig] class and [`runOptimizer`][Optimizer] function. Create an [`OptimizerConfig`][OptimizerConfig] instance by calling it's static `create()` method with some options, then pass it to the [`runOptimizer`][Optimizer] function. `runOptimizer()` returns an observable of update objects, which are summaries of the optimizer state plus an optional `event` property which describes the internal events occuring and may be of use. You can use the [`logOptimizerState()`][LogOptimizerState] helper to write the relevant bits of state to a tooling log or checkout it's implementation to see how the internal events like [`WorkerStdio`][ObserveWorker] and [`WorkerStarted`][ObserveWorker] are used. diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/src/legacy/ui/public/styles/_styling_constants.scss b/packages/kbn-optimizer/src/__fixtures__/mock_repo/src/legacy/ui/public/styles/_globals_v7dark.scss similarity index 100% rename from packages/kbn-optimizer/src/__fixtures__/mock_repo/src/legacy/ui/public/styles/_styling_constants.scss rename to packages/kbn-optimizer/src/__fixtures__/mock_repo/src/legacy/ui/public/styles/_globals_v7dark.scss diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/src/legacy/ui/public/styles/_globals_v7light.scss b/packages/kbn-optimizer/src/__fixtures__/mock_repo/src/legacy/ui/public/styles/_globals_v7light.scss new file mode 100644 index 00000000000000..63beb9927b9f57 --- /dev/null +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/src/legacy/ui/public/styles/_globals_v7light.scss @@ -0,0 +1 @@ +$globalStyleConstant: 11; diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/src/legacy/ui/public/styles/_globals_v8dark.scss b/packages/kbn-optimizer/src/__fixtures__/mock_repo/src/legacy/ui/public/styles/_globals_v8dark.scss new file mode 100644 index 00000000000000..4040cab1878fc6 --- /dev/null +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/src/legacy/ui/public/styles/_globals_v8dark.scss @@ -0,0 +1 @@ +$globalStyleConstant: 12; diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/src/legacy/ui/public/styles/_globals_v8light.scss b/packages/kbn-optimizer/src/__fixtures__/mock_repo/src/legacy/ui/public/styles/_globals_v8light.scss new file mode 100644 index 00000000000000..3918413c068639 --- /dev/null +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/src/legacy/ui/public/styles/_globals_v8light.scss @@ -0,0 +1 @@ +$globalStyleConstant: 13; diff --git a/packages/kbn-optimizer/src/common/index.ts b/packages/kbn-optimizer/src/common/index.ts index 7d021a5ee78475..89cde2c1cd064f 100644 --- a/packages/kbn-optimizer/src/common/index.ts +++ b/packages/kbn-optimizer/src/common/index.ts @@ -29,3 +29,4 @@ export * from './array_helpers'; export * from './event_stream_helpers'; export * from './disallowed_syntax_plugin'; export * from './parse_path'; +export * from './theme_tags'; diff --git a/packages/kbn-optimizer/src/common/theme_tags.test.ts b/packages/kbn-optimizer/src/common/theme_tags.test.ts new file mode 100644 index 00000000000000..019a9b7bdee3e6 --- /dev/null +++ b/packages/kbn-optimizer/src/common/theme_tags.test.ts @@ -0,0 +1,92 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { parseThemeTags } from './theme_tags'; + +it('returns default tags when passed undefined', () => { + expect(parseThemeTags()).toMatchInlineSnapshot(` + Array [ + "v7dark", + "v7light", + ] + `); +}); + +it('returns all tags when passed *', () => { + expect(parseThemeTags('*')).toMatchInlineSnapshot(` + Array [ + "v7dark", + "v7light", + "v8dark", + "v8light", + ] + `); +}); + +it('returns specific tag when passed a single value', () => { + expect(parseThemeTags('v8light')).toMatchInlineSnapshot(` + Array [ + "v8light", + ] + `); +}); + +it('returns specific tags when passed a comma separated list', () => { + expect(parseThemeTags('v8light, v7dark,v7light')).toMatchInlineSnapshot(` + Array [ + "v7dark", + "v7light", + "v8light", + ] + `); +}); + +it('returns specific tags when passed an array', () => { + expect(parseThemeTags(['v8light', 'v7light'])).toMatchInlineSnapshot(` + Array [ + "v7light", + "v8light", + ] + `); +}); + +it('throws when an invalid tag is in the array', () => { + expect(() => parseThemeTags(['v8light', 'v7light', 'bar'])).toThrowErrorMatchingInlineSnapshot( + `"Invalid theme tags [bar], options: [v7dark, v7light, v8dark, v8light]"` + ); +}); + +it('throws when an invalid tags in comma separated list', () => { + expect(() => parseThemeTags('v8light ,v7light,bar,box ')).toThrowErrorMatchingInlineSnapshot( + `"Invalid theme tags [bar, box], options: [v7dark, v7light, v8dark, v8light]"` + ); +}); + +it('returns tags in alphabetical order', () => { + const tags = parseThemeTags(['v7light', 'v8light']); + expect(tags).toEqual(tags.slice().sort((a, b) => a.localeCompare(b))); +}); + +it('returns an immutable array', () => { + expect(() => { + const tags = parseThemeTags('v8light'); + // @ts-expect-error + tags.push('foo'); + }).toThrowErrorMatchingInlineSnapshot(`"Cannot add property 1, object is not extensible"`); +}); diff --git a/packages/kbn-optimizer/src/common/theme_tags.ts b/packages/kbn-optimizer/src/common/theme_tags.ts new file mode 100644 index 00000000000000..27b5e12b807a87 --- /dev/null +++ b/packages/kbn-optimizer/src/common/theme_tags.ts @@ -0,0 +1,65 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ascending } from './array_helpers'; + +const tags = (...themeTags: string[]) => + Object.freeze(themeTags.sort(ascending((tag) => tag)) as ThemeTag[]); + +const validTag = (tag: any): tag is ThemeTag => ALL_THEMES.includes(tag); +const isArrayOfStrings = (input: unknown): input is string[] => + Array.isArray(input) && input.every((v) => typeof v === 'string'); + +export type ThemeTags = readonly ThemeTag[]; +export type ThemeTag = 'v7light' | 'v7dark' | 'v8light' | 'v8dark'; +export const DEFAULT_THEMES = tags('v7light', 'v7dark'); +export const ALL_THEMES = tags('v7light', 'v7dark', 'v8light', 'v8dark'); + +export function parseThemeTags(input?: any): ThemeTags { + if (!input) { + return DEFAULT_THEMES; + } + + if (input === '*') { + return ALL_THEMES; + } + + if (typeof input === 'string') { + input = input.split(',').map((tag) => tag.trim()); + } + + if (!isArrayOfStrings(input)) { + throw new Error(`Invalid theme tags, must be an array of strings`); + } + + if (!input.length) { + throw new Error( + `Invalid theme tags, you must specify at least one of [${ALL_THEMES.join(', ')}]` + ); + } + + const invalidTags = input.filter((t) => !validTag(t)); + if (invalidTags.length) { + throw new Error( + `Invalid theme tags [${invalidTags.join(', ')}], options: [${ALL_THEMES.join(', ')}]` + ); + } + + return tags(...input); +} diff --git a/packages/kbn-optimizer/src/common/worker_config.ts b/packages/kbn-optimizer/src/common/worker_config.ts index a1ab51ee97c231..8726b3452ff1e1 100644 --- a/packages/kbn-optimizer/src/common/worker_config.ts +++ b/packages/kbn-optimizer/src/common/worker_config.ts @@ -20,11 +20,13 @@ import Path from 'path'; import { UnknownVals } from './ts_helpers'; +import { ThemeTags, parseThemeTags } from './theme_tags'; export interface WorkerConfig { readonly repoRoot: string; readonly watch: boolean; readonly dist: boolean; + readonly themeTags: ThemeTags; readonly cache: boolean; readonly profileWebpack: boolean; readonly browserslistEnv: string; @@ -80,6 +82,8 @@ export function parseWorkerConfig(json: string): WorkerConfig { throw new Error('`browserslistEnv` must be a string'); } + const themes = parseThemeTags(parsed.themeTags); + return { repoRoot, cache, @@ -88,6 +92,7 @@ export function parseWorkerConfig(json: string): WorkerConfig { profileWebpack, optimizerCacheKey, browserslistEnv, + themeTags: themes, }; } catch (error) { throw new Error(`unable to parse worker config: ${error.message}`); diff --git a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap index b6b0973f0d5398..211cfac3806ad7 100644 --- a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap +++ b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap @@ -58,11 +58,15 @@ OptimizerConfig { ], "profileWebpack": false, "repoRoot": /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo, + "themeTags": Array [ + "v7dark", + "v7light", + ], "watch": false, } `; -exports[`prepares assets for distribution: bar bundle 1`] = `"(function(modules){var installedModules={};function __webpack_require__(moduleId){if(installedModules[moduleId]){return installedModules[moduleId].exports}var module=installedModules[moduleId]={i:moduleId,l:false,exports:{}};modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);module.l=true;return module.exports}__webpack_require__.m=modules;__webpack_require__.c=installedModules;__webpack_require__.d=function(exports,name,getter){if(!__webpack_require__.o(exports,name)){Object.defineProperty(exports,name,{enumerable:true,get:getter})}};__webpack_require__.r=function(exports){if(typeof Symbol!==\\"undefined\\"&&Symbol.toStringTag){Object.defineProperty(exports,Symbol.toStringTag,{value:\\"Module\\"})}Object.defineProperty(exports,\\"__esModule\\",{value:true})};__webpack_require__.t=function(value,mode){if(mode&1)value=__webpack_require__(value);if(mode&8)return value;if(mode&4&&typeof value===\\"object\\"&&value&&value.__esModule)return value;var ns=Object.create(null);__webpack_require__.r(ns);Object.defineProperty(ns,\\"default\\",{enumerable:true,value:value});if(mode&2&&typeof value!=\\"string\\")for(var key in value)__webpack_require__.d(ns,key,function(key){return value[key]}.bind(null,key));return ns};__webpack_require__.n=function(module){var getter=module&&module.__esModule?function getDefault(){return module[\\"default\\"]}:function getModuleExports(){return module};__webpack_require__.d(getter,\\"a\\",getter);return getter};__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)};__webpack_require__.p=\\"\\";return __webpack_require__(__webpack_require__.s=5)})([function(module,exports,__webpack_require__){\\"use strict\\";var isOldIE=function isOldIE(){var memo;return function memorize(){if(typeof memo===\\"undefined\\"){memo=Boolean(window&&document&&document.all&&!window.atob)}return memo}}();var getTarget=function getTarget(){var memo={};return function memorize(target){if(typeof memo[target]===\\"undefined\\"){var styleTarget=document.querySelector(target);if(window.HTMLIFrameElement&&styleTarget instanceof window.HTMLIFrameElement){try{styleTarget=styleTarget.contentDocument.head}catch(e){styleTarget=null}}memo[target]=styleTarget}return memo[target]}}();var stylesInDom=[];function getIndexByIdentifier(identifier){var result=-1;for(var i=0;i { tap((state) => { if (state.event?.type === 'worker stdio') { // eslint-disable-next-line no-console - console.log('worker', state.event.stream, state.event.chunk.toString('utf8')); + console.log('worker', state.event.stream, state.event.line); } }), toArray() @@ -226,7 +226,7 @@ const expectFileMatchesSnapshotWithCompression = (filePath: string, snapshotLabe // Verify the brotli variant matches expect( - // @ts-ignore @types/node is missing the brotli functions + // @ts-expect-error @types/node is missing the brotli functions Zlib.brotliDecompressSync( Fs.readFileSync(Path.resolve(MOCK_REPO_DIR, `${filePath}.br`)) ).toString() diff --git a/packages/kbn-optimizer/src/log_optimizer_state.ts b/packages/kbn-optimizer/src/log_optimizer_state.ts index cbec159bd27a02..23767be610da4f 100644 --- a/packages/kbn-optimizer/src/log_optimizer_state.ts +++ b/packages/kbn-optimizer/src/log_optimizer_state.ts @@ -24,7 +24,7 @@ import { tap } from 'rxjs/operators'; import { OptimizerConfig } from './optimizer'; import { OptimizerUpdate$ } from './run_optimizer'; -import { CompilerMsg, pipeClosure } from './common'; +import { CompilerMsg, pipeClosure, ALL_THEMES } from './common'; export function logOptimizerState(log: ToolingLog, config: OptimizerConfig) { return pipeClosure((update$: OptimizerUpdate$) => { @@ -37,12 +37,7 @@ export function logOptimizerState(log: ToolingLog, config: OptimizerConfig) { const { event, state } = update; if (event?.type === 'worker stdio') { - const chunk = event.chunk.toString('utf8'); - log.warning( - `worker`, - event.stream, - chunk.slice(0, chunk.length - (chunk.endsWith('\n') ? 1 : 0)) - ); + log.warning(`worker`, event.stream, event.line); } if (event?.type === 'bundle not cached') { @@ -76,6 +71,11 @@ export function logOptimizerState(log: ToolingLog, config: OptimizerConfig) { if (!loggedInit) { loggedInit = true; log.info(`initialized, ${state.offlineBundles.length} bundles cached`); + if (config.themeTags.length !== ALL_THEMES.length) { + log.warning( + `only building [${config.themeTags}] themes, customize with the KBN_OPTIMIZER_THEMES environment variable` + ); + } } return; } diff --git a/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts b/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts index 9d7f1709506f9f..47d01347a8f7d1 100644 --- a/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts +++ b/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts @@ -103,6 +103,10 @@ describe('getOptimizerCacheKey()', () => { "dist": false, "optimizerCacheKey": "♻", "repoRoot": , + "themeTags": Array [ + "v7dark", + "v7light", + ], }, } `); diff --git a/packages/kbn-optimizer/src/optimizer/observe_stdio.test.ts b/packages/kbn-optimizer/src/optimizer/observe_stdio.test.ts new file mode 100644 index 00000000000000..9bf8f9db1fe450 --- /dev/null +++ b/packages/kbn-optimizer/src/optimizer/observe_stdio.test.ts @@ -0,0 +1,49 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Readable } from 'stream'; + +import { toArray } from 'rxjs/operators'; + +import { observeStdio$ } from './observe_stdio'; + +it('notifies on every line, uncluding partial content at the end without a newline', async () => { + const chunks = [`foo\nba`, `r\nb`, `az`]; + + await expect( + observeStdio$( + new Readable({ + read() { + this.push(chunks.shift()!); + if (!chunks.length) { + this.push(null); + } + }, + }) + ) + .pipe(toArray()) + .toPromise() + ).resolves.toMatchInlineSnapshot(` + Array [ + "foo", + "bar", + "baz", + ] + `); +}); diff --git a/packages/kbn-optimizer/src/optimizer/observe_stdio.ts b/packages/kbn-optimizer/src/optimizer/observe_stdio.ts new file mode 100644 index 00000000000000..e8daecef8e0dd9 --- /dev/null +++ b/packages/kbn-optimizer/src/optimizer/observe_stdio.ts @@ -0,0 +1,76 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Readable } from 'stream'; +import * as Rx from 'rxjs'; + +// match newline characters followed either by a non-space character or another newline +const NEWLINE = /\r?\n/; + +/** + * Observe a readable stdio stream and emit the entire lines + * of text produced, completing once the stdio stream emits "end" + * and erroring if it emits "error". + */ +export function observeStdio$(stream: Readable) { + return new Rx.Observable((subscriber) => { + let buffer = ''; + + subscriber.add( + Rx.fromEvent(stream, 'data').subscribe({ + next(chunk) { + buffer += chunk.toString('utf8'); + + while (true) { + const match = NEWLINE.exec(buffer); + if (!match) { + break; + } + + const multilineChunk = buffer.slice(0, match.index); + buffer = buffer.slice(match.index + match[0].length); + subscriber.next(multilineChunk); + } + }, + }) + ); + + const flush = () => { + while (buffer.length && !subscriber.closed) { + const line = buffer; + buffer = ''; + subscriber.next(line); + } + }; + + subscriber.add( + Rx.fromEvent(stream, 'end').subscribe(() => { + flush(); + subscriber.complete(); + }) + ); + + subscriber.add( + Rx.fromEvent(stream, 'error').subscribe((error) => { + flush(); + subscriber.error(error); + }) + ); + }); +} diff --git a/packages/kbn-optimizer/src/optimizer/observe_worker.ts b/packages/kbn-optimizer/src/optimizer/observe_worker.ts index fef3efc13a516a..31b34bd5c59385 100644 --- a/packages/kbn-optimizer/src/optimizer/observe_worker.ts +++ b/packages/kbn-optimizer/src/optimizer/observe_worker.ts @@ -17,7 +17,6 @@ * under the License. */ -import { Readable } from 'stream'; import { inspect } from 'util'; import execa from 'execa'; @@ -26,12 +25,13 @@ import { map, takeUntil, first, ignoreElements } from 'rxjs/operators'; import { isWorkerMsg, WorkerConfig, WorkerMsg, Bundle, BundleRefs } from '../common'; +import { observeStdio$ } from './observe_stdio'; import { OptimizerConfig } from './optimizer_config'; export interface WorkerStdio { type: 'worker stdio'; stream: 'stdout' | 'stderr'; - chunk: Buffer; + line: string; } export interface WorkerStarted { @@ -99,28 +99,6 @@ function usingWorkerProc( ); } -function observeStdio$(stream: Readable, name: WorkerStdio['stream']) { - return Rx.fromEvent(stream, 'data').pipe( - takeUntil( - Rx.race( - Rx.fromEvent(stream, 'end'), - Rx.fromEvent(stream, 'error').pipe( - map((error) => { - throw error; - }) - ) - ) - ), - map( - (chunk): WorkerStdio => ({ - type: 'worker stdio', - chunk, - stream: name, - }) - ) - ); -} - /** * We used to pass configuration to the worker as JSON encoded arguments, but they * grew too large for argv, especially on Windows, so we had to move to an async init @@ -186,8 +164,24 @@ export function observeWorker( type: 'worker started', bundles, }), - observeStdio$(proc.stdout, 'stdout'), - observeStdio$(proc.stderr, 'stderr'), + observeStdio$(proc.stdout).pipe( + map( + (line): WorkerStdio => ({ + type: 'worker stdio', + line, + stream: 'stdout', + }) + ) + ), + observeStdio$(proc.stderr).pipe( + map( + (line): WorkerStdio => ({ + type: 'worker stdio', + line, + stream: 'stderr', + }) + ) + ), Rx.fromEvent<[unknown]>(proc, 'message') .pipe( // validate the messages from the process diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts index d4152133f289de..5b46d67479fd58 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts @@ -20,6 +20,7 @@ jest.mock('./assign_bundles_to_workers.ts'); jest.mock('./kibana_platform_plugins.ts'); jest.mock('./get_plugin_bundles.ts'); +jest.mock('../common/theme_tags.ts'); import Path from 'path'; import Os from 'os'; @@ -27,6 +28,7 @@ import Os from 'os'; import { REPO_ROOT, createAbsolutePathSerializer } from '@kbn/dev-utils'; import { OptimizerConfig } from './optimizer_config'; +import { parseThemeTags } from '../common'; jest.spyOn(Os, 'cpus').mockReturnValue(['foo'] as any); @@ -35,6 +37,7 @@ expect.addSnapshotSerializer(createAbsolutePathSerializer()); beforeEach(() => { delete process.env.KBN_OPTIMIZER_MAX_WORKERS; delete process.env.KBN_OPTIMIZER_NO_CACHE; + delete process.env.KBN_OPTIMIZER_THEMES; jest.clearAllMocks(); }); @@ -81,6 +84,26 @@ describe('OptimizerConfig::parseOptions()', () => { }).toThrowErrorMatchingInlineSnapshot(`"worker count must be a number"`); }); + it('defaults to * theme when dist = true', () => { + OptimizerConfig.parseOptions({ + repoRoot: REPO_ROOT, + dist: true, + }); + + expect(parseThemeTags).toBeCalledWith('*'); + }); + + it('defaults to KBN_OPTIMIZER_THEMES when dist = false', () => { + process.env.KBN_OPTIMIZER_THEMES = 'foo'; + + OptimizerConfig.parseOptions({ + repoRoot: REPO_ROOT, + dist: false, + }); + + expect(parseThemeTags).toBeCalledWith('foo'); + }); + it('applies defaults', () => { expect( OptimizerConfig.parseOptions({ @@ -102,6 +125,7 @@ describe('OptimizerConfig::parseOptions()', () => { ], "profileWebpack": false, "repoRoot": , + "themeTags": undefined, "watch": false, } `); @@ -127,6 +151,7 @@ describe('OptimizerConfig::parseOptions()', () => { ], "profileWebpack": false, "repoRoot": , + "themeTags": undefined, "watch": false, } `); @@ -154,6 +179,7 @@ describe('OptimizerConfig::parseOptions()', () => { ], "profileWebpack": false, "repoRoot": , + "themeTags": undefined, "watch": false, } `); @@ -178,6 +204,7 @@ describe('OptimizerConfig::parseOptions()', () => { ], "profileWebpack": false, "repoRoot": , + "themeTags": undefined, "watch": false, } `); @@ -201,6 +228,7 @@ describe('OptimizerConfig::parseOptions()', () => { ], "profileWebpack": false, "repoRoot": , + "themeTags": undefined, "watch": false, } `); @@ -222,6 +250,7 @@ describe('OptimizerConfig::parseOptions()', () => { "pluginScanDirs": Array [], "profileWebpack": false, "repoRoot": , + "themeTags": undefined, "watch": false, } `); @@ -243,6 +272,7 @@ describe('OptimizerConfig::parseOptions()', () => { "pluginScanDirs": Array [], "profileWebpack": false, "repoRoot": , + "themeTags": undefined, "watch": false, } `); @@ -264,6 +294,7 @@ describe('OptimizerConfig::parseOptions()', () => { "pluginScanDirs": Array [], "profileWebpack": false, "repoRoot": , + "themeTags": undefined, "watch": false, } `); @@ -286,6 +317,7 @@ describe('OptimizerConfig::parseOptions()', () => { "pluginScanDirs": Array [], "profileWebpack": false, "repoRoot": , + "themeTags": undefined, "watch": false, } `); @@ -308,6 +340,7 @@ describe('OptimizerConfig::parseOptions()', () => { "pluginScanDirs": Array [], "profileWebpack": false, "repoRoot": , + "themeTags": undefined, "watch": false, } `); @@ -346,6 +379,7 @@ describe('OptimizerConfig::create()', () => { pluginScanDirs: Symbol('parsed plugin scan dirs'), repoRoot: Symbol('parsed repo root'), watch: Symbol('parsed watch'), + themeTags: Symbol('theme tags'), inspectWorkers: Symbol('parsed inspect workers'), profileWebpack: Symbol('parsed profile webpack'), })); @@ -369,6 +403,7 @@ describe('OptimizerConfig::create()', () => { "plugins": Symbol(new platform plugins), "profileWebpack": Symbol(parsed profile webpack), "repoRoot": Symbol(parsed repo root), + "themeTags": Symbol(theme tags), "watch": Symbol(parsed watch), } `); @@ -385,7 +420,7 @@ describe('OptimizerConfig::create()', () => { [Window], ], "invocationCallOrder": Array [ - 7, + 21, ], "results": Array [ Object { @@ -408,7 +443,7 @@ describe('OptimizerConfig::create()', () => { [Window], ], "invocationCallOrder": Array [ - 8, + 22, ], "results": Array [ Object { diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts index c9e9b3ad01ccc6..7757004139d0d4 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts @@ -20,7 +20,14 @@ import Path from 'path'; import Os from 'os'; -import { Bundle, WorkerConfig, CacheableWorkerConfig } from '../common'; +import { + Bundle, + WorkerConfig, + CacheableWorkerConfig, + ThemeTag, + ThemeTags, + parseThemeTags, +} from '../common'; import { findKibanaPlatformPlugins, KibanaPlatformPlugin } from './kibana_platform_plugins'; import { getPluginBundles } from './get_plugin_bundles'; @@ -73,6 +80,18 @@ interface Options { /** flag that causes the core bundle to be built along with plugins */ includeCoreBundle?: boolean; + + /** + * style themes that sass files will be converted to, the correct style will be + * loaded in the browser automatically by checking the global `__kbnThemeTag__`. + * Specifying additional styles increases build time. + * + * Defaults: + * - "*" when building the dist + * - comma separated list of themes in the `KBN_OPTIMIZER_THEMES` env var + * - "k7light" + */ + themes?: ThemeTag | '*' | ThemeTag[]; } interface ParsedOptions { @@ -86,6 +105,7 @@ interface ParsedOptions { pluginScanDirs: string[]; inspectWorkers: boolean; includeCoreBundle: boolean; + themeTags: ThemeTags; } export class OptimizerConfig { @@ -139,6 +159,10 @@ export class OptimizerConfig { throw new TypeError('worker count must be a number'); } + const themeTags = parseThemeTags( + options.themes || (dist ? '*' : process.env.KBN_OPTIMIZER_THEMES) + ); + return { watch, dist, @@ -150,6 +174,7 @@ export class OptimizerConfig { pluginPaths, inspectWorkers, includeCoreBundle, + themeTags, }; } @@ -181,7 +206,8 @@ export class OptimizerConfig { options.repoRoot, options.maxWorkerCount, options.dist, - options.profileWebpack + options.profileWebpack, + options.themeTags ); } @@ -194,7 +220,8 @@ export class OptimizerConfig { public readonly repoRoot: string, public readonly maxWorkerCount: number, public readonly dist: boolean, - public readonly profileWebpack: boolean + public readonly profileWebpack: boolean, + public readonly themeTags: ThemeTags ) {} getWorkerConfig(optimizerCacheKey: unknown): WorkerConfig { @@ -205,6 +232,7 @@ export class OptimizerConfig { repoRoot: this.repoRoot, watch: this.watch, optimizerCacheKey, + themeTags: this.themeTags, browserslistEnv: this.dist ? 'production' : process.env.BROWSERSLIST_ENV || 'dev', }; } diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_state.ts b/packages/kbn-optimizer/src/optimizer/optimizer_state.ts index 1572f459e6ee5f..09f8ca10c61818 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_state.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_state.ts @@ -127,7 +127,7 @@ export function createOptimizerStateSummarizer( } if (event.type === 'worker stdio' || event.type === 'worker started') { - // same state, but updated to the event is shared externally + // same state, but updated so the event is shared externally return createOptimizerState(state); } diff --git a/packages/kbn-optimizer/src/worker/run_compilers.ts b/packages/kbn-optimizer/src/worker/run_compilers.ts index de5e9372e9e7ac..ca7673748bde91 100644 --- a/packages/kbn-optimizer/src/worker/run_compilers.ts +++ b/packages/kbn-optimizer/src/worker/run_compilers.ts @@ -77,7 +77,7 @@ const observeCompiler = ( */ const complete$ = Rx.fromEventPattern((cb) => done.tap(PLUGIN_NAME, cb)).pipe( maybeMap((stats) => { - // @ts-ignore not included in types, but it is real https://github.com/webpack/webpack/blob/ab4fa8ddb3f433d286653cd6af7e3aad51168649/lib/Watching.js#L58 + // @ts-expect-error not included in types, but it is real https://github.com/webpack/webpack/blob/ab4fa8ddb3f433d286653cd6af7e3aad51168649/lib/Watching.js#L58 if (stats.compilation.needAdditionalPass) { return undefined; } diff --git a/packages/kbn-optimizer/src/worker/theme_loader.ts b/packages/kbn-optimizer/src/worker/theme_loader.ts index 5d02462ef1bb87..f2f685bde65d96 100644 --- a/packages/kbn-optimizer/src/worker/theme_loader.ts +++ b/packages/kbn-optimizer/src/worker/theme_loader.ts @@ -17,16 +17,43 @@ * under the License. */ +import { stringifyRequest, getOptions } from 'loader-utils'; import webpack from 'webpack'; -import { stringifyRequest } from 'loader-utils'; +import { parseThemeTags, ALL_THEMES, ThemeTag } from '../common'; + +const getVersion = (tag: ThemeTag) => (tag.includes('v7') ? 7 : 8); +const getIsDark = (tag: ThemeTag) => tag.includes('dark'); +const compare = (a: ThemeTag, b: ThemeTag) => + (getVersion(a) === getVersion(b) ? 1 : 0) + (getIsDark(a) === getIsDark(b) ? 1 : 0); // eslint-disable-next-line import/no-default-export export default function (this: webpack.loader.LoaderContext) { + this.cacheable(true); + + const options = getOptions(this); + const bundleId: string = options.bundleId!; + const themeTags = parseThemeTags(options.themeTags); + + const cases = ALL_THEMES.map((tag) => { + if (themeTags.includes(tag)) { + return ` + case '${tag}': + return require(${stringifyRequest(this, `${this.resourcePath}?${tag}`)});`; + } + + const fallback = themeTags + .slice() + .sort((a, b) => compare(b, tag) - compare(a, tag)) + .shift()!; + + const message = `SASS files in [${bundleId}] were not built for theme [${tag}]. Styles were compiled using the [${fallback}] theme instead to keep Kibana somewhat usable. Please adjust the advanced settings to make use of [${themeTags}] or make sure the KBN_OPTIMIZER_THEMES environment variable includes [${tag}] in a comma separated list of themes you want to compile. You can also set it to "*" to build all themes.`; + return ` + case '${tag}': + console.error(new Error(${JSON.stringify(message)})); + return require(${stringifyRequest(this, `${this.resourcePath}?${fallback}`)})`; + }).join('\n'); + return ` -if (window.__kbnDarkMode__) { - require(${stringifyRequest(this, `${this.resourcePath}?dark`)}) -} else { - require(${stringifyRequest(this, `${this.resourcePath}?light`)}); -} - `; +switch (window.__kbnThemeTag__) {${cases} +}`; } diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index 11f5544cd9274d..aaea70d12c60d6 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -21,11 +21,10 @@ import Path from 'path'; import { stringifyRequest } from 'loader-utils'; import webpack from 'webpack'; -// @ts-ignore +// @ts-expect-error import TerserPlugin from 'terser-webpack-plugin'; -// @ts-ignore +// @ts-expect-error import webpackMerge from 'webpack-merge'; -// @ts-ignore import { CleanWebpackPlugin } from 'clean-webpack-plugin'; import CompressionPlugin from 'compression-webpack-plugin'; import * as UiSharedDeps from '@kbn/ui-shared-deps'; @@ -134,8 +133,8 @@ export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker: test: /\.scss$/, exclude: /node_modules/, oneOf: [ - { - resourceQuery: /dark|light/, + ...worker.themeTags.map((theme) => ({ + resourceQuery: `?${theme}`, use: [ { loader: 'style-loader', @@ -196,34 +195,27 @@ export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker: loaderContext, Path.resolve( worker.repoRoot, - 'src/legacy/ui/public/styles/_styling_constants.scss' + `src/legacy/ui/public/styles/_globals_${theme}.scss` ) )};\n`; }, webpackImporter: false, implementation: require('node-sass'), - sassOptions(loaderContext: webpack.loader.LoaderContext) { - const darkMode = loaderContext.resourceQuery === '?dark'; - - return { - outputStyle: 'nested', - includePaths: [Path.resolve(worker.repoRoot, 'node_modules')], - sourceMapRoot: `/${bundle.type}:${bundle.id}`, - importer: (url: string) => { - if (darkMode && url.includes('eui_colors_light')) { - return { file: url.replace('eui_colors_light', 'eui_colors_dark') }; - } - - return { file: url }; - }, - }; + sassOptions: { + outputStyle: 'nested', + includePaths: [Path.resolve(worker.repoRoot, 'node_modules')], + sourceMapRoot: `/${bundle.type}:${bundle.id}`, }, }, }, ], - }, + })), { loader: require.resolve('./theme_loader'), + options: { + bundleId: bundle.id, + themeTags: worker.themeTags, + }, }, ], }, diff --git a/packages/kbn-storybook/storybook_config/webpack.config.js b/packages/kbn-storybook/storybook_config/webpack.config.js index caeffaabea62be..b2df4f40d4fbe9 100644 --- a/packages/kbn-storybook/storybook_config/webpack.config.js +++ b/packages/kbn-storybook/storybook_config/webpack.config.js @@ -122,7 +122,7 @@ module.exports = async ({ config }) => { prependData(loaderContext) { return `@import ${stringifyRequest( loaderContext, - resolve(REPO_ROOT, 'src/legacy/ui/public/styles/_styling_constants.scss') + resolve(REPO_ROOT, 'src/legacy/ui/public/styles/_globals_v7light.scss') )};\n`; }, sassOptions: { diff --git a/packages/kbn-ui-shared-deps/entry.js b/packages/kbn-ui-shared-deps/entry.js index 02b64157686c19..0f981f3d07610f 100644 --- a/packages/kbn-ui-shared-deps/entry.js +++ b/packages/kbn-ui-shared-deps/entry.js @@ -51,15 +51,6 @@ export const ElasticEui = require('@elastic/eui'); export const ElasticEuiLibServices = require('@elastic/eui/lib/services'); export const ElasticEuiLibServicesFormat = require('@elastic/eui/lib/services/format'); export const ElasticEuiChartsTheme = require('@elastic/eui/dist/eui_charts_theme'); -export let ElasticEuiLightTheme; -export let ElasticEuiDarkTheme; -if (window.__kbnThemeVersion__ === 'v7') { - ElasticEuiLightTheme = require('@elastic/eui/dist/eui_theme_light.json'); - ElasticEuiDarkTheme = require('@elastic/eui/dist/eui_theme_dark.json'); -} else { - ElasticEuiLightTheme = require('@elastic/eui/dist/eui_theme_amsterdam_light.json'); - ElasticEuiDarkTheme = require('@elastic/eui/dist/eui_theme_amsterdam_dark.json'); -} import * as Theme from './theme.ts'; export { Theme }; diff --git a/packages/kbn-ui-shared-deps/theme.ts b/packages/kbn-ui-shared-deps/theme.ts index ca4714779d39e1..4b2758516fc260 100644 --- a/packages/kbn-ui-shared-deps/theme.ts +++ b/packages/kbn-ui-shared-deps/theme.ts @@ -23,9 +23,15 @@ const globals: any = typeof window === 'undefined' ? {} : window; export type Theme = typeof LightTheme; +// in the Kibana app we can rely on this global being defined, but in +// some cases (like jest, or karma tests) the global is undefined +export const tag: string = globals.__kbnThemeTag__ || 'v7light'; +export const version = tag.startsWith('v7') ? 7 : 8; +export const darkMode = tag.endsWith('dark'); + export let euiLightVars: Theme; export let euiDarkVars: Theme; -if (globals.__kbnThemeVersion__ === 'v7') { +if (version === 7) { euiLightVars = require('@elastic/eui/dist/eui_theme_light.json'); euiDarkVars = require('@elastic/eui/dist/eui_theme_dark.json'); } else { @@ -37,7 +43,7 @@ if (globals.__kbnThemeVersion__ === 'v7') { * EUI Theme vars that automatically adjust to light/dark theme */ export let euiThemeVars: Theme; -if (globals.__kbnDarkTheme__) { +if (darkMode) { euiThemeVars = euiDarkVars; } else { euiThemeVars = euiLightVars; diff --git a/src/core/public/index.scss b/src/core/public/index.scss index 4be46899cff67b..87825350b4e98f 100644 --- a/src/core/public/index.scss +++ b/src/core/public/index.scss @@ -1,7 +1,3 @@ -// This file is built by both the legacy and KP build systems so we need to -// import this explicitly -@import '../../legacy/ui/public/styles/_styling_constants'; - @import './core'; @import './chrome/index'; @import './overlays/index'; diff --git a/src/legacy/core_plugins/kibana/public/index.scss b/src/legacy/core_plugins/kibana/public/index.scss index e9810a747c8c78..7de0c8fc15f943 100644 --- a/src/legacy/core_plugins/kibana/public/index.scss +++ b/src/legacy/core_plugins/kibana/public/index.scss @@ -1,5 +1,3 @@ -@import 'src/legacy/ui/public/styles/styling_constants'; - // Elastic charts @import '@elastic/charts/dist/theme'; @import '@elastic/eui/src/themes/charts/theme'; diff --git a/src/legacy/core_plugins/tests_bundle/public/index.scss b/src/legacy/core_plugins/tests_bundle/public/index.scss index 8020cef8d84927..d8dbf8d6dc8856 100644 --- a/src/legacy/core_plugins/tests_bundle/public/index.scss +++ b/src/legacy/core_plugins/tests_bundle/public/index.scss @@ -1,5 +1,3 @@ -@import 'src/legacy/ui/public/styles/styling_constants'; - // This file pulls some styles of NP plugins into the legacy test stylesheet // so they are available for karma browser tests. @import '../../../../plugins/vis_type_vislib/public/index'; diff --git a/src/legacy/core_plugins/timelion/public/index.scss b/src/legacy/core_plugins/timelion/public/index.scss index ebf000d160b546..cf2a7859a505d8 100644 --- a/src/legacy/core_plugins/timelion/public/index.scss +++ b/src/legacy/core_plugins/timelion/public/index.scss @@ -1,6 +1,3 @@ -// Should import both the EUI constants and any Kibana ones that are considered global -@import 'src/legacy/ui/public/styles/styling_constants'; - /* Timelion plugin styles */ // Prefix all styles with "tim" to avoid conflicts. diff --git a/src/legacy/server/sass/__fixtures__/index.scss b/src/legacy/server/sass/__fixtures__/index.scss index 019941534cadd3..ed2657ed3f6ee5 100644 --- a/src/legacy/server/sass/__fixtures__/index.scss +++ b/src/legacy/server/sass/__fixtures__/index.scss @@ -1,5 +1,3 @@ -@import 'src/legacy/ui/public/styles/styling_constants'; - foo { bar { display: flex; diff --git a/src/legacy/server/sass/build.js b/src/legacy/server/sass/build.js index 2c0a2d84be2c00..536a6dc581db6c 100644 --- a/src/legacy/server/sass/build.js +++ b/src/legacy/server/sass/build.js @@ -29,19 +29,15 @@ import isPathInside from 'is-path-inside'; import { PUBLIC_PATH_PLACEHOLDER } from '../../../optimize/public_path_placeholder'; const renderSass = promisify(sass.render); +const readFile = promisify(fs.readFile); const writeFile = promisify(fs.writeFile); const access = promisify(fs.access); const copyFile = promisify(fs.copyFile); const mkdirAsync = promisify(fs.mkdir); const UI_ASSETS_DIR = resolve(__dirname, '../../../core/server/core_app/assets'); -const DARK_THEME_IMPORTER = (url) => { - if (url.includes('eui_colors_light')) { - return { file: url.replace('eui_colors_light', 'eui_colors_dark') }; - } - - return { file: url }; -}; +const LIGHT_GLOBALS_PATH = resolve(__dirname, '../../../legacy/ui/public/styles/_globals_v7light'); +const DARK_GLOBALS_PATH = resolve(__dirname, '../../../legacy/ui/public/styles/_globals_v7dark'); const makeAsset = (request, { path, root, boundry, copyRoot, urlRoot }) => { const relativePath = relative(root, path); @@ -84,10 +80,16 @@ export class Build { */ async build() { + const scss = await readFile(this.sourcePath); + const relativeGlobalsPath = + this.theme === 'dark' + ? relative(this.sourceDir, DARK_GLOBALS_PATH) + : relative(this.sourceDir, LIGHT_GLOBALS_PATH); + const rendered = await renderSass({ file: this.sourcePath, + data: `@import '${relativeGlobalsPath}';\n${scss}`, outFile: this.targetPath, - importer: this.theme === 'dark' ? DARK_THEME_IMPORTER : undefined, sourceMap: true, outputStyle: 'nested', sourceMapEmbed: true, diff --git a/src/legacy/ui/public/styles/_globals_v7dark.scss b/src/legacy/ui/public/styles/_globals_v7dark.scss new file mode 100644 index 00000000000000..d5a8535f327188 --- /dev/null +++ b/src/legacy/ui/public/styles/_globals_v7dark.scss @@ -0,0 +1,12 @@ +// v7dark global scope +// +// prepended to all .scss imports (from JS, when v7dark theme selected) and +// legacy uiExports.styleSheetPaths when any dark theme is selected + +@import '@elastic/eui/src/themes/eui/eui_colors_dark'; + +@import '@elastic/eui/src/global_styling/functions/index'; +@import '@elastic/eui/src/global_styling/variables/index'; +@import '@elastic/eui/src/global_styling/mixins/index'; + +@import './mixins'; diff --git a/src/legacy/ui/public/styles/_styling_constants.scss b/src/legacy/ui/public/styles/_globals_v7light.scss similarity index 59% rename from src/legacy/ui/public/styles/_styling_constants.scss rename to src/legacy/ui/public/styles/_globals_v7light.scss index 74fc54b4102856..522b346b64900b 100644 --- a/src/legacy/ui/public/styles/_styling_constants.scss +++ b/src/legacy/ui/public/styles/_globals_v7light.scss @@ -1,9 +1,10 @@ -// EUI global scope +// v7light global scope +// +// prepended to all .scss imports (from JS, when v7light theme selected) and +// legacy uiExports.styleSheetPaths when any dark theme is selected @import '@elastic/eui/src/themes/eui/eui_colors_light'; -// Note that fonts are loaded directly by src/legacy/ui/ui_render/views/chrome.pug - @import '@elastic/eui/src/global_styling/functions/index'; @import '@elastic/eui/src/global_styling/variables/index'; @import '@elastic/eui/src/global_styling/mixins/index'; diff --git a/src/legacy/ui/public/styles/_globals_v8dark.scss b/src/legacy/ui/public/styles/_globals_v8dark.scss new file mode 100644 index 00000000000000..972365e9e9d0ec --- /dev/null +++ b/src/legacy/ui/public/styles/_globals_v8dark.scss @@ -0,0 +1,16 @@ +// v8dark global scope +// +// prepended to all .scss imports (from JS, when v8dark theme selected) + +@import '@elastic/eui/src/themes/eui-amsterdam/eui_amsterdam_colors_dark'; + +@import '@elastic/eui/src/global_styling/functions/index'; +@import '@elastic/eui/src/themes/eui-amsterdam/global_styling/functions/index'; + +@import '@elastic/eui/src/global_styling/variables/index'; +@import '@elastic/eui/src/themes/eui-amsterdam/global_styling/variables/index'; + +@import '@elastic/eui/src/global_styling/mixins/index'; +@import '@elastic/eui/src/themes/eui-amsterdam/global_styling/mixins/index'; + +@import './mixins'; diff --git a/src/legacy/ui/public/styles/_globals_v8light.scss b/src/legacy/ui/public/styles/_globals_v8light.scss new file mode 100644 index 00000000000000..dc99f4d45082ed --- /dev/null +++ b/src/legacy/ui/public/styles/_globals_v8light.scss @@ -0,0 +1,16 @@ +// v8light global scope +// +// prepended to all .scss imports (from JS, when v8light theme selected) + +@import '@elastic/eui/src/themes/eui-amsterdam/eui_amsterdam_colors_light'; + +@import '@elastic/eui/src/global_styling/functions/index'; +@import '@elastic/eui/src/themes/eui-amsterdam/global_styling/functions/index'; + +@import '@elastic/eui/src/global_styling/variables/index'; +@import '@elastic/eui/src/themes/eui-amsterdam/global_styling/variables/index'; + +@import '@elastic/eui/src/global_styling/mixins/index'; +@import '@elastic/eui/src/themes/eui-amsterdam/global_styling/mixins/index'; + +@import './mixins'; diff --git a/src/legacy/ui/ui_render/bootstrap/template.js.hbs b/src/legacy/ui/ui_render/bootstrap/template.js.hbs index ca2e944489a734..bbca051ce31a1d 100644 --- a/src/legacy/ui/ui_render/bootstrap/template.js.hbs +++ b/src/legacy/ui/ui_render/bootstrap/template.js.hbs @@ -1,7 +1,6 @@ var kbnCsp = JSON.parse(document.querySelector('kbn-csp').getAttribute('data')); window.__kbnStrictCsp__ = kbnCsp.strictCsp; -window.__kbnDarkMode__ = {{darkMode}}; -window.__kbnThemeVersion__ = "{{themeVersion}}"; +window.__kbnThemeTag__ = "{{themeTag}}"; window.__kbnPublicPath__ = {{publicPathMap}}; window.__kbnBundles__ = {{kbnBundlesLoaderSource}} diff --git a/src/legacy/ui/ui_render/ui_render_mixin.js b/src/legacy/ui/ui_render/ui_render_mixin.js index 0cfcb91aa94efe..b4b18e086e809f 100644 --- a/src/legacy/ui/ui_render/ui_render_mixin.js +++ b/src/legacy/ui/ui_render/ui_render_mixin.js @@ -89,6 +89,7 @@ export function uiRenderMixin(kbnServer, server, config) { const isCore = !app; const uiSettings = request.getUiSettingsService(); + const darkMode = !authEnabled || request.auth.isAuthenticated ? await uiSettings.get('theme:darkMode') @@ -99,6 +100,8 @@ export function uiRenderMixin(kbnServer, server, config) { ? await uiSettings.get('theme:version') : 'v7'; + const themeTag = `${themeVersion === 'v7' ? 'v7' : 'v8'}${darkMode ? 'dark' : 'light'}`; + const buildHash = server.newPlatform.env.packageInfo.buildNum; const basePath = config.get('server.basePath'); @@ -178,8 +181,7 @@ export function uiRenderMixin(kbnServer, server, config) { const bootstrap = new AppBootstrap({ templateData: { - darkMode, - themeVersion, + themeTag, jsDependencyPaths, styleSheetPaths, publicPathMap, diff --git a/src/plugins/data/public/ui/saved_query_management/saved_query_management_component.tsx b/src/plugins/data/public/ui/saved_query_management/saved_query_management_component.tsx index 87f53e1c72079e..8582f4a12fa388 100644 --- a/src/plugins/data/public/ui/saved_query_management/saved_query_management_component.tsx +++ b/src/plugins/data/public/ui/saved_query_management/saved_query_management_component.tsx @@ -33,7 +33,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useEffect, useState, Fragment, useRef } from 'react'; +import React, { useCallback, useEffect, useState, Fragment, useRef } from 'react'; import { sortBy } from 'lodash'; import { SavedQuery, SavedQueryService } from '../..'; import { SavedQueryListItem } from './saved_query_list_item'; @@ -88,9 +88,51 @@ export function SavedQueryManagementComponent({ } }, [isOpen, activePage, savedQueryService]); - const goToPage = (pageNumber: number) => { - setActivePage(pageNumber); - }; + const handleTogglePopover = useCallback(() => setIsOpen((currentState) => !currentState), [ + setIsOpen, + ]); + + const handleClosePopover = useCallback(() => setIsOpen(false), []); + + const handleSave = useCallback(() => { + handleClosePopover(); + onSave(); + }, [handleClosePopover, onSave]); + + const handleSaveAsNew = useCallback(() => { + handleClosePopover(); + onSaveAsNew(); + }, [handleClosePopover, onSaveAsNew]); + + const handleSelect = useCallback( + (savedQueryToSelect) => { + handleClosePopover(); + onLoad(savedQueryToSelect); + }, + [handleClosePopover, onLoad] + ); + + const handleDelete = useCallback( + (savedQueryToDelete: SavedQuery) => { + const onDeleteSavedQuery = async (savedQuery: SavedQuery) => { + cancelPendingListingRequest.current(); + setSavedQueries( + savedQueries.filter((currentSavedQuery) => currentSavedQuery.id !== savedQuery.id) + ); + + if (loadedSavedQuery && loadedSavedQuery.id === savedQuery.id) { + onClearSavedQuery(); + } + + await savedQueryService.deleteSavedQuery(savedQuery.id); + setActivePage(0); + }; + + onDeleteSavedQuery(savedQueryToDelete); + handleClosePopover(); + }, + [handleClosePopover, loadedSavedQuery, onClearSavedQuery, savedQueries, savedQueryService] + ); const savedQueryDescriptionText = i18n.translate( 'data.search.searchBar.savedQueryDescriptionText', @@ -113,25 +155,13 @@ export function SavedQueryManagementComponent({ } ); - const onDeleteSavedQuery = async (savedQuery: SavedQuery) => { - cancelPendingListingRequest.current(); - setSavedQueries( - savedQueries.filter((currentSavedQuery) => currentSavedQuery.id !== savedQuery.id) - ); - - if (loadedSavedQuery && loadedSavedQuery.id === savedQuery.id) { - onClearSavedQuery(); - } - - await savedQueryService.deleteSavedQuery(savedQuery.id); - setActivePage(0); + const goToPage = (pageNumber: number) => { + setActivePage(pageNumber); }; const savedQueryPopoverButton = ( { - setIsOpen(!isOpen); - }} + onClick={handleTogglePopover} aria-label={i18n.translate('data.search.searchBar.savedQueryPopoverButtonText', { defaultMessage: 'See saved queries', })} @@ -159,11 +189,8 @@ export function SavedQueryManagementComponent({ key={savedQuery.id} savedQuery={savedQuery} isSelected={!!loadedSavedQuery && loadedSavedQuery.id === savedQuery.id} - onSelect={(savedQueryToSelect) => { - onLoad(savedQueryToSelect); - setIsOpen(false); - }} - onDelete={(savedQueryToDelete) => onDeleteSavedQuery(savedQueryToDelete)} + onSelect={handleSelect} + onDelete={handleDelete} showWriteOperations={!!showSaveQuery} /> )); @@ -175,9 +202,7 @@ export function SavedQueryManagementComponent({ id="savedQueryPopover" button={savedQueryPopoverButton} isOpen={isOpen} - closePopover={() => { - setIsOpen(false); - }} + closePopover={handleClosePopover} anchorPosition="downLeft" panelPaddingSize="none" buffer={-8} @@ -235,7 +260,7 @@ export function SavedQueryManagementComponent({ onSave()} + onClick={handleSave} aria-label={i18n.translate( 'data.search.searchBar.savedQueryPopoverSaveChangesButtonAriaLabel', { @@ -256,7 +281,7 @@ export function SavedQueryManagementComponent({ onSaveAsNew()} + onClick={handleSaveAsNew} aria-label={i18n.translate( 'data.search.searchBar.savedQueryPopoverSaveAsNewButtonAriaLabel', { @@ -280,7 +305,7 @@ export function SavedQueryManagementComponent({ onSave()} + onClick={handleSave} aria-label={i18n.translate( 'data.search.searchBar.savedQueryPopoverSaveButtonAriaLabel', { defaultMessage: 'Save a new saved query' } @@ -299,7 +324,7 @@ export function SavedQueryManagementComponent({ onClearSavedQuery()} + onClick={onClearSavedQuery} aria-label={i18n.translate( 'data.search.searchBar.savedQueryPopoverClearButtonAriaLabel', { defaultMessage: 'Clear current saved query' } diff --git a/src/plugins/tile_map/public/index.scss b/src/plugins/tile_map/public/index.scss index 4ce500b2da4d22..f4b86b0c31190b 100644 --- a/src/plugins/tile_map/public/index.scss +++ b/src/plugins/tile_map/public/index.scss @@ -1,5 +1,3 @@ -@import 'src/legacy/ui/public/styles/styling_constants'; - // Prefix all styles with "tlm" to avoid conflicts. // Examples // tlmChart diff --git a/test/scripts/jenkins_visual_regression.sh b/test/scripts/jenkins_visual_regression.sh index a32782deec65b9..17345d4301882b 100755 --- a/test/scripts/jenkins_visual_regression.sh +++ b/test/scripts/jenkins_visual_regression.sh @@ -11,7 +11,7 @@ mkdir -p "$installDir" tar -xzf "$linuxBuild" -C "$installDir" --strip=1 echo " -> running visual regression tests from kibana directory" -yarn percy exec -t 500 -- -- \ +yarn percy exec -t 10000 -- -- \ node scripts/functional_tests \ --debug --bail \ --kibana-install-dir "$installDir" \ diff --git a/test/scripts/jenkins_xpack_visual_regression.sh b/test/scripts/jenkins_xpack_visual_regression.sh index b67c1c9060a6e2..36bf3409a5421e 100755 --- a/test/scripts/jenkins_xpack_visual_regression.sh +++ b/test/scripts/jenkins_xpack_visual_regression.sh @@ -13,7 +13,7 @@ tar -xzf "$linuxBuild" -C "$installDir" --strip=1 echo " -> running visual regression tests from x-pack directory" cd "$XPACK_DIR" -yarn percy exec -t 500 -- -- \ +yarn percy exec -t 10000 -- -- \ node scripts/functional_tests \ --debug --bail \ --kibana-install-dir "$installDir" \ diff --git a/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/index.test.tsx b/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/index.test.tsx index 9f72ac6d5916e8..447e11eab5e412 100644 --- a/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/index.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/index.test.tsx @@ -58,28 +58,29 @@ describe.skip('useFetcher', () => { expect(hook.result.current).toEqual(true); }); - it('is true for minimum 1000ms', () => { - hook = renderHook((isLoading) => useDelayedVisibility(isLoading), { - initialProps: false, - }); + // Disabled because it's flaky: https://github.com/elastic/kibana/issues/66389 + // it('is true for minimum 1000ms', () => { + // hook = renderHook((isLoading) => useDelayedVisibility(isLoading), { + // initialProps: false, + // }); - hook.rerender(true); + // hook.rerender(true); - act(() => { - jest.advanceTimersByTime(100); - }); + // act(() => { + // jest.advanceTimersByTime(100); + // }); - hook.rerender(false); - act(() => { - jest.advanceTimersByTime(900); - }); + // hook.rerender(false); + // act(() => { + // jest.advanceTimersByTime(900); + // }); - expect(hook.result.current).toEqual(true); + // expect(hook.result.current).toEqual(true); - act(() => { - jest.advanceTimersByTime(100); - }); + // act(() => { + // jest.advanceTimersByTime(100); + // }); - expect(hook.result.current).toEqual(false); - }); + // expect(hook.result.current).toEqual(false); + // }); }); diff --git a/x-pack/plugins/canvas/.storybook/webpack.config.js b/x-pack/plugins/canvas/.storybook/webpack.config.js index 45a5303d8b0db1..3148a6742f76a4 100644 --- a/x-pack/plugins/canvas/.storybook/webpack.config.js +++ b/x-pack/plugins/canvas/.storybook/webpack.config.js @@ -80,7 +80,7 @@ module.exports = async ({ config }) => { prependData(loaderContext) { return `@import ${stringifyRequest( loaderContext, - path.resolve(KIBANA_ROOT, 'src/legacy/ui/public/styles/_styling_constants.scss') + path.resolve(KIBANA_ROOT, 'src/legacy/ui/public/styles/_globals_v7light.scss') )};\n`; }, sassOptions: { @@ -199,7 +199,6 @@ module.exports = async ({ config }) => { config.resolve.alias['ui/url/absolute_to_parsed_url'] = path.resolve(__dirname, '../tasks/mocks/uiAbsoluteToParsedUrl'); config.resolve.alias['ui/chrome'] = path.resolve(__dirname, '../tasks/mocks/uiChrome'); config.resolve.alias.ui = path.resolve(KIBANA_ROOT, 'src/legacy/ui/public'); - config.resolve.alias['src/legacy/ui/public/styles/styling_constants'] = path.resolve(KIBANA_ROOT, 'src/legacy/ui/public/styles/_styling_constants.scss'); config.resolve.alias.ng_mock$ = path.resolve(KIBANA_ROOT, 'src/test_utils/public/ng_mock'); return config; diff --git a/x-pack/plugins/canvas/public/style/index.scss b/x-pack/plugins/canvas/public/style/index.scss index 78a34a58f5f782..9cd2bdabd3f451 100644 --- a/x-pack/plugins/canvas/public/style/index.scss +++ b/x-pack/plugins/canvas/public/style/index.scss @@ -1,5 +1,3 @@ -@import 'src/legacy/ui/public/styles/styling_constants'; - // Canvas core @import 'hackery'; @import 'main'; diff --git a/x-pack/plugins/canvas/shareable_runtime/webpack.config.js b/x-pack/plugins/canvas/shareable_runtime/webpack.config.js index 66b0a7bc558cb6..1a5a21985ba72d 100644 --- a/x-pack/plugins/canvas/shareable_runtime/webpack.config.js +++ b/x-pack/plugins/canvas/shareable_runtime/webpack.config.js @@ -188,7 +188,7 @@ module.exports = { prependData(loaderContext) { return `@import ${stringifyRequest( loaderContext, - path.resolve(KIBANA_ROOT, 'src/legacy/ui/public/styles/_styling_constants.scss') + path.resolve(KIBANA_ROOT, 'src/legacy/ui/public/styles/_globals_v7light.scss') )};\n`; }, webpackImporter: false, diff --git a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_list/component_template_list.tsx b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_list/component_template_list.tsx index 05a5ed462d8f72..f9e6234e1415c0 100644 --- a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_list/component_template_list.tsx +++ b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_list/component_template_list.tsx @@ -43,10 +43,6 @@ export const ComponentTemplateList: React.FunctionComponent = ({ trackMetric('loaded', UIM_COMPONENT_TEMPLATE_LIST_LOAD); }, [trackMetric]); - if (data && data.length === 0) { - return ; - } - let content: React.ReactNode; if (isLoading) { @@ -67,6 +63,8 @@ export const ComponentTemplateList: React.FunctionComponent = ({ history={history as ScopedHistory} /> ); + } else if (data && data.length === 0) { + content = ; } else if (error) { content = ; } diff --git a/x-pack/plugins/index_management/public/index.scss b/x-pack/plugins/index_management/public/index.scss index 0fbf8ea5036c59..02686c4f7d6f34 100644 --- a/x-pack/plugins/index_management/public/index.scss +++ b/x-pack/plugins/index_management/public/index.scss @@ -1,6 +1,3 @@ -// Import the EUI global scope so we can use EUI constants -@import 'src/legacy/ui/public/styles/_styling_constants'; - // Index management plugin styles // Prefix all styles with "ind" to avoid conflicts. diff --git a/x-pack/plugins/infra/public/__snapshots__/metrics_overview_fetchers.test.ts.snap b/x-pack/plugins/infra/public/__snapshots__/metrics_overview_fetchers.test.ts.snap new file mode 100644 index 00000000000000..99ab129fc36e3f --- /dev/null +++ b/x-pack/plugins/infra/public/__snapshots__/metrics_overview_fetchers.test.ts.snap @@ -0,0 +1,215 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Metrics UI Observability Homepage Functions createMetricsFetchData() should just work 1`] = ` +Object { + "appLink": "/app/metrics", + "series": Object { + "inboundTraffic": Object { + "coordinates": Array [ + Object { + "x": 1593630455000, + "y": 0, + }, + Object { + "x": 1593630755000, + "y": 3.5, + }, + Object { + "x": 1593631055000, + "y": 3.5, + }, + Object { + "x": 1593631355000, + "y": 8.5, + }, + Object { + "x": 1593631655000, + "y": 3.5, + }, + Object { + "x": 1593631955000, + "y": 2.5, + }, + Object { + "x": 1593632255000, + "y": 1.5, + }, + Object { + "x": 1593632555000, + "y": 1.5, + }, + Object { + "x": 1593632855000, + "y": 3.5, + }, + Object { + "x": 1593633155000, + "y": 2.5, + }, + Object { + "x": 1593633455000, + "y": 1.5, + }, + Object { + "x": 1593633755000, + "y": 1.5, + }, + Object { + "x": 1593634055000, + "y": 2.5, + }, + Object { + "x": 1593634355000, + "y": 0, + }, + Object { + "x": 1593634655000, + "y": 10.5, + }, + Object { + "x": 1593634955000, + "y": 5.5, + }, + Object { + "x": 1593635255000, + "y": 13.5, + }, + Object { + "x": 1593635555000, + "y": 9.5, + }, + Object { + "x": 1593635855000, + "y": 7.5, + }, + Object { + "x": 1593636155000, + "y": 3, + }, + Object { + "x": 1593636455000, + "y": 3.5, + }, + ], + "label": "Inbound traffic", + }, + "outboundTraffic": Object { + "coordinates": Array [ + Object { + "x": 1593630455000, + "y": 0, + }, + Object { + "x": 1593630755000, + "y": 4, + }, + Object { + "x": 1593631055000, + "y": 4, + }, + Object { + "x": 1593631355000, + "y": 9, + }, + Object { + "x": 1593631655000, + "y": 4, + }, + Object { + "x": 1593631955000, + "y": 2.5, + }, + Object { + "x": 1593632255000, + "y": 2, + }, + Object { + "x": 1593632555000, + "y": 2, + }, + Object { + "x": 1593632855000, + "y": 4, + }, + Object { + "x": 1593633155000, + "y": 3, + }, + Object { + "x": 1593633455000, + "y": 2, + }, + Object { + "x": 1593633755000, + "y": 2, + }, + Object { + "x": 1593634055000, + "y": 2.5, + }, + Object { + "x": 1593634355000, + "y": 1, + }, + Object { + "x": 1593634655000, + "y": 11, + }, + Object { + "x": 1593634955000, + "y": 6, + }, + Object { + "x": 1593635255000, + "y": 14, + }, + Object { + "x": 1593635555000, + "y": 10, + }, + Object { + "x": 1593635855000, + "y": 8, + }, + Object { + "x": 1593636155000, + "y": 3, + }, + Object { + "x": 1593636455000, + "y": 4, + }, + ], + "label": "Outbound traffic", + }, + }, + "stats": Object { + "cpu": Object { + "label": "CPU usage", + "type": "percent", + "value": 0.0015, + }, + "hosts": Object { + "label": "Hosts", + "type": "number", + "value": 2, + }, + "inboundTraffic": Object { + "label": "Inbound traffic", + "type": "bytesPerSecond", + "value": 3.5, + }, + "memory": Object { + "label": "Memory usage", + "type": "percent", + "value": 0.0015, + }, + "outboundTraffic": Object { + "label": "Outbound traffic", + "type": "bytesPerSecond", + "value": 3, + }, + }, + "title": "Metrics", +} +`; diff --git a/x-pack/plugins/infra/public/index.scss b/x-pack/plugins/infra/public/index.scss index 05e045c1bd53be..a3d74e3afebe39 100644 --- a/x-pack/plugins/infra/public/index.scss +++ b/x-pack/plugins/infra/public/index.scss @@ -1,6 +1,3 @@ -// Import the EUI global scope so we can use EUI constants -@import 'src/legacy/ui/public/styles/_styling_constants'; - /* Infra plugin styles */ .infra-container-element { diff --git a/x-pack/plugins/infra/public/metrics_overview_fetchers.test.ts b/x-pack/plugins/infra/public/metrics_overview_fetchers.test.ts new file mode 100644 index 00000000000000..21946c7c5653ab --- /dev/null +++ b/x-pack/plugins/infra/public/metrics_overview_fetchers.test.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { coreMock } from 'src/core/public/mocks'; +import { createMetricsHasData, createMetricsFetchData } from './metrics_overview_fetchers'; +import { CoreStart } from 'kibana/public'; +import { InfraClientStartDeps, InfraClientStartExports } from './types'; +import moment from 'moment'; +import { FAKE_SNAPSHOT_RESPONSE } from './test_utils'; + +function setup() { + const core = coreMock.createStart(); + const mockedGetStartServices = jest.fn(() => { + const deps = {}; + return Promise.resolve([ + core as CoreStart, + deps as InfraClientStartDeps, + void 0 as InfraClientStartExports, + ]) as Promise<[CoreStart, InfraClientStartDeps, InfraClientStartExports]>; + }); + return { core, mockedGetStartServices }; +} + +describe('Metrics UI Observability Homepage Functions', () => { + describe('createMetricsHasData()', () => { + it('should return true when true', async () => { + const { core, mockedGetStartServices } = setup(); + core.http.get.mockResolvedValue({ + status: { + indexFields: [], + logIndicesExist: false, + metricIndicesExist: true, + }, + }); + const hasData = createMetricsHasData(mockedGetStartServices); + const response = await hasData(); + expect(core.http.get).toHaveBeenCalledTimes(1); + expect(response).toBeTruthy(); + }); + it('should return false when false', async () => { + const { core, mockedGetStartServices } = setup(); + core.http.get.mockResolvedValue({ + status: { + indexFields: [], + logIndicesExist: false, + metricIndicesExist: false, + }, + }); + const hasData = createMetricsHasData(mockedGetStartServices); + const response = await hasData(); + expect(core.http.get).toHaveBeenCalledTimes(1); + expect(response).toBeFalsy(); + }); + }); + + describe('createMetricsFetchData()', () => { + it('should just work', async () => { + const { core, mockedGetStartServices } = setup(); + core.http.post.mockResolvedValue(FAKE_SNAPSHOT_RESPONSE); + const fetchData = createMetricsFetchData(mockedGetStartServices); + const endTime = moment(); + const startTime = endTime.clone().subtract(1, 'h'); + const bucketSize = '300s'; + const response = await fetchData({ + startTime: startTime.toISOString(), + endTime: endTime.toISOString(), + bucketSize, + }); + expect(core.http.post).toHaveBeenCalledTimes(1); + expect(core.http.post).toHaveBeenCalledWith('/api/metrics/snapshot', { + body: JSON.stringify({ + sourceId: 'default', + metrics: [{ type: 'cpu' }, { type: 'memory' }, { type: 'rx' }, { type: 'tx' }], + groupBy: [], + nodeType: 'host', + timerange: { + from: startTime.valueOf(), + to: endTime.valueOf(), + interval: '300s', + forceInterval: true, + ignoreLookback: true, + }, + }), + }); + expect(response).toMatchSnapshot(); + }); + }); +}); diff --git a/x-pack/plugins/infra/public/metrics_overview_fetchers.ts b/x-pack/plugins/infra/public/metrics_overview_fetchers.ts new file mode 100644 index 00000000000000..d10ad5dda53204 --- /dev/null +++ b/x-pack/plugins/infra/public/metrics_overview_fetchers.ts @@ -0,0 +1,161 @@ +/* + * 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 moment from 'moment'; +import { sum, isFinite, isNumber } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { MetricsFetchDataResponse, FetchDataParams } from '../../observability/public'; +import { + SnapshotRequest, + SnapshotMetricInput, + SnapshotNode, + SnapshotNodeResponse, +} from '../common/http_api/snapshot_api'; +import { SnapshotMetricType } from '../common/inventory_models/types'; +import { InfraClientCoreSetup } from './types'; +import { SourceResponse } from '../common/http_api/source_api'; + +export const createMetricsHasData = ( + getStartServices: InfraClientCoreSetup['getStartServices'] +) => async () => { + const [coreServices] = await getStartServices(); + const { http } = coreServices; + const results = await http.get('/api/metrics/source/default/metrics'); + return results.status.metricIndicesExist; +}; + +export const average = (values: number[]) => (values.length ? sum(values) / values.length : 0); + +export const combineNodesBy = ( + metric: SnapshotMetricType, + nodes: SnapshotNode[], + combinator: (values: number[]) => number +) => { + const values = nodes.reduce((acc, node) => { + const snapshotMetric = node.metrics.find((m) => m.name === metric); + if (snapshotMetric?.value != null && isFinite(snapshotMetric.value)) { + acc.push(snapshotMetric.value); + } + return acc; + }, [] as number[]); + return combinator(values); +}; + +interface CombinedRow { + values: number[]; + timestamp: number; +} + +export const combineNodeTimeseriesBy = ( + metric: SnapshotMetricType, + nodes: SnapshotNode[], + combinator: (values: number[]) => number +) => { + const combinedTimeseries = nodes.reduce((acc, node) => { + const snapshotMetric = node.metrics.find((m) => m.name === metric); + if (snapshotMetric && snapshotMetric.timeseries) { + snapshotMetric.timeseries.rows.forEach((row) => { + const combinedRow = acc.find((r) => r.timestamp === row.timestamp); + if (combinedRow) { + combinedRow.values.push(isNumber(row.metric_0) ? row.metric_0 : 0); + } else { + acc.push({ + timestamp: row.timestamp, + values: [isNumber(row.metric_0) ? row.metric_0 : 0], + }); + } + }); + } + return acc; + }, [] as CombinedRow[]); + return combinedTimeseries.map((row) => ({ x: row.timestamp, y: combinator(row.values) })); +}; + +export const createMetricsFetchData = ( + getStartServices: InfraClientCoreSetup['getStartServices'] +) => async ({ + startTime, + endTime, + bucketSize, +}: FetchDataParams): Promise => { + const [coreServices] = await getStartServices(); + const { http } = coreServices; + const snapshotRequest: SnapshotRequest = { + sourceId: 'default', + metrics: ['cpu', 'memory', 'rx', 'tx'].map((type) => ({ type })) as SnapshotMetricInput[], + groupBy: [], + nodeType: 'host', + timerange: { + from: moment(startTime).valueOf(), + to: moment(endTime).valueOf(), + interval: bucketSize, + forceInterval: true, + ignoreLookback: true, + }, + }; + + const results = await http.post('/api/metrics/snapshot', { + body: JSON.stringify(snapshotRequest), + }); + + const inboundLabel = i18n.translate('xpack.infra.observabilityHomepage.metrics.rxLabel', { + defaultMessage: 'Inbound traffic', + }); + + const outboundLabel = i18n.translate('xpack.infra.observabilityHomepage.metrics.txLabel', { + defaultMessage: 'Outbound traffic', + }); + + return { + title: i18n.translate('xpack.infra.observabilityHomepage.metrics.title', { + defaultMessage: 'Metrics', + }), + appLink: '/app/metrics', + stats: { + hosts: { + type: 'number', + label: i18n.translate('xpack.infra.observabilityHomepage.metrics.hostsLabel', { + defaultMessage: 'Hosts', + }), + value: results.nodes.length, + }, + cpu: { + type: 'percent', + label: i18n.translate('xpack.infra.observabilityHomepage.metrics.cpuLabel', { + defaultMessage: 'CPU usage', + }), + value: combineNodesBy('cpu', results.nodes, average), + }, + memory: { + type: 'percent', + label: i18n.translate('xpack.infra.observabilityHomepage.metrics.memoryLabel', { + defaultMessage: 'Memory usage', + }), + value: combineNodesBy('memory', results.nodes, average), + }, + inboundTraffic: { + type: 'bytesPerSecond', + label: inboundLabel, + value: combineNodesBy('rx', results.nodes, average), + }, + outboundTraffic: { + type: 'bytesPerSecond', + label: outboundLabel, + value: combineNodesBy('tx', results.nodes, average), + }, + }, + series: { + inboundTraffic: { + label: inboundLabel, + coordinates: combineNodeTimeseriesBy('rx', results.nodes, average), + }, + outboundTraffic: { + label: outboundLabel, + coordinates: combineNodeTimeseriesBy('tx', results.nodes, average), + }, + }, + }; +}; diff --git a/x-pack/plugins/infra/public/plugin.ts b/x-pack/plugins/infra/public/plugin.ts index 1b28945320bb62..2dda664a7f675d 100644 --- a/x-pack/plugins/infra/public/plugin.ts +++ b/x-pack/plugins/infra/public/plugin.ts @@ -19,6 +19,7 @@ import { InfraClientPluginClass, } from './types'; import { getLogsHasDataFetcher, getLogsOverviewDataFetcher } from './utils/logs_overview_fetchers'; +import { createMetricsHasData, createMetricsFetchData } from './metrics_overview_fetchers'; export class Plugin implements InfraClientPluginClass { constructor(_context: PluginInitializerContext) {} @@ -36,6 +37,12 @@ export class Plugin implements InfraClientPluginClass { hasData: getLogsHasDataFetcher(core.getStartServices), fetchData: getLogsOverviewDataFetcher(core.getStartServices), }); + + pluginsSetup.observability.dashboard.register({ + appName: 'infra_metrics', + hasData: createMetricsHasData(core.getStartServices), + fetchData: createMetricsFetchData(core.getStartServices), + }); } core.application.register({ diff --git a/x-pack/plugins/infra/public/test_utils/index.ts b/x-pack/plugins/infra/public/test_utils/index.ts new file mode 100644 index 00000000000000..3de4c40f47cc9b --- /dev/null +++ b/x-pack/plugins/infra/public/test_utils/index.ts @@ -0,0 +1,309 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const FAKE_SNAPSHOT_RESPONSE = { + nodes: [ + { + path: [{ value: 'host-01', label: 'host-01', ip: '192.168.1.10' }], + metrics: [ + { + name: 'memory', + value: 0.002, + max: 0.00134, + avg: 0.0009833333333333335, + timeseries: { + id: 'memory', + columns: [ + { name: 'timestamp', type: 'date' }, + { name: 'metric_0', type: 'number' }, + ], + rows: [ + { timestamp: 1593630455000, metric_0: 0 }, + { timestamp: 1593630755000, metric_0: 0.001 }, + { timestamp: 1593631055000, metric_0: 0.00099 }, + { timestamp: 1593631355000, metric_0: 0.00133 }, + { timestamp: 1593631655000, metric_0: 0.00099 }, + { timestamp: 1593631955000, metric_0: 0.001 }, + { timestamp: 1593632255000, metric_0: 0.00099 }, + { timestamp: 1593632555000, metric_0: 0.00067 }, + { timestamp: 1593632855000, metric_0: 0.001 }, + { timestamp: 1593633155000, metric_0: 0.00099 }, + { timestamp: 1593633455000, metric_0: 0.00099 }, + { timestamp: 1593633755000, metric_0: 0.00099 }, + { timestamp: 1593634055000, metric_0: 0.001 }, + { timestamp: 1593634355000, metric_0: 0.00067 }, + { timestamp: 1593634655000, metric_0: 0.00133 }, + { timestamp: 1593634955000, metric_0: 0.00101 }, + { timestamp: 1593635255000, metric_0: 0.00134 }, + { timestamp: 1593635555000, metric_0: 0.00133 }, + { timestamp: 1593635855000, metric_0: 0.00102 }, + { timestamp: 1593636155000, metric_0: 0.00101 }, + { timestamp: 1593636455000, metric_0: 0.001 }, + ], + }, + }, + { + name: 'cpu', + value: 0.002, + max: 0.00134, + avg: 0.0009833333333333335, + timeseries: { + id: 'cpu', + columns: [ + { name: 'timestamp', type: 'date' }, + { name: 'metric_0', type: 'number' }, + ], + rows: [ + { timestamp: 1593630455000, metric_0: 0 }, + { timestamp: 1593630755000, metric_0: 0.001 }, + { timestamp: 1593631055000, metric_0: 0.00099 }, + { timestamp: 1593631355000, metric_0: 0.00133 }, + { timestamp: 1593631655000, metric_0: 0.00099 }, + { timestamp: 1593631955000, metric_0: 0.001 }, + { timestamp: 1593632255000, metric_0: 0.00099 }, + { timestamp: 1593632555000, metric_0: 0.00067 }, + { timestamp: 1593632855000, metric_0: 0.001 }, + { timestamp: 1593633155000, metric_0: 0.00099 }, + { timestamp: 1593633455000, metric_0: 0.00099 }, + { timestamp: 1593633755000, metric_0: 0.00099 }, + { timestamp: 1593634055000, metric_0: 0.001 }, + { timestamp: 1593634355000, metric_0: 0.00067 }, + { timestamp: 1593634655000, metric_0: 0.00133 }, + { timestamp: 1593634955000, metric_0: 0.00101 }, + { timestamp: 1593635255000, metric_0: 0.00134 }, + { timestamp: 1593635555000, metric_0: 0.00133 }, + { timestamp: 1593635855000, metric_0: 0.00102 }, + { timestamp: 1593636155000, metric_0: 0.00101 }, + { timestamp: 1593636455000, metric_0: 0.001 }, + ], + }, + }, + { + name: 'rx', + value: 4, + max: 13, + avg: 3.761904761904762, + timeseries: { + id: 'rx', + columns: [ + { name: 'timestamp', type: 'date' }, + { name: 'metric_0', type: 'number' }, + ], + rows: [ + { timestamp: 1593630455000, metric_0: 0 }, + { timestamp: 1593630755000, metric_0: 4 }, + { timestamp: 1593631055000, metric_0: 4 }, + { timestamp: 1593631355000, metric_0: 9 }, + { timestamp: 1593631655000, metric_0: 4 }, + { timestamp: 1593631955000, metric_0: 3 }, + { timestamp: 1593632255000, metric_0: 2 }, + { timestamp: 1593632555000, metric_0: 2 }, + { timestamp: 1593632855000, metric_0: 4 }, + { timestamp: 1593633155000, metric_0: 3 }, + { timestamp: 1593633455000, metric_0: 2 }, + { timestamp: 1593633755000, metric_0: 2 }, + { timestamp: 1593634055000, metric_0: 3 }, + { timestamp: 1593634355000, metric_0: 0 }, + { timestamp: 1593634655000, metric_0: 11 }, + { timestamp: 1593634955000, metric_0: 6 }, + { timestamp: 1593635255000, metric_0: 14 }, + { timestamp: 1593635555000, metric_0: 10 }, + { timestamp: 1593635855000, metric_0: 8 }, + { timestamp: 1593636155000, metric_0: 4 }, + { timestamp: 1593636455000, metric_0: 4 }, + ], + }, + }, + { + name: 'tx', + value: 3, + max: 13, + avg: 3.761904761904762, + timeseries: { + id: 'tx', + columns: [ + { name: 'timestamp', type: 'date' }, + { name: 'metric_0', type: 'number' }, + ], + rows: [ + { timestamp: 1593630455000, metric_0: 0 }, + { timestamp: 1593630755000, metric_0: 5 }, + { timestamp: 1593631055000, metric_0: 5 }, + { timestamp: 1593631355000, metric_0: 10 }, + { timestamp: 1593631655000, metric_0: 5 }, + { timestamp: 1593631955000, metric_0: 3 }, + { timestamp: 1593632255000, metric_0: 3 }, + { timestamp: 1593632555000, metric_0: 3 }, + { timestamp: 1593632855000, metric_0: 5 }, + { timestamp: 1593633155000, metric_0: 4 }, + { timestamp: 1593633455000, metric_0: 3 }, + { timestamp: 1593633755000, metric_0: 3 }, + { timestamp: 1593634055000, metric_0: 3 }, + { timestamp: 1593634355000, metric_0: 2 }, + { timestamp: 1593634655000, metric_0: 12 }, + { timestamp: 1593634955000, metric_0: 7 }, + { timestamp: 1593635255000, metric_0: 15 }, + { timestamp: 1593635555000, metric_0: 11 }, + { timestamp: 1593635855000, metric_0: 9 }, + { timestamp: 1593636155000, metric_0: 4 }, + { timestamp: 1593636455000, metric_0: 5 }, + ], + }, + }, + ], + }, + { + path: [{ value: 'host-02', label: 'host-02', ip: '192.168.1.11' }], + metrics: [ + { + name: 'memory', + value: 0.001, + max: 0.00134, + avg: 0.0009833333333333335, + timeseries: { + id: 'memory', + columns: [ + { name: 'timestamp', type: 'date' }, + { name: 'metric_0', type: 'number' }, + ], + rows: [ + { timestamp: 1593630455000, metric_0: 0 }, + { timestamp: 1593630755000, metric_0: 0.001 }, + { timestamp: 1593631055000, metric_0: 0.00099 }, + { timestamp: 1593631355000, metric_0: 0.00133 }, + { timestamp: 1593631655000, metric_0: 0.00099 }, + { timestamp: 1593631955000, metric_0: 0.001 }, + { timestamp: 1593632255000, metric_0: 0.00099 }, + { timestamp: 1593632555000, metric_0: 0.00067 }, + { timestamp: 1593632855000, metric_0: 0.001 }, + { timestamp: 1593633155000, metric_0: 0.00099 }, + { timestamp: 1593633455000, metric_0: 0.00099 }, + { timestamp: 1593633755000, metric_0: 0.00099 }, + { timestamp: 1593634055000, metric_0: 0.001 }, + { timestamp: 1593634355000, metric_0: 0.00067 }, + { timestamp: 1593634655000, metric_0: 0.00133 }, + { timestamp: 1593634955000, metric_0: 0.00101 }, + { timestamp: 1593635255000, metric_0: 0.00134 }, + { timestamp: 1593635555000, metric_0: 0.00133 }, + { timestamp: 1593635855000, metric_0: 0.00102 }, + { timestamp: 1593636155000, metric_0: 0.00101 }, + { timestamp: 1593636455000, metric_0: 0.001 }, + ], + }, + }, + { + name: 'cpu', + value: 0.001, + max: 0.00134, + avg: 0.0009833333333333335, + timeseries: { + id: 'cpu', + columns: [ + { name: 'timestamp', type: 'date' }, + { name: 'metric_0', type: 'number' }, + ], + rows: [ + { timestamp: 1593630455000, metric_0: 0 }, + { timestamp: 1593630755000, metric_0: 0.001 }, + { timestamp: 1593631055000, metric_0: 0.00099 }, + { timestamp: 1593631355000, metric_0: 0.00133 }, + { timestamp: 1593631655000, metric_0: 0.00099 }, + { timestamp: 1593631955000, metric_0: 0.001 }, + { timestamp: 1593632255000, metric_0: 0.00099 }, + { timestamp: 1593632555000, metric_0: 0.00067 }, + { timestamp: 1593632855000, metric_0: 0.001 }, + { timestamp: 1593633155000, metric_0: 0.00099 }, + { timestamp: 1593633455000, metric_0: 0.00099 }, + { timestamp: 1593633755000, metric_0: 0.00099 }, + { timestamp: 1593634055000, metric_0: 0.001 }, + { timestamp: 1593634355000, metric_0: 0.00067 }, + { timestamp: 1593634655000, metric_0: 0.00133 }, + { timestamp: 1593634955000, metric_0: 0.00101 }, + { timestamp: 1593635255000, metric_0: 0.00134 }, + { timestamp: 1593635555000, metric_0: 0.00133 }, + { timestamp: 1593635855000, metric_0: 0.00102 }, + { timestamp: 1593636155000, metric_0: 0.00101 }, + { timestamp: 1593636455000, metric_0: 0.001 }, + ], + }, + }, + { + name: 'rx', + value: 3, + max: 13, + avg: 3.761904761904762, + timeseries: { + id: 'rx', + columns: [ + { name: 'timestamp', type: 'date' }, + { name: 'metric_0', type: 'number' }, + ], + rows: [ + { timestamp: 1593630455000, metric_0: 0 }, + { timestamp: 1593630755000, metric_0: 3 }, + { timestamp: 1593631055000, metric_0: 3 }, + { timestamp: 1593631355000, metric_0: 8 }, + { timestamp: 1593631655000, metric_0: 3 }, + { timestamp: 1593631955000, metric_0: 2 }, + { timestamp: 1593632255000, metric_0: 1 }, + { timestamp: 1593632555000, metric_0: 1 }, + { timestamp: 1593632855000, metric_0: 3 }, + { timestamp: 1593633155000, metric_0: 2 }, + { timestamp: 1593633455000, metric_0: 1 }, + { timestamp: 1593633755000, metric_0: 1 }, + { timestamp: 1593634055000, metric_0: 2 }, + { timestamp: 1593634355000, metric_0: 0 }, + { timestamp: 1593634655000, metric_0: 10 }, + { timestamp: 1593634955000, metric_0: 5 }, + { timestamp: 1593635255000, metric_0: 13 }, + { timestamp: 1593635555000, metric_0: 9 }, + { timestamp: 1593635855000, metric_0: 7 }, + { timestamp: 1593636155000, metric_0: 2 }, + { timestamp: 1593636455000, metric_0: 3 }, + ], + }, + }, + { + name: 'tx', + value: 3, + max: 13, + avg: 3.761904761904762, + timeseries: { + id: 'tx', + columns: [ + { name: 'timestamp', type: 'date' }, + { name: 'metric_0', type: 'number' }, + ], + rows: [ + { timestamp: 1593630455000, metric_0: 0 }, + { timestamp: 1593630755000, metric_0: 3 }, + { timestamp: 1593631055000, metric_0: 3 }, + { timestamp: 1593631355000, metric_0: 8 }, + { timestamp: 1593631655000, metric_0: 3 }, + { timestamp: 1593631955000, metric_0: 2 }, + { timestamp: 1593632255000, metric_0: 1 }, + { timestamp: 1593632555000, metric_0: 1 }, + { timestamp: 1593632855000, metric_0: 3 }, + { timestamp: 1593633155000, metric_0: 2 }, + { timestamp: 1593633455000, metric_0: 1 }, + { timestamp: 1593633755000, metric_0: 1 }, + { timestamp: 1593634055000, metric_0: 2 }, + { timestamp: 1593634355000, metric_0: 0 }, + { timestamp: 1593634655000, metric_0: 10 }, + { timestamp: 1593634955000, metric_0: 5 }, + { timestamp: 1593635255000, metric_0: 13 }, + { timestamp: 1593635555000, metric_0: 9 }, + { timestamp: 1593635855000, metric_0: 7 }, + { timestamp: 1593636155000, metric_0: 2 }, + { timestamp: 1593636455000, metric_0: 3 }, + ], + }, + }, + ], + }, + ], + interval: '300s', +}; diff --git a/x-pack/plugins/ingest_manager/common/types/models/epm.ts b/x-pack/plugins/ingest_manager/common/types/models/epm.ts index e1a9dd93c84cca..0d2825f0aa80dd 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/epm.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/epm.ts @@ -256,7 +256,6 @@ export enum IngestAssetType { } export enum DefaultPackages { - base = 'base', system = 'system', endpoint = 'endpoint', } diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/__snapshots__/template.test.ts.snap b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/__snapshots__/template.test.ts.snap index 08b20d45e936a9..848e65b7931ebc 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/__snapshots__/template.test.ts.snap +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/__snapshots__/template.test.ts.snap @@ -2,7 +2,7 @@ exports[`tests loading base.yml: base.yml 1`] = ` { - "priority": 1, + "priority": 200, "index_patterns": [ "foo-*" ], @@ -106,7 +106,7 @@ exports[`tests loading base.yml: base.yml 1`] = ` exports[`tests loading coredns.logs.yml: coredns.logs.yml 1`] = ` { - "priority": 1, + "priority": 200, "index_patterns": [ "foo-*" ], @@ -210,7 +210,7 @@ exports[`tests loading coredns.logs.yml: coredns.logs.yml 1`] = ` exports[`tests loading system.yml: system.yml 1`] = ` { - "priority": 1, + "priority": 200, "index_patterns": [ "whatsthis-*" ], diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts index 4253baa208f4a4..e7867532ed1762 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts @@ -250,8 +250,11 @@ function getBaseTemplate( composedOfTemplates: string[] ): IndexTemplate { return { - // This takes precedence over all index templates installed with the 'base' package - priority: 1, + // This takes precedence over all index templates installed by ES by default (logs-*-* and metrics-*-*) + // if this number is lower than the ES value (which is 100) this template will never be applied when a data stream + // is created. I'm using 200 here to give some room for users to create their own template and fit it between the + // default and the one the ingest manager uses. + priority: 200, // To be completed with the correct index patterns index_patterns: [`${templateName}-*`], template: { diff --git a/x-pack/plugins/maps/public/actions/layer_actions.ts b/x-pack/plugins/maps/public/actions/layer_actions.ts index 51e251a5d8e202..a2711fbd124fbf 100644 --- a/x-pack/plugins/maps/public/actions/layer_actions.ts +++ b/x-pack/plugins/maps/public/actions/layer_actions.ts @@ -13,6 +13,7 @@ import { getLayerListRaw, getSelectedLayerId, getMapReady, + getMapColors, } from '../selectors/map_selectors'; import { FLYOUT_STATE } from '../reducers/ui'; import { cancelRequest } from '../reducers/non_serializable_instances'; @@ -384,7 +385,8 @@ export function clearMissingStyleProperties(layerId: string) { const nextFields = await (targetLayer as IVectorLayer).getFields(); // take into account all fields, since labels can be driven by any field (source or join) const { hasChanges, nextStyleDescriptor } = style.getDescriptorWithMissingStylePropsRemoved( - nextFields + nextFields, + getMapColors(getState()) ); if (hasChanges && nextStyleDescriptor) { dispatch(updateLayerStyle(layerId, nextStyleDescriptor)); diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.js b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.js index 97afac9ef17457..e20c509ccd4a29 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.js +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.js @@ -10,6 +10,8 @@ import { esAggFieldsFactory } from '../../fields/es_agg_field'; import { AGG_TYPE, COUNT_PROP_LABEL, FIELD_ORIGIN } from '../../../../common/constants'; import { getSourceAggKey } from '../../../../common/get_agg_key'; +export const DEFAULT_METRIC = { type: AGG_TYPE.COUNT }; + export class AbstractESAggSource extends AbstractESSource { constructor(descriptor, inspectorAdapters) { super(descriptor, inspectorAdapters); @@ -48,6 +50,7 @@ export class AbstractESAggSource extends AbstractESSource { getMetricFields() { const metrics = this._metricFields.filter((esAggField) => esAggField.isValid()); + // Handle case where metrics is empty because older saved object state is empty array or there are no valid aggs. return metrics.length === 0 ? esAggFieldsFactory({ type: AGG_TYPE.COUNT }, this, this.getOriginForField()) : metrics; diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js index b613f577067bac..9431fb55dc88bd 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js @@ -18,7 +18,7 @@ import { } from '../../../../common/constants'; import { i18n } from '@kbn/i18n'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; -import { AbstractESAggSource } from '../es_agg_source'; +import { AbstractESAggSource, DEFAULT_METRIC } from '../es_agg_source'; import { DataRequestAbortError } from '../../util/data_request'; import { registerSource } from '../source_registry'; import { makeESBbox } from '../../../elasticsearch_geo_utils'; @@ -42,7 +42,7 @@ export class ESGeoGridSource extends AbstractESAggSource { id: uuid(), indexPatternId, geoField, - metrics: metrics ? metrics : [], + metrics: metrics ? metrics : [DEFAULT_METRIC], requestType, resolution: resolution ? resolution : GRID_RESOLUTION.COARSE, }; diff --git a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/es_pew_pew_source.js b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/es_pew_pew_source.js index 076e7a758a4fbd..a4cff7c89a0119 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/es_pew_pew_source.js +++ b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/es_pew_pew_source.js @@ -12,7 +12,7 @@ import { i18n } from '@kbn/i18n'; import { SOURCE_TYPES, VECTOR_SHAPE_TYPE } from '../../../../common/constants'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; import { convertToLines } from './convert_to_lines'; -import { AbstractESAggSource } from '../es_agg_source'; +import { AbstractESAggSource, DEFAULT_METRIC } from '../es_agg_source'; import { indexPatterns } from '../../../../../../../src/plugins/data/public'; import { registerSource } from '../source_registry'; @@ -32,7 +32,7 @@ export class ESPewPewSource extends AbstractESAggSource { indexPatternId: indexPatternId, sourceGeoField, destGeoField, - metrics: metrics ? metrics : [], + metrics: metrics ? metrics : [DEFAULT_METRIC], }; } diff --git a/x-pack/plugins/maps/public/classes/styles/style.ts b/x-pack/plugins/maps/public/classes/styles/style.ts index 7d39acd504c426..1859c7875ad11c 100644 --- a/x-pack/plugins/maps/public/classes/styles/style.ts +++ b/x-pack/plugins/maps/public/classes/styles/style.ts @@ -13,7 +13,8 @@ import { DataRequest } from '../util/data_request'; export interface IStyle { getDescriptor(): StyleDescriptor | null; getDescriptorWithMissingStylePropsRemoved( - nextFields: IField[] + nextFields: IField[], + mapColors: string[] ): { hasChanges: boolean; nextStyleDescriptor?: StyleDescriptor }; pluckStyleMetaFromSourceDataRequest(sourceDataRequest: DataRequest): StyleMetaDescriptor; renderEditor({ @@ -34,7 +35,8 @@ export class AbstractStyle implements IStyle { } getDescriptorWithMissingStylePropsRemoved( - nextFields: IField[] + nextFields: IField[], + mapColors: string[] ): { hasChanges: boolean; nextStyleDescriptor?: StyleDescriptor } { return { hasChanges: false, diff --git a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.js b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.js index 04a5381fa25927..3cff48e4d682ed 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.js @@ -7,7 +7,12 @@ import _ from 'lodash'; import React from 'react'; import { VectorStyleEditor } from './components/vector_style_editor'; -import { getDefaultProperties, LINE_STYLES, POLYGON_STYLES } from './vector_style_defaults'; +import { + getDefaultProperties, + getDefaultStaticProperties, + LINE_STYLES, + POLYGON_STYLES, +} from './vector_style_defaults'; import { AbstractStyle } from '../style'; import { GEO_JSON_TYPE, @@ -191,7 +196,7 @@ export class VectorStyle extends AbstractStyle { * This method does not update its descriptor. It just returns a new descriptor that the caller * can then use to update store state via dispatch. */ - getDescriptorWithMissingStylePropsRemoved(nextFields) { + getDescriptorWithMissingStylePropsRemoved(nextFields, mapColors) { const originalProperties = this.getRawProperties(); const updatedProperties = {}; @@ -201,6 +206,13 @@ export class VectorStyle extends AbstractStyle { }); dynamicProperties.forEach((key) => { + // Convert dynamic styling to static stying when there are no nextFields + if (nextFields.length === 0) { + const staticProperties = getDefaultStaticProperties(mapColors); + updatedProperties[key] = staticProperties[key]; + return; + } + const dynamicProperty = originalProperties[key]; const fieldName = dynamicProperty && dynamicProperty.options.field && dynamicProperty.options.field.name; diff --git a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.test.js b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.test.js index a0dc07b8e545bb..a85cd0cc864075 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.test.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.test.js @@ -6,7 +6,12 @@ import { VectorStyle } from './vector_style'; import { DataRequest } from '../../util/data_request'; -import { FIELD_ORIGIN, STYLE_TYPE, VECTOR_SHAPE_TYPE } from '../../../../common/constants'; +import { + FIELD_ORIGIN, + STYLE_TYPE, + VECTOR_SHAPE_TYPE, + VECTOR_STYLES, +} from '../../../../common/constants'; jest.mock('../../../kibana_services'); jest.mock('ui/new_platform'); @@ -42,6 +47,7 @@ class MockSource { describe('getDescriptorWithMissingStylePropsRemoved', () => { const fieldName = 'doIStillExist'; + const mapColors = []; const properties = { fillColor: { type: STYLE_TYPE.STATIC, @@ -59,7 +65,8 @@ describe('getDescriptorWithMissingStylePropsRemoved', () => { iconSize: { type: STYLE_TYPE.DYNAMIC, options: { - color: 'a color', + minSize: 1, + maxSize: 10, field: { name: fieldName, origin: FIELD_ORIGIN.SOURCE }, }, }, @@ -75,86 +82,55 @@ describe('getDescriptorWithMissingStylePropsRemoved', () => { const vectorStyle = new VectorStyle({ properties }, new MockSource()); const nextFields = [new MockField({ fieldName })]; - const { hasChanges } = vectorStyle.getDescriptorWithMissingStylePropsRemoved(nextFields); + const { hasChanges } = vectorStyle.getDescriptorWithMissingStylePropsRemoved( + nextFields, + mapColors + ); expect(hasChanges).toBe(false); }); it('Should clear missing fields when next ordinal fields do not contain existing style property fields', () => { const vectorStyle = new VectorStyle({ properties }, new MockSource()); - const nextFields = []; + const nextFields = [new MockField({ fieldName: 'someOtherField' })]; const { hasChanges, nextStyleDescriptor, - } = vectorStyle.getDescriptorWithMissingStylePropsRemoved(nextFields); + } = vectorStyle.getDescriptorWithMissingStylePropsRemoved(nextFields, mapColors); expect(hasChanges).toBe(true); - expect(nextStyleDescriptor.properties).toEqual({ - fillColor: { - options: {}, - type: 'STATIC', - }, - icon: { - options: { - value: 'marker', - }, - type: 'STATIC', - }, - iconOrientation: { - options: { - orientation: 0, - }, - type: 'STATIC', - }, - iconSize: { - options: { - color: 'a color', - }, - type: 'DYNAMIC', - }, - labelText: { - options: { - value: '', - }, - type: 'STATIC', - }, - labelBorderColor: { - options: { - color: '#FFFFFF', - }, - type: 'STATIC', - }, - labelBorderSize: { - options: { - size: 'SMALL', - }, - }, - labelColor: { - options: { - color: '#000000', - }, - type: 'STATIC', - }, - labelSize: { - options: { - size: 14, - }, - type: 'STATIC', - }, - lineColor: { - options: {}, - type: 'DYNAMIC', + expect(nextStyleDescriptor.properties[VECTOR_STYLES.LINE_COLOR]).toEqual({ + options: {}, + type: 'DYNAMIC', + }); + expect(nextStyleDescriptor.properties[VECTOR_STYLES.ICON_SIZE]).toEqual({ + options: { + minSize: 1, + maxSize: 10, }, - lineWidth: { - options: { - size: 1, - }, - type: 'STATIC', + type: 'DYNAMIC', + }); + }); + + it('Should convert dynamic styles to static styles when there are no next fields', () => { + const vectorStyle = new VectorStyle({ properties }, new MockSource()); + + const nextFields = []; + const { + hasChanges, + nextStyleDescriptor, + } = vectorStyle.getDescriptorWithMissingStylePropsRemoved(nextFields, mapColors); + expect(hasChanges).toBe(true); + expect(nextStyleDescriptor.properties[VECTOR_STYLES.LINE_COLOR]).toEqual({ + options: { + color: '#41937c', }, - symbolizeAs: { - options: { - value: 'circle', - }, + type: 'STATIC', + }); + expect(nextStyleDescriptor.properties[VECTOR_STYLES.ICON_SIZE]).toEqual({ + options: { + size: 6, }, + type: 'STATIC', }); }); }); diff --git a/x-pack/plugins/maps/public/components/__snapshots__/metrics_editor.test.js.snap b/x-pack/plugins/maps/public/components/__snapshots__/metrics_editor.test.js.snap new file mode 100644 index 00000000000000..0d4f1f99e464ce --- /dev/null +++ b/x-pack/plugins/maps/public/components/__snapshots__/metrics_editor.test.js.snap @@ -0,0 +1,86 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should add default count metric when metrics is empty array 1`] = ` + +
+
+ +
+
+ + + + + + +
+`; + +exports[`should render metrics editor 1`] = ` + +
+
+ +
+
+ + + + + + +
+`; diff --git a/x-pack/plugins/maps/public/components/metrics_editor.js b/x-pack/plugins/maps/public/components/metrics_editor.js index 6c5a9af8f0f025..7d4d7bf3ec7ab1 100644 --- a/x-pack/plugins/maps/public/components/metrics_editor.js +++ b/x-pack/plugins/maps/public/components/metrics_editor.js @@ -10,11 +10,14 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiButtonEmpty, EuiSpacer, EuiTextAlign } from '@elastic/eui'; import { MetricEditor } from './metric_editor'; -import { AGG_TYPE } from '../../common/constants'; +import { DEFAULT_METRIC } from '../classes/sources/es_agg_source'; export function MetricsEditor({ fields, metrics, onChange, allowMultipleMetrics, metricsFilter }) { function renderMetrics() { - return metrics.map((metric, index) => { + // There was a bug in 7.8 that initialized metrics to []. + // This check is needed to handle any saved objects created before the bug was patched. + const nonEmptyMetrics = metrics.length === 0 ? [DEFAULT_METRIC] : metrics; + return nonEmptyMetrics.map((metric, index) => { const onMetricChange = (metric) => { onChange([...metrics.slice(0, index), metric, ...metrics.slice(index + 1)]); }; @@ -100,6 +103,6 @@ MetricsEditor.propTypes = { }; MetricsEditor.defaultProps = { - metrics: [{ type: AGG_TYPE.COUNT }], + metrics: [DEFAULT_METRIC], allowMultipleMetrics: true, }; diff --git a/x-pack/plugins/maps/public/components/metrics_editor.test.js b/x-pack/plugins/maps/public/components/metrics_editor.test.js new file mode 100644 index 00000000000000..bcbeef29875eeb --- /dev/null +++ b/x-pack/plugins/maps/public/components/metrics_editor.test.js @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; +import { MetricsEditor } from './metrics_editor'; +import { AGG_TYPE } from '../../common/constants'; + +const defaultProps = { + metrics: [ + { + type: AGG_TYPE.SUM, + field: 'myField', + }, + ], + fields: [], + onChange: () => {}, + allowMultipleMetrics: true, + metricsFilter: () => {}, +}; + +test('should render metrics editor', async () => { + const component = shallow(); + expect(component).toMatchSnapshot(); +}); + +test('should add default count metric when metrics is empty array', async () => { + const component = shallow(); + expect(component).toMatchSnapshot(); +}); diff --git a/x-pack/plugins/maps/public/index.scss b/x-pack/plugins/maps/public/index.scss index fe974fa610c03e..d2dd07b0f81f91 100644 --- a/x-pack/plugins/maps/public/index.scss +++ b/x-pack/plugins/maps/public/index.scss @@ -1,8 +1,5 @@ /* GIS plugin styles */ -// Import the EUI global scope so we can use EUI constants -@import 'src/legacy/ui/public/styles/_styling_constants'; - // Prefix all styles with "map" to avoid conflicts. // Examples // mapChart diff --git a/x-pack/plugins/ml/public/application/_index.scss b/x-pack/plugins/ml/public/application/_index.scss index 11dc593a235a14..65e914a1ac923a 100644 --- a/x-pack/plugins/ml/public/application/_index.scss +++ b/x-pack/plugins/ml/public/application/_index.scss @@ -1,6 +1,3 @@ -// Should import both the EUI constants and any Kibana ones that are considered global -@import 'src/legacy/ui/public/styles/styling_constants'; - // ML has it's own variables for coloring @import 'variables'; diff --git a/x-pack/plugins/observability/public/typings/fetch_overview_data/index.ts b/x-pack/plugins/observability/public/typings/fetch_overview_data/index.ts index e65d1779520cf3..e57dfebb36419e 100644 --- a/x-pack/plugins/observability/public/typings/fetch_overview_data/index.ts +++ b/x-pack/plugins/observability/public/typings/fetch_overview_data/index.ts @@ -59,7 +59,6 @@ export interface MetricsFetchDataResponse extends FetchDataResponse { hosts: Stat; cpu: Stat; memory: Stat; - disk: Stat; inboundTraffic: Stat; outboundTraffic: Stat; }; diff --git a/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts b/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts index 199b8a91e43078..37b73088561960 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts @@ -25,22 +25,8 @@ export const factory = (): PolicyConfig => { mode: ProtectionModes.prevent, }, logging: { - stdout: 'debug', file: 'info', }, - advanced: { - elasticsearch: { - indices: { - control: 'control-index', - event: 'event-index', - logging: 'logging-index', - }, - kernel: { - connect: true, - process: true, - }, - }, - }, }, mac: { events: { @@ -49,25 +35,11 @@ export const factory = (): PolicyConfig => { network: true, }, malware: { - mode: ProtectionModes.detect, + mode: ProtectionModes.prevent, }, logging: { - stdout: 'debug', file: 'info', }, - advanced: { - elasticsearch: { - indices: { - control: 'control-index', - event: 'event-index', - logging: 'logging-index', - }, - kernel: { - connect: true, - process: true, - }, - }, - }, }, linux: { events: { @@ -76,22 +48,8 @@ export const factory = (): PolicyConfig => { network: true, }, logging: { - stdout: 'debug', file: 'info', }, - advanced: { - elasticsearch: { - indices: { - control: 'control-index', - event: 'event-index', - logging: 'logging-index', - }, - kernel: { - connect: true, - process: true, - }, - }, - }, }, }; }; diff --git a/x-pack/plugins/security_solution/common/endpoint/types.ts b/x-pack/plugins/security_solution/common/endpoint/types.ts index 42b1337a91464e..f2b8acb627cc4c 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types.ts @@ -613,10 +613,8 @@ export interface PolicyConfig { }; malware: MalwareFields; logging: { - stdout: string; file: string; }; - advanced: PolicyConfigAdvancedOptions; }; mac: { events: { @@ -626,10 +624,8 @@ export interface PolicyConfig { }; malware: MalwareFields; logging: { - stdout: string; file: string; }; - advanced: PolicyConfigAdvancedOptions; }; linux: { events: { @@ -638,10 +634,8 @@ export interface PolicyConfig { network: boolean; }; logging: { - stdout: string; file: string; }; - advanced: PolicyConfigAdvancedOptions; }; } @@ -663,20 +657,6 @@ export interface UIPolicyConfig { linux: Pick; } -interface PolicyConfigAdvancedOptions { - elasticsearch: { - indices: { - control: string; - event: string; - logging: string; - }; - kernel: { - connect: boolean; - process: boolean; - }; - }; -} - /** Policy: Malware protection fields */ export interface MalwareFields { mode: ProtectionModes; @@ -686,7 +666,6 @@ export interface MalwareFields { export enum ProtectionModes { detect = 'detect', prevent = 'prevent', - preventNotify = 'preventNotify', off = 'off', } diff --git a/x-pack/plugins/security_solution/public/alerts/components/alerts_info/query.dsl.ts b/x-pack/plugins/security_solution/public/alerts/components/alerts_info/query.dsl.ts index a3972fd35bf2d5..4b57c7dc20d9f7 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/alerts_info/query.dsl.ts +++ b/x-pack/plugins/security_solution/public/alerts/components/alerts_info/query.dsl.ts @@ -10,6 +10,7 @@ export const buildLastAlertsQuery = (ruleId: string | undefined | null) => { bool: { should: [{ match: { 'signal.status': 'open' } }], minimum_should_match: 1 }, }, ]; + return { aggs: { lastSeen: { max: { field: '@timestamp' } }, @@ -30,7 +31,7 @@ export const buildLastAlertsQuery = (ruleId: string | undefined | null) => { : queryFilter, }, }, - size: 0, + size: 1, track_total_hits: true, }; }; diff --git a/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx index f079715baec1c1..a3cab1cfabd71b 100644 --- a/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx @@ -376,4 +376,63 @@ describe('QueryBar ', () => { expect(onSubmitQueryRef).toEqual(wrapper.find(SearchBar).props().onQuerySubmit); }); }); + + describe('SavedQueryManagementComponent state', () => { + test('popover should hidden when "Save current query" button was clicked', () => { + const KibanaWithStorageProvider = createKibanaContextProviderMock(); + + const Proxy = (props: QueryBarComponentProps) => ( + + + + + + ); + + const wrapper = mount( + + ); + + const isSavedQueryPopoverOpen = () => + wrapper.find('EuiPopover[id="savedQueryPopover"]').prop('isOpen'); + + expect(isSavedQueryPopoverOpen()).toBeFalsy(); + + wrapper + .find('button[data-test-subj="saved-query-management-popover-button"]') + .simulate('click'); + + expect(isSavedQueryPopoverOpen()).toBeTruthy(); + + wrapper.find('button[data-test-subj="saved-query-management-save-button"]').simulate('click'); + + expect(isSavedQueryPopoverOpen()).toBeFalsy(); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/malware.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/malware.tsx index 77d4d4364acdd7..23ac6cc5b813d4 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/malware.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/malware.tsx @@ -95,13 +95,6 @@ export const MalwareProtections = React.memo(() => { }), protection: 'malware', }, - { - id: ProtectionModes.preventNotify, - label: i18n.translate('xpack.securitySolution.endpoint.policy.details.preventAndNotify', { - defaultMessage: 'Prevent and notify user', - }), - protection: 'malware', - }, ]; }, []); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/translations.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/translations.ts index ef7ee26cd3ecf5..5af2f3ef488b07 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/translations.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/translations.ts @@ -69,6 +69,6 @@ export const COLLAPSE = i18n.translate( export const ACTION_INVESTIGATE_IN_RESOLVER = i18n.translate( 'xpack.securitySolution.timeline.body.actions.investigateInResolverTooltip', { - defaultMessage: 'Investigate in Resolver', + defaultMessage: 'Analyze event', } ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx index b27f213c6a02c1..d1a58dafcb328f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx @@ -204,6 +204,7 @@ export const EventsTrSupplement = styled.div.attrs(({ className = '' }) => ({ export const EventsTdGroupActions = styled.div.attrs(({ className = '' }) => ({ className: `siemEventsTable__tdGroupActions ${className}`, }))<{ actionsColumnWidth: number }>` + align-items: center; display: flex; flex: 0 0 ${({ actionsColumnWidth }) => `${actionsColumnWidth}px`}; min-width: 0; diff --git a/x-pack/plugins/snapshot_restore/public/application/index.scss b/x-pack/plugins/snapshot_restore/public/application/index.scss index b680f4d3ebf903..3e16e3b5301e79 100644 --- a/x-pack/plugins/snapshot_restore/public/application/index.scss +++ b/x-pack/plugins/snapshot_restore/public/application/index.scss @@ -1,6 +1,3 @@ -// Import the EUI global scope so we can use EUI constants -@import 'src/legacy/ui/public/styles/_styling_constants'; - // Snapshot and Restore plugin styles // Prefix all styles with "snapshotRestore" to avoid conflicts. diff --git a/x-pack/plugins/transform/public/app/index.scss b/x-pack/plugins/transform/public/app/index.scss index beb5ee6be67e66..cc5cc87c754c9c 100644 --- a/x-pack/plugins/transform/public/app/index.scss +++ b/x-pack/plugins/transform/public/app/index.scss @@ -1,6 +1,3 @@ -// Import the EUI global scope so we can use EUI constants -@import 'src/legacy/ui/public/styles/_styling_constants'; - // Transform plugin styles // Prefix all styles with "transform" to avoid conflicts. diff --git a/x-pack/plugins/upgrade_assistant/public/application/_index.scss b/x-pack/plugins/upgrade_assistant/public/application/_index.scss index 6000af5498cd6e..841415620d6917 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/_index.scss +++ b/x-pack/plugins/upgrade_assistant/public/application/_index.scss @@ -1,3 +1 @@ -@import 'src/legacy/ui/public/styles/_styling_constants'; - @import 'components/index'; diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts index 45ea82c59bf978..bacba619e5648e 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts @@ -118,45 +118,15 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }, policy: { linux: { - advanced: { - elasticsearch: { - indices: { - control: 'control-index', - event: 'event-index', - logging: 'logging-index', - }, - kernel: { connect: true, process: true }, - }, - }, events: { file: false, network: true, process: true }, - logging: { file: 'info', stdout: 'debug' }, + logging: { file: 'info' }, }, mac: { - advanced: { - elasticsearch: { - indices: { - control: 'control-index', - event: 'event-index', - logging: 'logging-index', - }, - kernel: { connect: true, process: true }, - }, - }, events: { file: false, network: true, process: true }, - logging: { file: 'info', stdout: 'debug' }, - malware: { mode: 'detect' }, + logging: { file: 'info' }, + malware: { mode: 'prevent' }, }, windows: { - advanced: { - elasticsearch: { - indices: { - control: 'control-index', - event: 'event-index', - logging: 'logging-index', - }, - kernel: { connect: true, process: true }, - }, - }, events: { dll_and_driver_load: true, dns: true, @@ -166,7 +136,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { registry: true, security: true, }, - logging: { file: 'info', stdout: 'debug' }, + logging: { file: 'info' }, malware: { mode: 'prevent' }, }, },