diff --git a/.ci/end2end.groovy b/.ci/end2end.groovy index 0cd64dcfd41fd..ed642f22cfeb4 100644 --- a/.ci/end2end.groovy +++ b/.ci/end2end.groovy @@ -76,7 +76,7 @@ pipeline { } } steps{ - notifyStatus('Running smoke tests', 'PENDING') + notifyTestStatus('Running smoke tests', 'PENDING') dir("${BASE_DIR}"){ sh "${E2E_DIR}/ci/run-e2e.sh" } @@ -95,10 +95,10 @@ pipeline { } } unsuccessful { - notifyStatus('Test failures', 'FAILURE') + notifyTestStatus('Test failures', 'FAILURE') } success { - notifyStatus('Tests passed', 'SUCCESS') + notifyTestStatus('Tests passed', 'SUCCESS') } } } @@ -113,5 +113,9 @@ pipeline { } def notifyStatus(String description, String status) { - withGithubNotify.notify('end2end-for-apm-ui', description, status, getBlueoceanDisplayURL()) + withGithubNotify.notify('end2end-for-apm-ui', description, status, getBlueoceanTabURL('pipeline')) +} + +def notifyTestStatus(String description, String status) { + withGithubNotify.notify('end2end-for-apm-ui', description, status, getBlueoceanTabURL('tests')) } diff --git a/.eslintrc.js b/.eslintrc.js index 9657719f0f526..8d5b4525d51ba 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -472,6 +472,7 @@ module.exports = { { files: [ 'test/functional/services/lib/web_element_wrapper/scroll_into_view_if_necessary.js', + 'src/legacy/ui/ui_render/bootstrap/kbn_bundles_loader_source.js', '**/browser_exec_scripts/**/*.js', ], rules: { diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 00573e04396b4..681564b44297f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -66,11 +66,10 @@ # APM /x-pack/plugins/apm/ @elastic/apm-ui -/x-pack/plugins/apm/ @elastic/apm-ui /x-pack/test/functional/apps/apm/ @elastic/apm-ui /src/legacy/core_plugins/apm_oss/ @elastic/apm-ui /src/plugins/apm_oss/ @elastic/apm-ui -/src/apm.js @watson +/src/apm.js @watson @vigneshshanmugam # Beats /x-pack/legacy/plugins/beats_management/ @elastic/beats diff --git a/NOTICE.txt b/NOTICE.txt index 33c1d535d7df3..946b328b8766c 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -21,6 +21,11 @@ used. Logarithmic ticks are places at powers of ten and at half those values if there are not to many ticks already (e.g. [1, 5, 10, 50, 100]). For details, see https://github.com/flot/flot/pull/1328 +--- +This module was heavily inspired by the externals plugin that ships with webpack@97d58d31 +MIT License http://www.opensource.org/licenses/mit-license.php +Author Tobias Koppers @sokra + --- This product has relied on ASTExplorer that is licensed under MIT. diff --git a/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.extrapublicdirs.md b/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.extrapublicdirs.md new file mode 100644 index 0000000000000..c46e60f2ecf6d --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.extrapublicdirs.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [PluginManifest](./kibana-plugin-core-server.pluginmanifest.md) > [extraPublicDirs](./kibana-plugin-core-server.pluginmanifest.extrapublicdirs.md) + +## PluginManifest.extraPublicDirs property + +> Warning: This API is now obsolete. +> +> + +Specifies directory names that can be imported by other ui-plugins built using the same instance of the @kbn/optimizer. A temporary measure we plan to replace with better mechanisms for sharing static code between plugins + +Signature: + +```typescript +readonly extraPublicDirs?: string[]; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md b/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md index fe0ca476bbcb2..5edee51d6c523 100644 --- a/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md +++ b/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md @@ -21,6 +21,7 @@ Should never be used in code outside of Core but is exported for documentation p | Property | Type | Description | | --- | --- | --- | | [configPath](./kibana-plugin-core-server.pluginmanifest.configpath.md) | ConfigPath | Root [configuration path](./kibana-plugin-core-server.configpath.md) used by the plugin, defaults to "id" in snake\_case format. | +| [extraPublicDirs](./kibana-plugin-core-server.pluginmanifest.extrapublicdirs.md) | string[] | Specifies directory names that can be imported by other ui-plugins built using the same instance of the @kbn/optimizer. A temporary measure we plan to replace with better mechanisms for sharing static code between plugins | | [id](./kibana-plugin-core-server.pluginmanifest.id.md) | PluginName | Identifier of the plugin. Must be a string in camelCase. Part of a plugin public contract. Other plugins leverage it to access plugin API, navigate to the plugin, etc. | | [kibanaVersion](./kibana-plugin-core-server.pluginmanifest.kibanaversion.md) | string | The version of Kibana the plugin is compatible with, defaults to "version". | | [optionalPlugins](./kibana-plugin-core-server.pluginmanifest.optionalplugins.md) | readonly PluginName[] | An optional list of the other plugins that if installed and enabled \*\*may be\*\* leveraged by this plugin for some additional functionality but otherwise are not required for this plugin to work properly. | diff --git a/docs/user/ml/index.asciidoc b/docs/user/ml/index.asciidoc index f82bb0a406511..1bc74ce87de08 100644 --- a/docs/user/ml/index.asciidoc +++ b/docs/user/ml/index.asciidoc @@ -87,12 +87,14 @@ and {ml-docs}/xpack-ml.html[{ml-cap} {anomaly-detect}]. [[xpack-ml-dfanalytics]] == {dfanalytics-cap} +experimental[] + The Elastic {ml} {dfanalytics} feature enables you to analyze your data using -{oldetection} and {regression} algorithms and generate new indices that contain -the results alongside your source data. +{classification}, {oldetection}, and {regression} algorithms and generate new +indices that contain the results alongside your source data. If you have a license that includes the {ml-features}, you can create -{oldetection} {dfanalytics-jobs} and view their results on the *Analytics* page +{dfanalytics-jobs} and view their results on the *Analytics* page in {kib}. For example: [role="screenshot"] diff --git a/docs/visualize/vega.asciidoc b/docs/visualize/vega.asciidoc index efe9094a14922..5f127df459ed3 100644 --- a/docs/visualize/vega.asciidoc +++ b/docs/visualize/vega.asciidoc @@ -32,7 +32,7 @@ image::images/vega_lite_default.png[] The default visualization uses Vega-Lite version 2. To use Vega version 4, edit the `schema`. -Go to `$schema`, enter `https://vega.github.io/schema/vega/v4.json`, then click +Go to `$schema`, enter `https://vega.github.io/schema/vega/v5.json`, then click *Update*. [float] diff --git a/examples/demo_search/kibana.json b/examples/demo_search/kibana.json index cdf74121ea2db..f909ca47fcd55 100644 --- a/examples/demo_search/kibana.json +++ b/examples/demo_search/kibana.json @@ -5,5 +5,6 @@ "server": true, "ui": true, "requiredPlugins": ["data"], - "optionalPlugins": [] + "optionalPlugins": [], + "extraPublicDirs": ["common"] } diff --git a/examples/embeddable_examples/kibana.json b/examples/embeddable_examples/kibana.json index 489f768552b28..b3ee0de096989 100644 --- a/examples/embeddable_examples/kibana.json +++ b/examples/embeddable_examples/kibana.json @@ -5,5 +5,6 @@ "server": true, "ui": true, "requiredPlugins": ["embeddable"], - "optionalPlugins": [] + "optionalPlugins": [], + "extraPublicDirs": ["public/todo", "public/hello_world"] } diff --git a/examples/url_generators_examples/kibana.json b/examples/url_generators_examples/kibana.json index cdb2127fdd26f..9658f5c7300aa 100644 --- a/examples/url_generators_examples/kibana.json +++ b/examples/url_generators_examples/kibana.json @@ -5,5 +5,8 @@ "server": false, "ui": true, "requiredPlugins": ["share"], - "optionalPlugins": [] + "optionalPlugins": [], + "extraPublicDirs": [ + "public/url_generator" + ] } diff --git a/package.json b/package.json index e8b07ae4abba2..85eba40c6251a 100644 --- a/package.json +++ b/package.json @@ -122,7 +122,7 @@ "@babel/core": "^7.10.2", "@babel/plugin-transform-modules-commonjs": "^7.10.1", "@babel/register": "^7.10.1", - "@elastic/apm-rum": "^5.1.1", + "@elastic/apm-rum": "^5.2.0", "@elastic/charts": "19.2.0", "@elastic/datemath": "5.0.3", "@elastic/ems-client": "7.9.3", @@ -243,7 +243,7 @@ "react-input-range": "^1.3.0", "react-markdown": "^4.3.1", "react-monaco-editor": "~0.27.0", - "react-redux": "^7.1.3", + "react-redux": "^7.2.0", "react-resize-detector": "^4.2.0", "react-router": "^5.1.2", "react-router-dom": "^5.1.2", @@ -280,9 +280,9 @@ "uuid": "3.3.2", "val-loader": "^1.1.1", "validate-npm-package-name": "2.2.2", - "vega-lib": "4.3.0", - "vega-lite": "^2.6.0", - "vega-schema-url-parser": "1.0.0", + "vega": "^5.13.0", + "vega-lite": "^4.13.1", + "vega-schema-url-parser": "^1.1.0", "vega-tooltip": "^0.12.0", "vision": "^5.3.3", "webpack": "^4.41.5", @@ -375,7 +375,7 @@ "@types/react": "^16.9.36", "@types/react-dom": "^16.9.8", "@types/react-grid-layout": "^0.16.7", - "@types/react-redux": "^7.1.7", + "@types/react-redux": "^7.1.9", "@types/react-resize-detector": "^4.0.1", "@types/react-router": "^5.1.3", "@types/react-router-dom": "^5.1.3", diff --git a/packages/kbn-es/src/utils/native_realm.js b/packages/kbn-es/src/utils/native_realm.js index 573944a8cc6d0..5075cfb965c08 100644 --- a/packages/kbn-es/src/utils/native_realm.js +++ b/packages/kbn-es/src/utils/native_realm.js @@ -37,13 +37,8 @@ exports.NativeRealm = class NativeRealm { this._log = log; } - async setPassword(username, password = this._elasticPassword, { attempt = 1 } = {}) { - await this._autoRetry(async () => { - this._log.info( - (attempt > 1 ? `attempt ${attempt}: ` : '') + - `setting ${chalk.bold(username)} password to ${chalk.bold(password)}` - ); - + async setPassword(username, password = this._elasticPassword, retryOpts = {}) { + await this._autoRetry(retryOpts, async () => { try { await this._client.security.changePassword({ username, @@ -83,8 +78,8 @@ exports.NativeRealm = class NativeRealm { ); } - async getReservedUsers() { - return await this._autoRetry(async () => { + async getReservedUsers(retryOpts = {}) { + return await this._autoRetry(retryOpts, async () => { const resp = await this._client.security.getUser(); const usernames = Object.keys(resp.body).filter( (user) => resp.body[user].metadata._reserved === true @@ -98,9 +93,9 @@ exports.NativeRealm = class NativeRealm { }); } - async isSecurityEnabled() { + async isSecurityEnabled(retryOpts = {}) { try { - return await this._autoRetry(async () => { + return await this._autoRetry(retryOpts, async () => { const { body: { features }, } = await this._client.xpack.info({ categories: 'features' }); @@ -115,18 +110,25 @@ exports.NativeRealm = class NativeRealm { } } - async _autoRetry(fn, attempt = 1) { + async _autoRetry(opts, fn) { + const { attempt = 1, maxAttempts = 3 } = opts; + try { return await fn(attempt); } catch (error) { - if (attempt >= 3) { + if (attempt >= maxAttempts) { throw error; } const sec = 1.5 * attempt; this._log.warning(`assuming ES isn't initialized completely, trying again in ${sec} seconds`); await new Promise((resolve) => setTimeout(resolve, sec * 1000)); - return await this._autoRetry(fn, attempt + 1); + + const nextOpts = { + ...opts, + attempt: attempt + 1, + }; + return await this._autoRetry(nextOpts, fn); } } }; diff --git a/packages/kbn-es/src/utils/native_realm.test.js b/packages/kbn-es/src/utils/native_realm.test.js index 54732f7136fcc..cd124c97dfdd4 100644 --- a/packages/kbn-es/src/utils/native_realm.test.js +++ b/packages/kbn-es/src/utils/native_realm.test.js @@ -85,7 +85,7 @@ describe('isSecurityEnabled', () => { throw error; }); - expect(await nativeRealm.isSecurityEnabled()).toBe(false); + expect(await nativeRealm.isSecurityEnabled({ maxAttempts: 1 })).toBe(false); }); test('rejects if unexpected error is thrown', async () => { @@ -97,9 +97,9 @@ describe('isSecurityEnabled', () => { throw error; }); - await expect(nativeRealm.isSecurityEnabled()).rejects.toThrowErrorMatchingInlineSnapshot( - `"ResponseError"` - ); + await expect( + nativeRealm.isSecurityEnabled({ maxAttempts: 1 }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"ResponseError"`); }); }); @@ -226,7 +226,7 @@ describe('setPassword', () => { }); await expect( - nativeRealm.setPassword('kibana_system', 'foo') + nativeRealm.setPassword('kibana_system', 'foo', { maxAttempts: 1 }) ).rejects.toThrowErrorMatchingInlineSnapshot(`"SomeError"`); }); }); diff --git a/packages/kbn-optimizer/README.md b/packages/kbn-optimizer/README.md index c7f50c6af8dfd..9ff0f56344274 100644 --- a/packages/kbn-optimizer/README.md +++ b/packages/kbn-optimizer/README.md @@ -30,6 +30,18 @@ Bundles built by the the optimizer include a cache file which describes the info When a bundle is determined to be up-to-date a worker is not started for the bundle. If running the optimizer with the `--dev/--watch` flag, then all the files referenced by cached bundles are watched for changes. Once a change is detected in any of the files referenced by the built bundle a worker is started. If a file is changed that is referenced by several bundles then workers will be started for each bundle, combining workers together to respect the worker limit. +## Bundle Refs + +In order to dramatically reduce the size of our bundles, and the time it takes to build them, bundles will "ref" other bundles being built at the same time. When the optimizer starts it creates a list of "refs" that could be had from the list of bundles being built. Each worker uses that list to determine which import statements in a bundle should be replaced with a runtime reference to the output of another bundle. + +At runtime the bundles share a set of entry points via the `__kbnBundles__` global. By default a plugin shares `public` so that other code can use relative imports to access that directory. To expose additional directories they must be listed in the plugin's kibana.json "extraPublicDirs" field. The directories listed there will **also** be exported from the plugins bundle so that any other plugin can import that directory. "common" is commonly in the list of "extraPublicDirs". + +> NOTE: We plan to replace the `extraPublicDirs` functionality soon with better mechanisms for statically sharing code between bundles. + +When a directory is listed in the "extraPublicDirs" it will always be included in the bundle so that other plugins have access to it. The worker building the bundle has no way of knowing whether another plugin is using the directory, so be careful of adding test code or unnecessary directories to that list. + +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. + ## 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/package.json b/packages/kbn-optimizer/package.json index a372b9e394b9a..c7bf1dd60985d 100644 --- a/packages/kbn-optimizer/package.json +++ b/packages/kbn-optimizer/package.json @@ -45,6 +45,7 @@ "terser-webpack-plugin": "^2.1.2", "tinymath": "1.2.1", "url-loader": "^2.2.0", + "val-loader": "^1.1.1", "watchpack": "^1.6.0", "webpack": "^4.41.5", "webpack-merge": "^4.2.2" diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/public/index.ts b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/public/index.ts index 7ddd10f4a388f..c881a15eac5b5 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/public/index.ts +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/public/index.ts @@ -19,6 +19,6 @@ import './legacy/styles.scss'; import './index.scss'; -import { fooLibFn } from '../../foo/public/index'; +import { fooLibFn } from '../../foo/public'; export * from './lib'; export { fooLibFn }; diff --git a/packages/kbn-optimizer/src/common/bundle.test.ts b/packages/kbn-optimizer/src/common/bundle.test.ts index ec78a1bdf020e..b209bbca25ac4 100644 --- a/packages/kbn-optimizer/src/common/bundle.test.ts +++ b/packages/kbn-optimizer/src/common/bundle.test.ts @@ -23,7 +23,7 @@ jest.mock('fs'); const SPEC: BundleSpec = { contextDir: '/foo/bar', - entry: 'entry', + publicDirNames: ['public'], id: 'bar', outputDir: '/foo/bar/target', sourceRoot: '/foo', @@ -49,9 +49,11 @@ it('creates cache keys', () => { }, "spec": Object { "contextDir": "/foo/bar", - "entry": "entry", "id": "bar", "outputDir": "/foo/bar/target", + "publicDirNames": Array [ + "public", + ], "sourceRoot": "/foo", "type": "plugin", }, @@ -82,9 +84,11 @@ it('parses bundles from JSON specs', () => { "state": undefined, }, "contextDir": "/foo/bar", - "entry": "entry", "id": "bar", "outputDir": "/foo/bar/target", + "publicDirNames": Array [ + "public", + ], "sourceRoot": "/foo", "type": "plugin", }, diff --git a/packages/kbn-optimizer/src/common/bundle.ts b/packages/kbn-optimizer/src/common/bundle.ts index 9e2ad186ba40c..80af94c30f8da 100644 --- a/packages/kbn-optimizer/src/common/bundle.ts +++ b/packages/kbn-optimizer/src/common/bundle.ts @@ -29,8 +29,8 @@ export interface BundleSpec { readonly type: typeof VALID_BUNDLE_TYPES[0]; /** Unique id for this bundle */ readonly id: string; - /** Webpack entry request for this plugin, relative to the contextDir */ - readonly entry: string; + /** directory names relative to the contextDir that can be imported from */ + readonly publicDirNames: string[]; /** Absolute path to the plugin source directory */ readonly contextDir: string; /** Absolute path to the root of the repository */ @@ -44,8 +44,8 @@ export class Bundle { public readonly type: BundleSpec['type']; /** Unique identifier for this bundle */ public readonly id: BundleSpec['id']; - /** Path, relative to `contextDir`, to the entry file for the Webpack bundle */ - public readonly entry: BundleSpec['entry']; + /** directory names relative to the contextDir that can be imported from */ + public readonly publicDirNames: BundleSpec['publicDirNames']; /** * Absolute path to the root of the bundle context (plugin directory) * where the entry is resolved relative to and the default output paths @@ -62,7 +62,7 @@ export class Bundle { constructor(spec: BundleSpec) { this.type = spec.type; this.id = spec.id; - this.entry = spec.entry; + this.publicDirNames = spec.publicDirNames; this.contextDir = spec.contextDir; this.sourceRoot = spec.sourceRoot; this.outputDir = spec.outputDir; @@ -73,8 +73,6 @@ export class Bundle { /** * Calculate the cache key for this bundle based from current * mtime values. - * - * @param mtimes pre-fetched mtimes (ms || undefined) for all referenced files */ createCacheKey(files: string[], mtimes: Map): unknown { return { @@ -94,7 +92,7 @@ export class Bundle { return { type: this.type, id: this.id, - entry: this.entry, + publicDirNames: this.publicDirNames, contextDir: this.contextDir, sourceRoot: this.sourceRoot, outputDir: this.outputDir, @@ -134,9 +132,9 @@ export function parseBundles(json: string) { throw new Error('`bundles[]` must have a string `id` property'); } - const { entry } = spec; - if (!(typeof entry === 'string')) { - throw new Error('`bundles[]` must have a string `entry` property'); + const { publicDirNames } = spec; + if (!Array.isArray(publicDirNames) || !publicDirNames.every((d) => typeof d === 'string')) { + throw new Error('`bundles[]` must have an array of strings `publicDirNames` property'); } const { contextDir } = spec; @@ -157,7 +155,7 @@ export function parseBundles(json: string) { return new Bundle({ type, id, - entry, + publicDirNames, contextDir, sourceRoot, outputDir, diff --git a/packages/kbn-optimizer/src/common/bundle_cache.ts b/packages/kbn-optimizer/src/common/bundle_cache.ts index 1dbc7f1d1b6b0..5ae3e4c28a201 100644 --- a/packages/kbn-optimizer/src/common/bundle_cache.ts +++ b/packages/kbn-optimizer/src/common/bundle_cache.ts @@ -25,6 +25,7 @@ export interface State { cacheKey?: unknown; moduleCount?: number; files?: string[]; + bundleRefExportIds?: string[]; } const DEFAULT_STATE: State = {}; @@ -87,6 +88,10 @@ export class BundleCache { return this.get().files; } + public getBundleRefExportIds() { + return this.get().bundleRefExportIds; + } + public getCacheKey() { return this.get().cacheKey; } diff --git a/packages/kbn-optimizer/src/common/bundle_refs.ts b/packages/kbn-optimizer/src/common/bundle_refs.ts new file mode 100644 index 0000000000000..a5c60f2031c0b --- /dev/null +++ b/packages/kbn-optimizer/src/common/bundle_refs.ts @@ -0,0 +1,130 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Path from 'path'; + +import { Bundle } from './bundle'; +import { UnknownVals } from './ts_helpers'; + +export interface BundleRef { + bundleId: string; + contextDir: string; + contextPrefix: string; + entry: string; + exportId: string; +} + +export class BundleRefs { + static fromBundles(bundles: Bundle[]) { + return new BundleRefs( + bundles.reduce( + (acc: BundleRef[], b) => [ + ...acc, + ...b.publicDirNames.map( + (name): BundleRef => ({ + bundleId: b.id, + contextDir: b.contextDir, + // Path.resolve converts separators and strips the final separator + contextPrefix: Path.resolve(b.contextDir) + Path.sep, + entry: name, + exportId: `${b.type}/${b.id}/${name}`, + }) + ), + ], + [] + ) + ); + } + + static parseSpec(json: unknown) { + if (typeof json !== 'string') { + throw new Error('expected `bundleRefs` spec to be a JSON string'); + } + + let spec; + try { + spec = JSON.parse(json); + } catch (error) { + throw new Error('`bundleRefs` spec must be valid JSON'); + } + + if (!Array.isArray(spec)) { + throw new Error('`bundleRefs` spec must be an array'); + } + + return new BundleRefs( + spec.map( + (refSpec: UnknownVals): BundleRef => { + if (typeof refSpec !== 'object' || !refSpec) { + throw new Error('`bundleRefs[]` must be an object'); + } + + const { bundleId } = refSpec; + if (typeof bundleId !== 'string') { + throw new Error('`bundleRefs[].bundleId` must be a string'); + } + + const { contextDir } = refSpec; + if (typeof contextDir !== 'string' || !Path.isAbsolute(contextDir)) { + throw new Error('`bundleRefs[].contextDir` must be an absolute directory'); + } + + const { contextPrefix } = refSpec; + if (typeof contextPrefix !== 'string' || !Path.isAbsolute(contextPrefix)) { + throw new Error('`bundleRefs[].contextPrefix` must be an absolute directory'); + } + + const { entry } = refSpec; + if (typeof entry !== 'string') { + throw new Error('`bundleRefs[].entry` must be a string'); + } + + const { exportId } = refSpec; + if (typeof exportId !== 'string') { + throw new Error('`bundleRefs[].exportId` must be a string'); + } + + return { + bundleId, + contextDir, + contextPrefix, + entry, + exportId, + }; + } + ) + ); + } + + constructor(private readonly refs: BundleRef[]) {} + + public filterByExportIds(exportIds: string[]) { + return this.refs.filter((r) => exportIds.includes(r.exportId)); + } + + public filterByContextPrefix(bundle: Bundle, absolutePath: string) { + return this.refs.filter( + (ref) => ref.bundleId !== bundle.id && absolutePath.startsWith(ref.contextPrefix) + ); + } + + public toSpecJson() { + return JSON.stringify(this.refs); + } +} diff --git a/packages/kbn-optimizer/src/common/index.ts b/packages/kbn-optimizer/src/common/index.ts index c51905be04565..7d021a5ee7847 100644 --- a/packages/kbn-optimizer/src/common/index.ts +++ b/packages/kbn-optimizer/src/common/index.ts @@ -19,6 +19,7 @@ export * from './bundle'; export * from './bundle_cache'; +export * from './bundle_refs'; export * from './worker_config'; export * from './worker_messages'; export * from './compiler_messages'; 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 2814ab32017d2..2265bad9f6afa 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 @@ -9,9 +9,11 @@ OptimizerConfig { "state": undefined, }, "contextDir": /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar, - "entry": "./public/index", "id": "bar", "outputDir": /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/target/public, + "publicDirNames": Array [ + "public", + ], "sourceRoot": /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo, "type": "plugin", }, @@ -21,9 +23,11 @@ OptimizerConfig { "state": undefined, }, "contextDir": /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo, - "entry": "./public/index", "id": "foo", "outputDir": /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/target/public, + "publicDirNames": Array [ + "public", + ], "sourceRoot": /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo, "type": "plugin", }, @@ -35,16 +39,19 @@ OptimizerConfig { "plugins": Array [ Object { "directory": /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar, + "extraPublicDirs": Array [], "id": "bar", "isUiPlugin": true, }, Object { "directory": /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/baz, + "extraPublicDirs": Array [], "id": "baz", "isUiPlugin": false, }, Object { "directory": /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo, + "extraPublicDirs": Array [], "id": "foo", "isUiPlugin": true, }, @@ -55,8 +62,8 @@ OptimizerConfig { } `; -exports[`prepares assets for distribution: bar bundle 1`] = `"var __kbnBundles__=typeof __kbnBundles__===\\"object\\"?__kbnBundles__:{};__kbnBundles__[\\"plugin/bar\\"]=function(modules){function webpackJsonpCallback(data){var chunkIds=data[0];var moreModules=data[1];var moduleId,chunkId,i=0,resolves=[];for(;i { const foo = config.bundles.find((b) => b.id === 'foo')!; expect(foo).toBeTruthy(); foo.cache.refresh(); - expect(foo.cache.getModuleCount()).toBe(5); + expect(foo.cache.getModuleCount()).toBe(6); expect(foo.cache.getReferencedFiles()).toMatchInlineSnapshot(` Array [ /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/async_import.ts, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/ext.ts, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/index.ts, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/lib.ts, + /packages/kbn-optimizer/target/worker/entry_point_creator.js, /packages/kbn-ui-shared-deps/public_path_module_creator.js, ] `); @@ -148,7 +149,7 @@ it('builds expected bundles, saves bundle counts to metadata', async () => { bar.cache.refresh(); expect(bar.cache.getModuleCount()).toBe( // code + styles + style/css-loader runtimes + public path updater - 21 + 18 ); expect(bar.cache.getReferencedFiles()).toMatchInlineSnapshot(` @@ -159,11 +160,8 @@ it('builds expected bundles, saves bundle counts to metadata', async () => { /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/index.ts, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/legacy/styles.scss, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/lib.ts, - /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/async_import.ts, - /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/ext.ts, - /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/index.ts, - /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/lib.ts, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/src/legacy/ui/public/icon.svg, + /packages/kbn-optimizer/target/worker/entry_point_creator.js, /packages/kbn-ui-shared-deps/public_path_module_creator.js, ] `); diff --git a/packages/kbn-optimizer/src/integration_tests/bundle_cache.test.ts b/packages/kbn-optimizer/src/integration_tests/bundle_cache.test.ts index 39064c64062e8..48cab508954a0 100644 --- a/packages/kbn-optimizer/src/integration_tests/bundle_cache.test.ts +++ b/packages/kbn-optimizer/src/integration_tests/bundle_cache.test.ts @@ -75,6 +75,7 @@ it('emits "bundle cached" event when everything is updated', async () => { optimizerCacheKey, files, moduleCount: files.length, + bundleRefExportIds: [], }); const cacheEvents = await getBundleCacheEvent$(config, optimizerCacheKey) @@ -115,6 +116,7 @@ it('emits "bundle not cached" event when cacheKey is up to date but caching is d optimizerCacheKey, files, moduleCount: files.length, + bundleRefExportIds: [], }); const cacheEvents = await getBundleCacheEvent$(config, optimizerCacheKey) @@ -155,6 +157,7 @@ it('emits "bundle not cached" event when optimizerCacheKey is missing', async () optimizerCacheKey: undefined, files, moduleCount: files.length, + bundleRefExportIds: [], }); const cacheEvents = await getBundleCacheEvent$(config, optimizerCacheKey) @@ -195,6 +198,7 @@ it('emits "bundle not cached" event when optimizerCacheKey is outdated, includes optimizerCacheKey: 'old', files, moduleCount: files.length, + bundleRefExportIds: [], }); const cacheEvents = await getBundleCacheEvent$(config, optimizerCacheKey) @@ -217,6 +221,53 @@ it('emits "bundle not cached" event when optimizerCacheKey is outdated, includes `); }); +it('emits "bundle not cached" event when bundleRefExportIds is outdated, includes diff', async () => { + const config = OptimizerConfig.create({ + repoRoot: MOCK_REPO_DIR, + pluginScanDirs: [], + pluginPaths: [Path.resolve(MOCK_REPO_DIR, 'plugins/foo')], + maxWorkerCount: 1, + }); + const [bundle] = config.bundles; + + const optimizerCacheKey = 'optimizerCacheKey'; + const files = [ + Path.resolve(MOCK_REPO_DIR, 'plugins/foo/public/ext.ts'), + Path.resolve(MOCK_REPO_DIR, 'plugins/foo/public/index.ts'), + Path.resolve(MOCK_REPO_DIR, 'plugins/foo/public/lib.ts'), + ]; + const mtimes = await getMtimes(files); + const cacheKey = bundle.createCacheKey(files, mtimes); + + bundle.cache.set({ + cacheKey, + optimizerCacheKey, + files, + moduleCount: files.length, + bundleRefExportIds: ['plugin/bar/public'], + }); + + const cacheEvents = await getBundleCacheEvent$(config, optimizerCacheKey) + .pipe(toArray()) + .toPromise(); + + expect(cacheEvents).toMatchInlineSnapshot(` + Array [ + Object { + "bundle": , + "diff": "- Expected + + Received + +  [ + + \\"plugin/bar/public\\" +  ]", + "reason": "bundle references outdated", + "type": "bundle not cached", + }, + ] + `); +}); + it('emits "bundle not cached" event when cacheKey is missing', async () => { const config = OptimizerConfig.create({ repoRoot: MOCK_REPO_DIR, @@ -238,6 +289,7 @@ it('emits "bundle not cached" event when cacheKey is missing', async () => { optimizerCacheKey, files, moduleCount: files.length, + bundleRefExportIds: [], }); const cacheEvents = await getBundleCacheEvent$(config, optimizerCacheKey) @@ -276,6 +328,7 @@ it('emits "bundle not cached" event when cacheKey is outdated', async () => { optimizerCacheKey, files, moduleCount: files.length, + bundleRefExportIds: [], }); jest.spyOn(bundle, 'createCacheKey').mockImplementation(() => 'new'); diff --git a/packages/kbn-optimizer/src/integration_tests/watch_bundles_for_changes.test.ts b/packages/kbn-optimizer/src/integration_tests/watch_bundles_for_changes.test.ts index 91d0f308e0ef6..176b17c979da9 100644 --- a/packages/kbn-optimizer/src/integration_tests/watch_bundles_for_changes.test.ts +++ b/packages/kbn-optimizer/src/integration_tests/watch_bundles_for_changes.test.ts @@ -29,14 +29,14 @@ jest.mock('fs'); jest.mock('watchpack'); const MockWatchPack: jest.MockedClass = jest.requireMock('watchpack'); -const bundleEntryPath = (bundle: Bundle) => `${bundle.contextDir}/${bundle.entry}`; +const bundleEntryPath = (bundle: Bundle) => `${bundle.contextDir}/public/index.ts`; const makeTestBundle = (id: string) => { const bundle = new Bundle({ type: 'plugin', id, contextDir: `/repo/plugins/${id}/public`, - entry: 'index.ts', + publicDirNames: ['public'], outputDir: `/repo/plugins/${id}/target/public`, sourceRoot: `/repo`, }); diff --git a/packages/kbn-optimizer/src/optimizer/assign_bundles_to_workers.test.ts b/packages/kbn-optimizer/src/optimizer/assign_bundles_to_workers.test.ts index 4671276797049..ca50a49e26913 100644 --- a/packages/kbn-optimizer/src/optimizer/assign_bundles_to_workers.test.ts +++ b/packages/kbn-optimizer/src/optimizer/assign_bundles_to_workers.test.ts @@ -57,7 +57,7 @@ const assertReturnVal = (workers: Assignments[]) => { const testBundle = (id: string) => new Bundle({ contextDir: `/repo/plugin/${id}/public`, - entry: 'index.ts', + publicDirNames: ['public'], id, outputDir: `/repo/plugins/${id}/target/public`, sourceRoot: `/repo`, diff --git a/packages/kbn-optimizer/src/optimizer/bundle_cache.ts b/packages/kbn-optimizer/src/optimizer/bundle_cache.ts index 55e8e1d3fd084..83db8570bd408 100644 --- a/packages/kbn-optimizer/src/optimizer/bundle_cache.ts +++ b/packages/kbn-optimizer/src/optimizer/bundle_cache.ts @@ -20,7 +20,7 @@ import * as Rx from 'rxjs'; import { mergeAll } from 'rxjs/operators'; -import { Bundle } from '../common'; +import { Bundle, BundleRefs } from '../common'; import { OptimizerConfig } from './optimizer_config'; import { getMtimes } from './get_mtimes'; @@ -35,7 +35,9 @@ export interface BundleNotCachedEvent { | 'optimizer cache key mismatch' | 'missing cache key' | 'cache key mismatch' - | 'cache disabled'; + | 'cache disabled' + | 'bundle references missing' + | 'bundle references outdated'; diff?: string; bundle: Bundle; } @@ -52,6 +54,7 @@ export function getBundleCacheEvent$( return Rx.defer(async () => { const events: BundleCacheEvent[] = []; const eligibleBundles: Bundle[] = []; + const bundleRefs = BundleRefs.fromBundles(config.bundles); for (const bundle of config.bundles) { if (!config.cache) { @@ -93,6 +96,32 @@ export function getBundleCacheEvent$( continue; } + const bundleRefExportIds = bundle.cache.getBundleRefExportIds(); + if (!bundleRefExportIds) { + events.push({ + type: 'bundle not cached', + reason: 'bundle references missing', + bundle, + }); + continue; + } + + const refs = bundleRefs.filterByExportIds(bundleRefExportIds); + + const bundleRefsDiff = diffCacheKey( + refs.map((r) => r.exportId).sort((a, b) => a.localeCompare(b)), + bundleRefExportIds + ); + if (bundleRefsDiff) { + events.push({ + type: 'bundle not cached', + reason: 'bundle references outdated', + diff: bundleRefsDiff, + bundle, + }); + continue; + } + eligibleBundles.push(bundle); } diff --git a/packages/kbn-optimizer/src/optimizer/cache_keys.ts b/packages/kbn-optimizer/src/optimizer/cache_keys.ts index 2766f6d63702b..d0aaad979485d 100644 --- a/packages/kbn-optimizer/src/optimizer/cache_keys.ts +++ b/packages/kbn-optimizer/src/optimizer/cache_keys.ts @@ -37,16 +37,6 @@ import { OptimizerConfig } from './optimizer_config'; const OPTIMIZER_DIR = Path.dirname(require.resolve('../../package.json')); const RELATIVE_DIR = Path.relative(REPO_ROOT, OPTIMIZER_DIR); -function omit(obj: T, keys: K[]): Omit { - const result: any = {}; - for (const [key, value] of Object.entries(obj) as any) { - if (!keys.includes(key)) { - result[key] = value; - } - } - return result as Omit; -} - export function diffCacheKey(expected?: unknown, actual?: unknown) { const expectedJson = jsonStable(expected, { space: ' ', @@ -185,7 +175,7 @@ export async function getOptimizerCacheKey(config: OptimizerConfig) { bootstrap, deletedPaths, modifiedTimes: {} as Record, - workerConfig: omit(config.getWorkerConfig('♻'), ['watch', 'profileWebpack', 'cache']), + workerConfig: config.getCacheableWorkerConfig(), }; const mtimes = await getMtimes(modifiedPaths); diff --git a/packages/kbn-optimizer/src/optimizer/get_plugin_bundles.test.ts b/packages/kbn-optimizer/src/optimizer/get_plugin_bundles.test.ts index 2174c488ad6cc..bbd3ddc11f448 100644 --- a/packages/kbn-optimizer/src/optimizer/get_plugin_bundles.test.ts +++ b/packages/kbn-optimizer/src/optimizer/get_plugin_bundles.test.ts @@ -31,16 +31,19 @@ it('returns a bundle for core and each plugin', () => { directory: '/repo/plugins/foo', id: 'foo', isUiPlugin: true, + extraPublicDirs: [], }, { directory: '/repo/plugins/bar', id: 'bar', isUiPlugin: false, + extraPublicDirs: [], }, { directory: '/outside/of/repo/plugins/baz', id: 'baz', isUiPlugin: true, + extraPublicDirs: [], }, ], '/repo' @@ -49,17 +52,21 @@ it('returns a bundle for core and each plugin', () => { Array [ Object { "contextDir": /plugins/foo, - "entry": "./public/index", "id": "foo", "outputDir": /plugins/foo/target/public, + "publicDirNames": Array [ + "public", + ], "sourceRoot": , "type": "plugin", }, Object { "contextDir": "/outside/of/repo/plugins/baz", - "entry": "./public/index", "id": "baz", "outputDir": "/outside/of/repo/plugins/baz/target/public", + "publicDirNames": Array [ + "public", + ], "sourceRoot": , "type": "plugin", }, diff --git a/packages/kbn-optimizer/src/optimizer/get_plugin_bundles.ts b/packages/kbn-optimizer/src/optimizer/get_plugin_bundles.ts index b75a8a6edc264..2635289088725 100644 --- a/packages/kbn-optimizer/src/optimizer/get_plugin_bundles.ts +++ b/packages/kbn-optimizer/src/optimizer/get_plugin_bundles.ts @@ -31,7 +31,7 @@ export function getPluginBundles(plugins: KibanaPlatformPlugin[], repoRoot: stri new Bundle({ type: 'plugin', id: p.id, - entry: './public/index', + publicDirNames: ['public', ...p.extraPublicDirs], sourceRoot: repoRoot, contextDir: p.directory, outputDir: Path.resolve(p.directory, 'target/public'), diff --git a/packages/kbn-optimizer/src/optimizer/kibana_platform_plugins.test.ts b/packages/kbn-optimizer/src/optimizer/kibana_platform_plugins.test.ts index e047b6d1e44cf..0961881df461c 100644 --- a/packages/kbn-optimizer/src/optimizer/kibana_platform_plugins.test.ts +++ b/packages/kbn-optimizer/src/optimizer/kibana_platform_plugins.test.ts @@ -37,21 +37,25 @@ it('parses kibana.json files of plugins found in pluginDirs', () => { Array [ Object { "directory": /packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar, + "extraPublicDirs": Array [], "id": "bar", "isUiPlugin": true, }, Object { "directory": /packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/baz, + "extraPublicDirs": Array [], "id": "baz", "isUiPlugin": false, }, Object { "directory": /packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/foo, + "extraPublicDirs": Array [], "id": "foo", "isUiPlugin": true, }, Object { "directory": /packages/kbn-optimizer/src/__fixtures__/mock_repo/test_plugins/test_baz, + "extraPublicDirs": Array [], "id": "test_baz", "isUiPlugin": false, }, diff --git a/packages/kbn-optimizer/src/optimizer/kibana_platform_plugins.ts b/packages/kbn-optimizer/src/optimizer/kibana_platform_plugins.ts index 992feab6cd364..bfc60a29efa27 100644 --- a/packages/kbn-optimizer/src/optimizer/kibana_platform_plugins.ts +++ b/packages/kbn-optimizer/src/optimizer/kibana_platform_plugins.ts @@ -26,6 +26,7 @@ export interface KibanaPlatformPlugin { readonly directory: string; readonly id: string; readonly isUiPlugin: boolean; + readonly extraPublicDirs: string[]; } /** @@ -64,9 +65,24 @@ function readKibanaPlatformPlugin(manifestPath: string): KibanaPlatformPlugin { throw new TypeError('expected new platform plugin manifest to have a string id'); } + let extraPublicDirs: string[] | undefined; + if (manifest.extraPublicDirs) { + if ( + !Array.isArray(manifest.extraPublicDirs) || + !manifest.extraPublicDirs.every((p) => typeof p === 'string') + ) { + throw new TypeError( + 'expected new platform plugin manifest to have an array of strings `extraPublicDirs` property' + ); + } + + extraPublicDirs = manifest.extraPublicDirs as string[]; + } + return { directory: Path.dirname(manifestPath), id: manifest.id, isUiPlugin: !!manifest.ui, + extraPublicDirs: extraPublicDirs || [], }; } diff --git a/packages/kbn-optimizer/src/optimizer/observe_worker.ts b/packages/kbn-optimizer/src/optimizer/observe_worker.ts index c929cf62d1bb0..4527052fa821a 100644 --- a/packages/kbn-optimizer/src/optimizer/observe_worker.ts +++ b/packages/kbn-optimizer/src/optimizer/observe_worker.ts @@ -24,7 +24,7 @@ import execa from 'execa'; import * as Rx from 'rxjs'; import { map, takeUntil } from 'rxjs/operators'; -import { isWorkerMsg, WorkerConfig, WorkerMsg, Bundle } from '../common'; +import { isWorkerMsg, WorkerConfig, WorkerMsg, Bundle, BundleRefs } from '../common'; import { OptimizerConfig } from './optimizer_config'; @@ -74,7 +74,11 @@ function usingWorkerProc( ) { return Rx.using( (): ProcResource => { - const args = [JSON.stringify(workerConfig), JSON.stringify(bundles.map((b) => b.toSpec()))]; + const args = [ + JSON.stringify(workerConfig), + JSON.stringify(bundles.map((b) => b.toSpec())), + BundleRefs.fromBundles(config.bundles).toSpecJson(), + ]; const proc = execa.node(require.resolve('../worker/run_worker'), args, { nodeOptions: [ diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts index 37d8a4f5eb8ae..c9e9b3ad01ccc 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts @@ -20,7 +20,7 @@ import Path from 'path'; import Os from 'os'; -import { Bundle, WorkerConfig } from '../common'; +import { Bundle, WorkerConfig, CacheableWorkerConfig } from '../common'; import { findKibanaPlatformPlugins, KibanaPlatformPlugin } from './kibana_platform_plugins'; import { getPluginBundles } from './get_plugin_bundles'; @@ -34,6 +34,16 @@ function pickMaxWorkerCount(dist: boolean) { return Math.max(maxWorkers, 2); } +function omit(obj: T, keys: K[]): Omit { + const result: any = {}; + for (const [key, value] of Object.entries(obj) as any) { + if (!keys.includes(key)) { + result[key] = value; + } + } + return result as Omit; +} + interface Options { /** absolute path to root of the repo/build */ repoRoot: string; @@ -152,7 +162,7 @@ export class OptimizerConfig { new Bundle({ type: 'entry', id: 'core', - entry: './public/index', + publicDirNames: ['public', 'public/utils'], sourceRoot: options.repoRoot, contextDir: Path.resolve(options.repoRoot, 'src/core'), outputDir: Path.resolve(options.repoRoot, 'src/core/target/public'), @@ -198,4 +208,14 @@ export class OptimizerConfig { browserslistEnv: this.dist ? 'production' : process.env.BROWSERSLIST_ENV || 'dev', }; } + + getCacheableWorkerConfig(): CacheableWorkerConfig { + return omit(this.getWorkerConfig('♻'), [ + // these config options don't change the output of the bundles, so + // should not invalidate caches when they change + 'watch', + 'profileWebpack', + 'cache', + ]); + } } diff --git a/packages/kbn-optimizer/src/worker/bundle_ref_module.ts b/packages/kbn-optimizer/src/worker/bundle_ref_module.ts new file mode 100644 index 0000000000000..cde25564cf528 --- /dev/null +++ b/packages/kbn-optimizer/src/worker/bundle_ref_module.ts @@ -0,0 +1,67 @@ +/* eslint-disable @kbn/eslint/require-license-header */ + +/** + * @notice + * + * This module was heavily inspired by the externals plugin that ships with webpack@97d58d31 + * MIT License http://www.opensource.org/licenses/mit-license.php + * Author Tobias Koppers @sokra + */ + +// @ts-ignore not typed by @types/webpack +import Module from 'webpack/lib/Module'; + +export class BundleRefModule extends Module { + public built = false; + public buildMeta?: any; + public buildInfo?: any; + public exportsArgument = '__webpack_exports__'; + + constructor(public readonly exportId: string) { + super('kbn/bundleRef', null); + } + + libIdent() { + return this.exportId; + } + + chunkCondition(chunk: any) { + return chunk.hasEntryModule(); + } + + identifier() { + return '@kbn/bundleRef ' + JSON.stringify(this.exportId); + } + + readableIdentifier() { + return this.identifier(); + } + + needRebuild() { + return false; + } + + build(_: any, __: any, ___: any, ____: any, callback: () => void) { + this.built = true; + this.buildMeta = {}; + this.buildInfo = {}; + callback(); + } + + source() { + return ` + __webpack_require__.r(__webpack_exports__); + var ns = __kbnBundles__.get('${this.exportId}'); + Object.defineProperties(__webpack_exports__, Object.getOwnPropertyDescriptors(ns)) + `; + } + + size() { + return 42; + } + + updateHash(hash: any) { + hash.update(this.identifier()); + super.updateHash(hash); + } +} diff --git a/packages/kbn-optimizer/src/worker/bundle_refs_plugin.ts b/packages/kbn-optimizer/src/worker/bundle_refs_plugin.ts new file mode 100644 index 0000000000000..6defcaa787b7d --- /dev/null +++ b/packages/kbn-optimizer/src/worker/bundle_refs_plugin.ts @@ -0,0 +1,165 @@ +/* eslint-disable @kbn/eslint/require-license-header */ + +/** + * @notice + * + * This module was heavily inspired by the externals plugin that ships with webpack@97d58d31 + * MIT License http://www.opensource.org/licenses/mit-license.php + * Author Tobias Koppers @sokra + */ + +import Path from 'path'; +import Fs from 'fs'; + +import webpack from 'webpack'; + +import { Bundle, BundleRefs, BundleRef } from '../common'; +import { BundleRefModule } from './bundle_ref_module'; + +const RESOLVE_EXTENSIONS = ['.js', '.ts', '.tsx']; + +function safeStat(path: string): Promise { + return new Promise((resolve, reject) => { + Fs.stat(path, (error, stat) => { + if (error?.code === 'ENOENT') { + resolve(undefined); + } else if (error) { + reject(error); + } else { + resolve(stat); + } + }); + }); +} + +interface RequestData { + context: string; + dependencies: Array<{ request: string }>; +} + +type Callback = (error?: any, result?: T) => void; +type ModuleFactory = (data: RequestData, callback: Callback) => void; + +/** + * Isolate the weired type juggling we have to do to add a hook to the webpack compiler + */ +function hookIntoCompiler( + compiler: webpack.Compiler, + handler: (context: string, request: string) => Promise +) { + compiler.hooks.compile.tap('BundleRefsPlugin', (compilationParams: any) => { + compilationParams.normalModuleFactory.hooks.factory.tap( + 'BundleRefsPlugin/normalModuleFactory/factory', + (wrappedFactory: ModuleFactory): ModuleFactory => (data, callback) => { + const context = data.context; + const dep = data.dependencies[0]; + + handler(context, dep.request).then( + (result) => { + if (!result) { + wrappedFactory(data, callback); + } else { + callback(undefined, result); + } + }, + (error) => callback(error) + ); + } + ); + }); +} + +export class BundleRefsPlugin { + private resolvedRequestCache = new Map>(); + + constructor(private readonly bundle: Bundle, public readonly bundleRefs: BundleRefs) {} + + apply(compiler: webpack.Compiler) { + hookIntoCompiler(compiler, async (context, request) => { + const ref = await this.resolveRef(context, request); + if (ref) { + return new BundleRefModule(ref.exportId); + } + }); + } + + private cachedResolveRequest(context: string, request: string) { + const absoluteRequest = Path.resolve(context, request); + const cached = this.resolvedRequestCache.get(absoluteRequest); + + if (cached) { + return cached; + } + + const promise = this.resolveRequest(absoluteRequest); + this.resolvedRequestCache.set(absoluteRequest, promise); + return promise; + } + + private async resolveRequest(absoluteRequest: string) { + const stats = await safeStat(absoluteRequest); + if (stats && stats.isFile()) { + return absoluteRequest; + } + + if (stats?.isDirectory()) { + for (const ext of RESOLVE_EXTENSIONS) { + const indexPath = Path.resolve(absoluteRequest, `index${ext}`); + const indexStats = await safeStat(indexPath); + if (indexStats?.isFile()) { + return indexPath; + } + } + } + + return; + } + + /** + * Determine if an import request resolves to a bundleRef export id. If the + * request resolves to a bundle ref context but none of the exported directories + * then an error is thrown. If the request does not resolve to a bundleRef then + * undefined is returned. Otherwise it returns the referenced bundleRef. + */ + private async resolveRef(context: string, request: string) { + // ignore imports that have loaders defined or are not relative seeming + if (request.includes('!') || !request.startsWith('.')) { + return; + } + + const requestExt = Path.extname(request); + if (requestExt && !RESOLVE_EXTENSIONS.includes(requestExt)) { + return; + } + + const resolved = await this.cachedResolveRequest(context, request); + if (!resolved) { + return; + } + + const eligibleRefs = this.bundleRefs.filterByContextPrefix(this.bundle, resolved); + if (!eligibleRefs.length) { + // import doesn't match a bundle context + return; + } + + let matchingRef: BundleRef | undefined; + for (const ref of eligibleRefs) { + const resolvedEntry = await this.cachedResolveRequest(ref.contextDir, ref.entry); + if (resolved === resolvedEntry) { + matchingRef = ref; + break; + } + } + + if (!matchingRef) { + const bundleId = Array.from(new Set(eligibleRefs.map((r) => r.bundleId))).join(', '); + const publicDir = eligibleRefs.map((r) => r.entry).join(', '); + throw new Error( + `import [${request}] references a non-public export of the [${bundleId}] bundle and must point to one of the public directories: [${publicDir}]` + ); + } + + return matchingRef; + } +} diff --git a/packages/kbn-optimizer/src/worker/entry_point_creator.ts b/packages/kbn-optimizer/src/worker/entry_point_creator.ts new file mode 100644 index 0000000000000..a613e3e8925a4 --- /dev/null +++ b/packages/kbn-optimizer/src/worker/entry_point_creator.ts @@ -0,0 +1,28 @@ +/* + * 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. + */ + +module.exports = function ({ entries }: { entries: Array<{ importId: string; relPath: string }> }) { + const lines = entries.map(({ importId, relPath }) => [ + `__kbnBundles__.define('${importId}', __webpack_require__, require.resolve('./${relPath}'))`, + ]); + + return { + code: lines.join('\n'), + }; +}; diff --git a/packages/kbn-optimizer/src/worker/run_compilers.ts b/packages/kbn-optimizer/src/worker/run_compilers.ts index 4ab289d031d72..de5e9372e9e7a 100644 --- a/packages/kbn-optimizer/src/worker/run_compilers.ts +++ b/packages/kbn-optimizer/src/worker/run_compilers.ts @@ -35,7 +35,9 @@ import { WorkerConfig, ascending, parseFilePath, + BundleRefs, } from '../common'; +import { BundleRefModule } from './bundle_ref_module'; import { getWebpackConfig } from './webpack.config'; import { isFailureStats, failedStatsToErrorMessage } from './webpack_helpers'; import { @@ -43,7 +45,6 @@ import { isNormalModule, isIgnoredModule, isConcatenatedModule, - WebpackNormalModule, getModulePath, } from './webpack_helpers'; @@ -98,40 +99,43 @@ const observeCompiler = ( }); } - const normalModules = stats.compilation.modules.filter( - (module): module is WebpackNormalModule => { - if (isNormalModule(module)) { - return true; - } + const bundleRefExportIds: string[] = []; + const referencedFiles = new Set(); + let normalModuleCount = 0; + + for (const module of stats.compilation.modules) { + if (isNormalModule(module)) { + normalModuleCount += 1; + const path = getModulePath(module); + const parsedPath = parseFilePath(path); - if (isExternalModule(module) || isIgnoredModule(module) || isConcatenatedModule(module)) { - return false; + if (!parsedPath.dirs.includes('node_modules')) { + referencedFiles.add(path); + continue; } - throw new Error(`Unexpected module type: ${inspect(module)}`); + const nmIndex = parsedPath.dirs.lastIndexOf('node_modules'); + const isScoped = parsedPath.dirs[nmIndex + 1].startsWith('@'); + referencedFiles.add( + Path.join( + parsedPath.root, + ...parsedPath.dirs.slice(0, nmIndex + 1 + (isScoped ? 2 : 1)), + 'package.json' + ) + ); + continue; } - ); - - const referencedFiles = new Set(); - for (const module of normalModules) { - const path = getModulePath(module); - const parsedPath = parseFilePath(path); + if (module instanceof BundleRefModule) { + bundleRefExportIds.push(module.exportId); + continue; + } - if (!parsedPath.dirs.includes('node_modules')) { - referencedFiles.add(path); + if (isExternalModule(module) || isIgnoredModule(module) || isConcatenatedModule(module)) { continue; } - const nmIndex = parsedPath.dirs.lastIndexOf('node_modules'); - const isScoped = parsedPath.dirs[nmIndex + 1].startsWith('@'); - referencedFiles.add( - Path.join( - parsedPath.root, - ...parsedPath.dirs.slice(0, nmIndex + 1 + (isScoped ? 2 : 1)), - 'package.json' - ) - ); + throw new Error(`Unexpected module type: ${inspect(module)}`); } const files = Array.from(referencedFiles).sort(ascending((p) => p)); @@ -150,14 +154,15 @@ const observeCompiler = ( ); bundle.cache.set({ + bundleRefExportIds, optimizerCacheKey: workerConfig.optimizerCacheKey, cacheKey: bundle.createCacheKey(files, mtimes), - moduleCount: normalModules.length, + moduleCount: normalModuleCount, files, }); return compilerMsgs.compilerSuccess({ - moduleCount: normalModules.length, + moduleCount: normalModuleCount, }); }) ); @@ -185,8 +190,14 @@ const observeCompiler = ( /** * Run webpack compilers */ -export const runCompilers = (workerConfig: WorkerConfig, bundles: Bundle[]) => { - const multiCompiler = webpack(bundles.map((def) => getWebpackConfig(def, workerConfig))); +export const runCompilers = ( + workerConfig: WorkerConfig, + bundles: Bundle[], + bundleRefs: BundleRefs +) => { + const multiCompiler = webpack( + bundles.map((def) => getWebpackConfig(def, bundleRefs, workerConfig)) + ); return Rx.merge( /** diff --git a/packages/kbn-optimizer/src/worker/run_worker.ts b/packages/kbn-optimizer/src/worker/run_worker.ts index f83c69477f471..178637d39ab00 100644 --- a/packages/kbn-optimizer/src/worker/run_worker.ts +++ b/packages/kbn-optimizer/src/worker/run_worker.ts @@ -19,7 +19,14 @@ import * as Rx from 'rxjs'; -import { parseBundles, parseWorkerConfig, WorkerMsg, isWorkerMsg, WorkerMsgs } from '../common'; +import { + parseBundles, + parseWorkerConfig, + WorkerMsg, + isWorkerMsg, + WorkerMsgs, + BundleRefs, +} from '../common'; import { runCompilers } from './run_compilers'; @@ -76,11 +83,12 @@ setInterval(() => { Rx.defer(() => { const workerConfig = parseWorkerConfig(process.argv[2]); const bundles = parseBundles(process.argv[3]); + const bundleRefs = BundleRefs.parseSpec(process.argv[4]); // set BROWSERSLIST_ENV so that style/babel loaders see it before running compilers process.env.BROWSERSLIST_ENV = workerConfig.browserslistEnv; - return runCompilers(workerConfig, bundles); + return runCompilers(workerConfig, bundles, bundleRefs); }).subscribe( (msg) => { send(msg); diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index d31c098ca1f2e..e361b186c30e0 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -17,10 +17,8 @@ * under the License. */ -import Fs from 'fs'; import Path from 'path'; -import normalizePath from 'normalize-path'; import { stringifyRequest } from 'loader-utils'; import webpack from 'webpack'; // @ts-ignore @@ -32,88 +30,22 @@ import { CleanWebpackPlugin } from 'clean-webpack-plugin'; import CompressionPlugin from 'compression-webpack-plugin'; import * as UiSharedDeps from '@kbn/ui-shared-deps'; -import { Bundle, WorkerConfig, parseDirPath, DisallowedSyntaxPlugin } from '../common'; +import { Bundle, BundleRefs, WorkerConfig, parseDirPath, DisallowedSyntaxPlugin } from '../common'; +import { BundleRefsPlugin } from './bundle_refs_plugin'; const IS_CODE_COVERAGE = !!process.env.CODE_COVERAGE; const ISTANBUL_PRESET_PATH = require.resolve('@kbn/babel-preset/istanbul_preset'); const BABEL_PRESET_PATH = require.resolve('@kbn/babel-preset/webpack_preset'); -const SHARED_BUNDLES = [ - { - type: 'entry', - id: 'core', - rootRelativeDir: 'src/core/public', - }, - { - type: 'plugin', - id: 'data', - rootRelativeDir: 'src/plugins/data/public', - }, - { - type: 'plugin', - id: 'kibanaReact', - rootRelativeDir: 'src/plugins/kibana_react/public', - }, - { - type: 'plugin', - id: 'kibanaUtils', - rootRelativeDir: 'src/plugins/kibana_utils/public', - }, - { - type: 'plugin', - id: 'esUiShared', - rootRelativeDir: 'src/plugins/es_ui_shared/public', - }, -]; - -/** - * Determine externals statements for require/import statements by looking - * for requests resolving to the primary public export of the data, kibanaReact, - * amd kibanaUtils plugins. If this module is being imported then rewrite - * the import to access the global `__kbnBundles__` variables and access - * the relavent properties from that global object. - * - * @param bundle - * @param context the directory containing the module which made `request` - * @param request the request for a module from a commonjs require() call or import statement - */ -function dynamicExternals(bundle: Bundle, context: string, request: string) { - // ignore imports that have loaders defined or are not relative seeming - if (request.includes('!') || !request.startsWith('.')) { - return; - } - - // determine the most acurate resolution string we can without running full resolution - const rootRelative = normalizePath( - Path.relative(bundle.sourceRoot, Path.resolve(context, request)) - ); - for (const sharedBundle of SHARED_BUNDLES) { - if ( - rootRelative !== sharedBundle.rootRelativeDir || - `${bundle.type}/${bundle.id}` === `${sharedBundle.type}/${sharedBundle.id}` - ) { - continue; - } - - return `__kbnBundles__['${sharedBundle.type}/${sharedBundle.id}']`; - } - - // import doesn't match a root public import - return undefined; -} - -export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) { - const extensions = ['.js', '.ts', '.tsx', '.json']; - const entryExtension = extensions.find((ext) => - Fs.existsSync(Path.resolve(bundle.contextDir, bundle.entry) + ext) - ); +export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker: WorkerConfig) { + const ENTRY_CREATOR = require.resolve('./entry_point_creator'); const commonConfig: webpack.Configuration = { node: { fs: 'empty' }, context: bundle.contextDir, cache: true, entry: { - [bundle.id]: `${bundle.entry}${entryExtension}`, + [bundle.id]: ENTRY_CREATOR, }, devtool: worker.dist ? false : '#cheap-source-map', @@ -128,27 +60,19 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) { info.absoluteResourcePath )}${info.query}`, jsonpFunction: `${bundle.id}_bundle_jsonpfunction`, - // When the entry point is loaded, assign it's default export - // to a key on the global `__kbnBundles__` object. - library: ['__kbnBundles__', `${bundle.type}/${bundle.id}`], }, optimization: { noEmitOnErrors: true, }, - externals: [ - UiSharedDeps.externals, - function (context, request, cb) { - try { - cb(undefined, dynamicExternals(bundle, context, request)); - } catch (error) { - cb(error, undefined); - } - }, - ], + externals: [UiSharedDeps.externals], - plugins: [new CleanWebpackPlugin(), new DisallowedSyntaxPlugin()], + plugins: [ + new CleanWebpackPlugin(), + new DisallowedSyntaxPlugin(), + new BundleRefsPlugin(bundle, bundleRefs), + ], module: { // no parse rules for a few known large packages which have no require() statements @@ -157,16 +81,33 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) { noParse: [ /[\/\\]node_modules[\/\\]elasticsearch-browser[\/\\]/, /[\/\\]node_modules[\/\\]lodash[\/\\]index\.js$/, - /[\/\\]node_modules[\/\\]vega-lib[\/\\]build[\/\\]vega\.js$/, + /[\/\\]node_modules[\/\\]vega[\/\\]build[\/\\]vega\.js$/, ], rules: [ { - include: [`${Path.resolve(bundle.contextDir, bundle.entry)}${entryExtension}`], - loader: UiSharedDeps.publicPathLoader, - options: { - key: bundle.id, - }, + include: [ENTRY_CREATOR], + use: [ + { + loader: UiSharedDeps.publicPathLoader, + options: { + key: bundle.id, + }, + }, + { + loader: require.resolve('val-loader'), + options: { + entries: bundle.publicDirNames.map((name) => { + const absolute = Path.resolve(bundle.contextDir, name); + const newContext = Path.dirname(ENTRY_CREATOR); + return { + importId: `${bundle.type}/${bundle.id}/${name}`, + relPath: Path.relative(newContext, absolute), + }; + }), + }, + }, + ], }, { test: /\.css$/, @@ -310,7 +251,7 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) { }, resolve: { - extensions, + extensions: ['.js', '.ts', '.tsx', 'json'], mainFields: ['browser', 'main'], alias: { tinymath: require.resolve('tinymath/lib/tinymath.es5.js'), diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 13bddfd0f110a..1600d66ec7558 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -94,7 +94,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _cli__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "run", function() { return _cli__WEBPACK_IMPORTED_MODULE_0__["run"]; }); -/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(497); +/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(498); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["buildProductionProjects"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "prepareExternalProjectDependencies", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["prepareExternalProjectDependencies"]; }); @@ -105,10 +105,10 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_project__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(162); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Project", function() { return _utils_project__WEBPACK_IMPORTED_MODULE_3__["Project"]; }); -/* harmony import */ var _utils_workspaces__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(270); +/* harmony import */ var _utils_workspaces__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(271); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "copyWorkspacePackages", function() { return _utils_workspaces__WEBPACK_IMPORTED_MODULE_4__["copyWorkspacePackages"]; }); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(271); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(272); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getProjectPaths", function() { return _config__WEBPACK_IMPORTED_MODULE_5__["getProjectPaths"]; }); /* @@ -152,7 +152,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5); /* harmony import */ var _kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _commands__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(126); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(489); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(490); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(142); /* * Licensed to Elasticsearch B.V. under one or more contributor @@ -1576,10 +1576,12 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__metadata", function() { return __metadata; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__awaiter", function() { return __awaiter; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__generator", function() { return __generator; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__createBinding", function() { return __createBinding; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__exportStar", function() { return __exportStar; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__values", function() { return __values; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__read", function() { return __read; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__spread", function() { return __spread; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__spreadArrays", function() { return __spreadArrays; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__await", function() { return __await; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__asyncGenerator", function() { return __asyncGenerator; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__asyncDelegator", function() { return __asyncDelegator; }); @@ -1587,19 +1589,21 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__makeTemplateObject", function() { return __makeTemplateObject; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__importStar", function() { return __importStar; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__importDefault", function() { return __importDefault; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__classPrivateFieldGet", function() { return __classPrivateFieldGet; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__classPrivateFieldSet", function() { return __classPrivateFieldSet; }); /*! ***************************************************************************** -Copyright (c) Microsoft Corporation. All rights reserved. -Licensed 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 +Copyright (c) Microsoft Corporation. -THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED -WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, -MERCHANTABLITY OR NON-INFRINGEMENT. +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. -See the Apache Version 2.0 License for specific language governing permissions -and limitations under the License. +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise */ @@ -1632,8 +1636,10 @@ function __rest(s, e) { for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) - t[p[i]] = s[p[i]]; + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } return t; } @@ -1653,10 +1659,11 @@ function __metadata(metadataKey, metadataValue) { } function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } @@ -1689,19 +1696,25 @@ function __generator(thisArg, body) { } } +function __createBinding(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +} + function __exportStar(m, exports) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) exports[p] = m[p]; } function __values(o) { - var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); - return { + if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __read(o, n) { @@ -1727,6 +1740,14 @@ function __spread() { return ar; } +function __spreadArrays() { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; +}; + function __await(v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } @@ -1773,6 +1794,21 @@ function __importStar(mod) { function __importDefault(mod) { return (mod && mod.__esModule) ? mod : { default: mod }; } + +function __classPrivateFieldGet(receiver, privateMap) { + if (!privateMap.has(receiver)) { + throw new TypeError("attempted to get private field on non-instance"); + } + return privateMap.get(receiver); +} + +function __classPrivateFieldSet(receiver, privateMap, value) { + if (!privateMap.has(receiver)) { + throw new TypeError("attempted to set private field on non-instance"); + } + privateMap.set(receiver, value); + return value; +} /***/ }), @@ -8693,9 +8729,9 @@ exports.ToolingLogCollectingWriter = ToolingLogCollectingWriter; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "commands", function() { return commands; }); /* harmony import */ var _bootstrap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(127); -/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(279); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(388); -/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(389); +/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(280); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(389); +/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(390); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -8736,8 +8772,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(142); /* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(143); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(144); -/* harmony import */ var _utils_project_checksums__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(272); -/* harmony import */ var _utils_bootstrap_cache_file__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(278); +/* harmony import */ var _utils_project_checksums__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(273); +/* harmony import */ var _utils_bootstrap_cache_file__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(279); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -10745,7 +10781,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(161); /* harmony import */ var _project__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(162); -/* harmony import */ var _workspaces__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(270); +/* harmony import */ var _workspaces__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(271); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -14356,7 +14392,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(161); /* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(142); /* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(163); -/* harmony import */ var _scripts__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(225); +/* harmony import */ var _scripts__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(226); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } @@ -14603,7 +14639,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isLinkDependency", function() { return isLinkDependency; }); /* harmony import */ var read_pkg__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(164); /* harmony import */ var read_pkg__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(read_pkg__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var write_pkg__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(205); +/* harmony import */ var write_pkg__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(206); /* harmony import */ var write_pkg__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(write_pkg__WEBPACK_IMPORTED_MODULE_1__); /* * Licensed to Elasticsearch B.V. under one or more contributor @@ -16200,7 +16236,7 @@ module.exports = normalize var fixer = __webpack_require__(181) normalize.fixer = fixer -var makeWarning = __webpack_require__(203) +var makeWarning = __webpack_require__(204) var fieldsToFix = ['name','version','description','repository','modules','scripts' ,'files','bin','man','bugs','keywords','readme','homepage','license'] @@ -16245,9 +16281,9 @@ var validateLicense = __webpack_require__(183); var hostedGitInfo = __webpack_require__(188) var isBuiltinModule = __webpack_require__(192).isCore var depTypes = ["dependencies","devDependencies","optionalDependencies"] -var extractDescription = __webpack_require__(201) +var extractDescription = __webpack_require__(202) var url = __webpack_require__(189) -var typos = __webpack_require__(202) +var typos = __webpack_require__(203) var fixer = module.exports = { // default warning function @@ -20197,13 +20233,11 @@ GitHost.prototype.toString = function (opts) { /* 192 */ /***/ (function(module, exports, __webpack_require__) { -var core = __webpack_require__(193); -var async = __webpack_require__(195); -async.core = core; -async.isCore = function isCore(x) { return core[x]; }; -async.sync = __webpack_require__(200); +var async = __webpack_require__(193); +async.core = __webpack_require__(199); +async.isCore = __webpack_require__(198); +async.sync = __webpack_require__(201); -exports = async; module.exports = async; @@ -20211,77 +20245,14 @@ module.exports = async; /* 193 */ /***/ (function(module, exports, __webpack_require__) { -var current = (process.versions && process.versions.node && process.versions.node.split('.')) || []; - -function specifierIncluded(specifier) { - var parts = specifier.split(' '); - var op = parts.length > 1 ? parts[0] : '='; - var versionParts = (parts.length > 1 ? parts[1] : parts[0]).split('.'); - - for (var i = 0; i < 3; ++i) { - var cur = Number(current[i] || 0); - var ver = Number(versionParts[i] || 0); - if (cur === ver) { - continue; // eslint-disable-line no-restricted-syntax, no-continue - } - if (op === '<') { - return cur < ver; - } else if (op === '>=') { - return cur >= ver; - } else { - return false; - } - } - return op === '>='; -} - -function matchesRange(range) { - var specifiers = range.split(/ ?&& ?/); - if (specifiers.length === 0) { return false; } - for (var i = 0; i < specifiers.length; ++i) { - if (!specifierIncluded(specifiers[i])) { return false; } - } - return true; -} - -function versionIncluded(specifierValue) { - if (typeof specifierValue === 'boolean') { return specifierValue; } - if (specifierValue && typeof specifierValue === 'object') { - for (var i = 0; i < specifierValue.length; ++i) { - if (matchesRange(specifierValue[i])) { return true; } - } - return false; - } - return matchesRange(specifierValue); -} - -var data = __webpack_require__(194); - -var core = {}; -for (var mod in data) { // eslint-disable-line no-restricted-syntax - if (Object.prototype.hasOwnProperty.call(data, mod)) { - core[mod] = versionIncluded(data[mod]); - } -} -module.exports = core; - - -/***/ }), -/* 194 */ -/***/ (function(module) { - -module.exports = JSON.parse("{\"assert\":true,\"async_hooks\":\">= 8\",\"buffer_ieee754\":\"< 0.9.7\",\"buffer\":true,\"child_process\":true,\"cluster\":true,\"console\":true,\"constants\":true,\"crypto\":true,\"_debugger\":\"< 8\",\"dgram\":true,\"dns\":true,\"domain\":true,\"events\":true,\"freelist\":\"< 6\",\"fs\":true,\"fs/promises\":\">= 10 && < 10.1\",\"_http_agent\":\">= 0.11.1\",\"_http_client\":\">= 0.11.1\",\"_http_common\":\">= 0.11.1\",\"_http_incoming\":\">= 0.11.1\",\"_http_outgoing\":\">= 0.11.1\",\"_http_server\":\">= 0.11.1\",\"http\":true,\"http2\":\">= 8.8\",\"https\":true,\"inspector\":\">= 8.0.0\",\"_linklist\":\"< 8\",\"module\":true,\"net\":true,\"node-inspect/lib/_inspect\":\">= 7.6.0\",\"node-inspect/lib/internal/inspect_client\":\">= 7.6.0\",\"node-inspect/lib/internal/inspect_repl\":\">= 7.6.0\",\"os\":true,\"path\":true,\"perf_hooks\":\">= 8.5\",\"process\":\">= 1\",\"punycode\":true,\"querystring\":true,\"readline\":true,\"repl\":true,\"smalloc\":\">= 0.11.5 && < 3\",\"_stream_duplex\":\">= 0.9.4\",\"_stream_transform\":\">= 0.9.4\",\"_stream_wrap\":\">= 1.4.1\",\"_stream_passthrough\":\">= 0.9.4\",\"_stream_readable\":\">= 0.9.4\",\"_stream_writable\":\">= 0.9.4\",\"stream\":true,\"string_decoder\":true,\"sys\":true,\"timers\":true,\"_tls_common\":\">= 0.11.13\",\"_tls_legacy\":\">= 0.11.3 && < 10\",\"_tls_wrap\":\">= 0.11.3\",\"tls\":true,\"trace_events\":\">= 10\",\"tty\":true,\"url\":true,\"util\":true,\"v8/tools/arguments\":\">= 10\",\"v8/tools/codemap\":[\">= 4.4.0 && < 5\",\">= 5.2.0\"],\"v8/tools/consarray\":[\">= 4.4.0 && < 5\",\">= 5.2.0\"],\"v8/tools/csvparser\":[\">= 4.4.0 && < 5\",\">= 5.2.0\"],\"v8/tools/logreader\":[\">= 4.4.0 && < 5\",\">= 5.2.0\"],\"v8/tools/profile_view\":[\">= 4.4.0 && < 5\",\">= 5.2.0\"],\"v8/tools/splaytree\":[\">= 4.4.0 && < 5\",\">= 5.2.0\"],\"v8\":\">= 1\",\"vm\":true,\"worker_threads\":\">= 11.7\",\"zlib\":true}"); - -/***/ }), -/* 195 */ -/***/ (function(module, exports, __webpack_require__) { - -var core = __webpack_require__(193); var fs = __webpack_require__(132); var path = __webpack_require__(4); -var caller = __webpack_require__(196); -var nodeModulesPaths = __webpack_require__(197); -var normalizeOptions = __webpack_require__(199); +var caller = __webpack_require__(194); +var nodeModulesPaths = __webpack_require__(195); +var normalizeOptions = __webpack_require__(197); +var isCore = __webpack_require__(198); + +var realpathFS = fs.realpath && typeof fs.realpath.native === 'function' ? fs.realpath.native : fs.realpath; var defaultIsFile = function isFile(file, cb) { fs.stat(file, function (err, stat) { @@ -20293,6 +20264,39 @@ var defaultIsFile = function isFile(file, cb) { }); }; +var defaultIsDir = function isDirectory(dir, cb) { + fs.stat(dir, function (err, stat) { + if (!err) { + return cb(null, stat.isDirectory()); + } + if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false); + return cb(err); + }); +}; + +var defaultRealpath = function realpath(x, cb) { + realpathFS(x, function (realpathErr, realPath) { + if (realpathErr && realpathErr.code !== 'ENOENT') cb(realpathErr); + else cb(null, realpathErr ? x : realPath); + }); +}; + +var maybeRealpath = function maybeRealpath(realpath, x, opts, cb) { + if (opts && opts.preserveSymlinks === false) { + realpath(x, cb); + } else { + cb(null, x); + } +}; + +var getPackageCandidates = function getPackageCandidates(x, start, opts) { + var dirs = nodeModulesPaths(start, opts, x); + for (var i = 0; i < dirs.length; i++) { + dirs[i] = path.join(dirs[i], x); + } + return dirs; +}; + module.exports = function resolve(x, options, callback) { var cb = callback; var opts = options; @@ -20310,7 +20314,10 @@ module.exports = function resolve(x, options, callback) { opts = normalizeOptions(x, opts); var isFile = opts.isFile || defaultIsFile; + var isDirectory = opts.isDirectory || defaultIsDir; var readFile = opts.readFile || fs.readFile; + var realpath = opts.realpath || defaultRealpath; + var packageIterator = opts.packageIterator; var extensions = opts.extensions || ['.js']; var basedir = opts.basedir || path.dirname(caller()); @@ -20321,28 +20328,37 @@ module.exports = function resolve(x, options, callback) { // ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory var absoluteStart = path.resolve(basedir); - if (opts.preserveSymlinks === false) { - fs.realpath(absoluteStart, function (realPathErr, realStart) { - if (realPathErr && realPathErr.code !== 'ENOENT') cb(err); - else init(realPathErr ? absoluteStart : realStart); - }); - } else { - init(absoluteStart); - } + maybeRealpath( + realpath, + absoluteStart, + opts, + function (err, realStart) { + if (err) cb(err); + else init(realStart); + } + ); var res; function init(basedir) { if ((/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/).test(x)) { res = path.resolve(basedir, x); - if (x === '..' || x.slice(-1) === '/') res += '/'; + if (x === '.' || x === '..' || x.slice(-1) === '/') res += '/'; if ((/\/$/).test(x) && res === basedir) { loadAsDirectory(res, opts.package, onfile); } else loadAsFile(res, opts.package, onfile); + } else if (isCore(x)) { + return cb(null, x); } else loadNodeModules(x, basedir, function (err, n, pkg) { if (err) cb(err); - else if (n) cb(null, n, pkg); - else if (core[x]) return cb(null, x); - else { + else if (n) { + return maybeRealpath(realpath, n, opts, function (err, realN) { + if (err) { + cb(err); + } else { + cb(null, realN, pkg); + } + }); + } else { var moduleError = new Error("Cannot find module '" + x + "' from '" + parent + "'"); moduleError.code = 'MODULE_NOT_FOUND'; cb(moduleError); @@ -20355,8 +20371,15 @@ module.exports = function resolve(x, options, callback) { else if (m) cb(null, m, pkg); else loadAsDirectory(res, function (err, d, pkg) { if (err) cb(err); - else if (d) cb(null, d, pkg); - else { + else if (d) { + maybeRealpath(realpath, d, opts, function (err, realD) { + if (err) { + cb(err); + } else { + cb(null, realD, pkg); + } + }); + } else { var moduleError = new Error("Cannot find module '" + x + "' from '" + parent + "'"); moduleError.code = 'MODULE_NOT_FOUND'; cb(moduleError); @@ -20413,19 +20436,22 @@ module.exports = function resolve(x, options, callback) { } if ((/[/\\]node_modules[/\\]*$/).test(dir)) return cb(null); - var pkgfile = path.join(dir, 'package.json'); - isFile(pkgfile, function (err, ex) { - // on err, ex is false - if (!ex) return loadpkg(path.dirname(dir), cb); + maybeRealpath(realpath, dir, opts, function (unwrapErr, pkgdir) { + if (unwrapErr) return loadpkg(path.dirname(dir), cb); + var pkgfile = path.join(pkgdir, 'package.json'); + isFile(pkgfile, function (err, ex) { + // on err, ex is false + if (!ex) return loadpkg(path.dirname(dir), cb); - readFile(pkgfile, function (err, body) { - if (err) cb(err); - try { var pkg = JSON.parse(body); } catch (jsonErr) {} + readFile(pkgfile, function (err, body) { + if (err) cb(err); + try { var pkg = JSON.parse(body); } catch (jsonErr) {} - if (pkg && opts.packageFilter) { - pkg = opts.packageFilter(pkg, pkgfile); - } - cb(null, pkg, dir); + if (pkg && opts.packageFilter) { + pkg = opts.packageFilter(pkg, pkgfile); + } + cb(null, pkg, dir); + }); }); }); } @@ -20438,46 +20464,49 @@ module.exports = function resolve(x, options, callback) { fpkg = opts.package; } - var pkgfile = path.join(x, 'package.json'); - isFile(pkgfile, function (err, ex) { - if (err) return cb(err); - if (!ex) return loadAsFile(path.join(x, 'index'), fpkg, cb); - - readFile(pkgfile, function (err, body) { + maybeRealpath(realpath, x, opts, function (unwrapErr, pkgdir) { + if (unwrapErr) return cb(unwrapErr); + var pkgfile = path.join(pkgdir, 'package.json'); + isFile(pkgfile, function (err, ex) { if (err) return cb(err); - try { - var pkg = JSON.parse(body); - } catch (jsonErr) {} + if (!ex) return loadAsFile(path.join(x, 'index'), fpkg, cb); - if (opts.packageFilter) { - pkg = opts.packageFilter(pkg, pkgfile); - } + readFile(pkgfile, function (err, body) { + if (err) return cb(err); + try { + var pkg = JSON.parse(body); + } catch (jsonErr) {} - if (pkg.main) { - if (typeof pkg.main !== 'string') { - var mainError = new TypeError('package “' + pkg.name + '” `main` must be a string'); - mainError.code = 'INVALID_PACKAGE_MAIN'; - return cb(mainError); - } - if (pkg.main === '.' || pkg.main === './') { - pkg.main = 'index'; + if (pkg && opts.packageFilter) { + pkg = opts.packageFilter(pkg, pkgfile); } - loadAsFile(path.resolve(x, pkg.main), pkg, function (err, m, pkg) { - if (err) return cb(err); - if (m) return cb(null, m, pkg); - if (!pkg) return loadAsFile(path.join(x, 'index'), pkg, cb); - var dir = path.resolve(x, pkg.main); - loadAsDirectory(dir, pkg, function (err, n, pkg) { + if (pkg && pkg.main) { + if (typeof pkg.main !== 'string') { + var mainError = new TypeError('package “' + pkg.name + '” `main` must be a string'); + mainError.code = 'INVALID_PACKAGE_MAIN'; + return cb(mainError); + } + if (pkg.main === '.' || pkg.main === './') { + pkg.main = 'index'; + } + loadAsFile(path.resolve(x, pkg.main), pkg, function (err, m, pkg) { if (err) return cb(err); - if (n) return cb(null, n, pkg); - loadAsFile(path.join(x, 'index'), pkg, cb); + if (m) return cb(null, m, pkg); + if (!pkg) return loadAsFile(path.join(x, 'index'), pkg, cb); + + var dir = path.resolve(x, pkg.main); + loadAsDirectory(dir, pkg, function (err, n, pkg) { + if (err) return cb(err); + if (n) return cb(null, n, pkg); + loadAsFile(path.join(x, 'index'), pkg, cb); + }); }); - }); - return; - } + return; + } - loadAsFile(path.join(x, '/index'), pkg, cb); + loadAsFile(path.join(x, '/index'), pkg, cb); + }); }); }); } @@ -20486,13 +20515,18 @@ module.exports = function resolve(x, options, callback) { if (dirs.length === 0) return cb(null, undefined); var dir = dirs[0]; - var file = path.join(dir, x); - loadAsFile(file, opts.package, onfile); + isDirectory(path.dirname(dir), isdir); + + function isdir(err, isdir) { + if (err) return cb(err); + if (!isdir) return processDirs(cb, dirs.slice(1)); + loadAsFile(dir, opts.package, onfile); + } function onfile(err, m, pkg) { if (err) return cb(err); if (m) return cb(null, m, pkg); - loadAsDirectory(path.join(dir, x), opts.package, ondir); + loadAsDirectory(dir, opts.package, ondir); } function ondir(err, n, pkg) { @@ -20502,13 +20536,17 @@ module.exports = function resolve(x, options, callback) { } } function loadNodeModules(x, start, cb) { - processDirs(cb, nodeModulesPaths(start, opts, x)); + var thunk = function () { return getPackageCandidates(x, start, opts); }; + processDirs( + cb, + packageIterator ? packageIterator(x, start, thunk, opts) : thunk() + ); } }; /***/ }), -/* 196 */ +/* 194 */ /***/ (function(module, exports) { module.exports = function () { @@ -20522,11 +20560,11 @@ module.exports = function () { /***/ }), -/* 197 */ +/* 195 */ /***/ (function(module, exports, __webpack_require__) { var path = __webpack_require__(4); -var parse = path.parse || __webpack_require__(198); +var parse = path.parse || __webpack_require__(196); var getNodeModulesDirs = function getNodeModulesDirs(absoluteStart, modules) { var prefix = '/'; @@ -20545,7 +20583,7 @@ var getNodeModulesDirs = function getNodeModulesDirs(absoluteStart, modules) { return paths.reduce(function (dirs, aPath) { return dirs.concat(modules.map(function (moduleDir) { - return path.join(prefix, aPath, moduleDir); + return path.resolve(prefix, aPath, moduleDir); })); }, []); }; @@ -20570,7 +20608,7 @@ module.exports = function nodeModulesPaths(start, opts, request) { /***/ }), -/* 198 */ +/* 196 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -20670,7 +20708,7 @@ module.exports.win32 = win32.parse; /***/ }), -/* 199 */ +/* 197 */ /***/ (function(module, exports) { module.exports = function (x, opts) { @@ -20685,16 +20723,94 @@ module.exports = function (x, opts) { }; +/***/ }), +/* 198 */ +/***/ (function(module, exports, __webpack_require__) { + +var core = __webpack_require__(199); + +module.exports = function isCore(x) { + return Object.prototype.hasOwnProperty.call(core, x); +}; + + +/***/ }), +/* 199 */ +/***/ (function(module, exports, __webpack_require__) { + +var current = (process.versions && process.versions.node && process.versions.node.split('.')) || []; + +function specifierIncluded(specifier) { + var parts = specifier.split(' '); + var op = parts.length > 1 ? parts[0] : '='; + var versionParts = (parts.length > 1 ? parts[1] : parts[0]).split('.'); + + for (var i = 0; i < 3; ++i) { + var cur = Number(current[i] || 0); + var ver = Number(versionParts[i] || 0); + if (cur === ver) { + continue; // eslint-disable-line no-restricted-syntax, no-continue + } + if (op === '<') { + return cur < ver; + } else if (op === '>=') { + return cur >= ver; + } else { + return false; + } + } + return op === '>='; +} + +function matchesRange(range) { + var specifiers = range.split(/ ?&& ?/); + if (specifiers.length === 0) { return false; } + for (var i = 0; i < specifiers.length; ++i) { + if (!specifierIncluded(specifiers[i])) { return false; } + } + return true; +} + +function versionIncluded(specifierValue) { + if (typeof specifierValue === 'boolean') { return specifierValue; } + if (specifierValue && typeof specifierValue === 'object') { + for (var i = 0; i < specifierValue.length; ++i) { + if (matchesRange(specifierValue[i])) { return true; } + } + return false; + } + return matchesRange(specifierValue); +} + +var data = __webpack_require__(200); + +var core = {}; +for (var mod in data) { // eslint-disable-line no-restricted-syntax + if (Object.prototype.hasOwnProperty.call(data, mod)) { + core[mod] = versionIncluded(data[mod]); + } +} +module.exports = core; + + /***/ }), /* 200 */ +/***/ (function(module) { + +module.exports = JSON.parse("{\"assert\":true,\"async_hooks\":\">= 8\",\"buffer_ieee754\":\"< 0.9.7\",\"buffer\":true,\"child_process\":true,\"cluster\":true,\"console\":true,\"constants\":true,\"crypto\":true,\"_debug_agent\":\">= 1 && < 8\",\"_debugger\":\"< 8\",\"dgram\":true,\"dns\":true,\"domain\":true,\"events\":true,\"freelist\":\"< 6\",\"fs\":true,\"fs/promises\":[\">= 10 && < 10.1\",\">= 14\"],\"_http_agent\":\">= 0.11.1\",\"_http_client\":\">= 0.11.1\",\"_http_common\":\">= 0.11.1\",\"_http_incoming\":\">= 0.11.1\",\"_http_outgoing\":\">= 0.11.1\",\"_http_server\":\">= 0.11.1\",\"http\":true,\"http2\":\">= 8.8\",\"https\":true,\"inspector\":\">= 8.0.0\",\"_linklist\":\"< 8\",\"module\":true,\"net\":true,\"node-inspect/lib/_inspect\":\">= 7.6.0 && < 12\",\"node-inspect/lib/internal/inspect_client\":\">= 7.6.0 && < 12\",\"node-inspect/lib/internal/inspect_repl\":\">= 7.6.0 && < 12\",\"os\":true,\"path\":true,\"perf_hooks\":\">= 8.5\",\"process\":\">= 1\",\"punycode\":true,\"querystring\":true,\"readline\":true,\"repl\":true,\"smalloc\":\">= 0.11.5 && < 3\",\"_stream_duplex\":\">= 0.9.4\",\"_stream_transform\":\">= 0.9.4\",\"_stream_wrap\":\">= 1.4.1\",\"_stream_passthrough\":\">= 0.9.4\",\"_stream_readable\":\">= 0.9.4\",\"_stream_writable\":\">= 0.9.4\",\"stream\":true,\"string_decoder\":true,\"sys\":true,\"timers\":true,\"_tls_common\":\">= 0.11.13\",\"_tls_legacy\":\">= 0.11.3 && < 10\",\"_tls_wrap\":\">= 0.11.3\",\"tls\":true,\"trace_events\":\">= 10\",\"tty\":true,\"url\":true,\"util\":true,\"v8/tools/arguments\":\">= 10 && < 12\",\"v8/tools/codemap\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/consarray\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/csvparser\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/logreader\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/profile_view\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/splaytree\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8\":\">= 1\",\"vm\":true,\"wasi\":\">= 13.4 && < 13.5\",\"worker_threads\":\">= 11.7\",\"zlib\":true}"); + +/***/ }), +/* 201 */ /***/ (function(module, exports, __webpack_require__) { -var core = __webpack_require__(193); +var isCore = __webpack_require__(198); var fs = __webpack_require__(132); var path = __webpack_require__(4); -var caller = __webpack_require__(196); -var nodeModulesPaths = __webpack_require__(197); -var normalizeOptions = __webpack_require__(199); +var caller = __webpack_require__(194); +var nodeModulesPaths = __webpack_require__(195); +var normalizeOptions = __webpack_require__(197); + +var realpathFS = fs.realpathSync && typeof fs.realpathSync.native === 'function' ? fs.realpathSync.native : fs.realpathSync; var defaultIsFile = function isFile(file) { try { @@ -20706,7 +20822,43 @@ var defaultIsFile = function isFile(file) { return stat.isFile() || stat.isFIFO(); }; -module.exports = function (x, options) { +var defaultIsDir = function isDirectory(dir) { + try { + var stat = fs.statSync(dir); + } catch (e) { + if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false; + throw e; + } + return stat.isDirectory(); +}; + +var defaultRealpathSync = function realpathSync(x) { + try { + return realpathFS(x); + } catch (realpathErr) { + if (realpathErr.code !== 'ENOENT') { + throw realpathErr; + } + } + return x; +}; + +var maybeRealpathSync = function maybeRealpathSync(realpathSync, x, opts) { + if (opts && opts.preserveSymlinks === false) { + return realpathSync(x); + } + return x; +}; + +var getPackageCandidates = function getPackageCandidates(x, start, opts) { + var dirs = nodeModulesPaths(start, opts, x); + for (var i = 0; i < dirs.length; i++) { + dirs[i] = path.join(dirs[i], x); + } + return dirs; +}; + +module.exports = function resolveSync(x, options) { if (typeof x !== 'string') { throw new TypeError('Path must be a string.'); } @@ -20714,6 +20866,9 @@ module.exports = function (x, options) { var isFile = opts.isFile || defaultIsFile; var readFileSync = opts.readFileSync || fs.readFileSync; + var isDirectory = opts.isDirectory || defaultIsDir; + var realpathSync = opts.realpathSync || defaultRealpathSync; + var packageIterator = opts.packageIterator; var extensions = opts.extensions || ['.js']; var basedir = opts.basedir || path.dirname(caller()); @@ -20722,30 +20877,20 @@ module.exports = function (x, options) { opts.paths = opts.paths || []; // ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory - var absoluteStart = path.resolve(basedir); - - if (opts.preserveSymlinks === false) { - try { - absoluteStart = fs.realpathSync(absoluteStart); - } catch (realPathErr) { - if (realPathErr.code !== 'ENOENT') { - throw realPathErr; - } - } - } + var absoluteStart = maybeRealpathSync(realpathSync, path.resolve(basedir), opts); if ((/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/).test(x)) { var res = path.resolve(absoluteStart, x); - if (x === '..' || x.slice(-1) === '/') res += '/'; + if (x === '.' || x === '..' || x.slice(-1) === '/') res += '/'; var m = loadAsFileSync(res) || loadAsDirectorySync(res); - if (m) return m; + if (m) return maybeRealpathSync(realpathSync, m, opts); + } else if (isCore(x)) { + return x; } else { var n = loadNodeModulesSync(x, absoluteStart); - if (n) return n; + if (n) return maybeRealpathSync(realpathSync, n, opts); } - if (core[x]) return x; - var err = new Error("Cannot find module '" + x + "' from '" + parent + "'"); err.code = 'MODULE_NOT_FOUND'; throw err; @@ -20780,7 +20925,7 @@ module.exports = function (x, options) { } if ((/[/\\]node_modules[/\\]*$/).test(dir)) return; - var pkgfile = path.join(dir, 'package.json'); + var pkgfile = path.join(maybeRealpathSync(realpathSync, dir, opts), 'package.json'); if (!isFile(pkgfile)) { return loadpkg(path.dirname(dir)); @@ -20793,25 +20938,27 @@ module.exports = function (x, options) { } catch (jsonErr) {} if (pkg && opts.packageFilter) { - pkg = opts.packageFilter(pkg, dir); + // v2 will pass pkgfile + pkg = opts.packageFilter(pkg, /*pkgfile,*/ dir); // eslint-disable-line spaced-comment } return { pkg: pkg, dir: dir }; } function loadAsDirectorySync(x) { - var pkgfile = path.join(x, '/package.json'); + var pkgfile = path.join(maybeRealpathSync(realpathSync, x, opts), '/package.json'); if (isFile(pkgfile)) { try { var body = readFileSync(pkgfile, 'UTF8'); var pkg = JSON.parse(body); } catch (e) {} - if (opts.packageFilter) { - pkg = opts.packageFilter(pkg, x); + if (pkg && opts.packageFilter) { + // v2 will pass pkgfile + pkg = opts.packageFilter(pkg, /*pkgfile,*/ x); // eslint-disable-line spaced-comment } - if (pkg.main) { + if (pkg && pkg.main) { if (typeof pkg.main !== 'string') { var mainError = new TypeError('package “' + pkg.name + '” `main` must be a string'); mainError.code = 'INVALID_PACKAGE_MAIN'; @@ -20833,20 +20980,24 @@ module.exports = function (x, options) { } function loadNodeModulesSync(x, start) { - var dirs = nodeModulesPaths(start, opts, x); + var thunk = function () { return getPackageCandidates(x, start, opts); }; + var dirs = packageIterator ? packageIterator(x, start, thunk, opts) : thunk(); + for (var i = 0; i < dirs.length; i++) { var dir = dirs[i]; - var m = loadAsFileSync(path.join(dir, '/', x)); - if (m) return m; - var n = loadAsDirectorySync(path.join(dir, '/', x)); - if (n) return n; + if (isDirectory(path.dirname(dir))) { + var m = loadAsFileSync(dir); + if (m) return m; + var n = loadAsDirectorySync(dir); + if (n) return n; + } } } }; /***/ }), -/* 201 */ +/* 202 */ /***/ (function(module, exports) { module.exports = extractDescription @@ -20866,17 +21017,17 @@ function extractDescription (d) { /***/ }), -/* 202 */ +/* 203 */ /***/ (function(module) { module.exports = JSON.parse("{\"topLevel\":{\"dependancies\":\"dependencies\",\"dependecies\":\"dependencies\",\"depdenencies\":\"dependencies\",\"devEependencies\":\"devDependencies\",\"depends\":\"dependencies\",\"dev-dependencies\":\"devDependencies\",\"devDependences\":\"devDependencies\",\"devDepenencies\":\"devDependencies\",\"devdependencies\":\"devDependencies\",\"repostitory\":\"repository\",\"repo\":\"repository\",\"prefereGlobal\":\"preferGlobal\",\"hompage\":\"homepage\",\"hampage\":\"homepage\",\"autohr\":\"author\",\"autor\":\"author\",\"contributers\":\"contributors\",\"publicationConfig\":\"publishConfig\",\"script\":\"scripts\"},\"bugs\":{\"web\":\"url\",\"name\":\"url\"},\"script\":{\"server\":\"start\",\"tests\":\"test\"}}"); /***/ }), -/* 203 */ +/* 204 */ /***/ (function(module, exports, __webpack_require__) { var util = __webpack_require__(111) -var messages = __webpack_require__(204) +var messages = __webpack_require__(205) module.exports = function() { var args = Array.prototype.slice.call(arguments, 0) @@ -20901,20 +21052,20 @@ function makeTypoWarning (providedName, probableName, field) { /***/ }), -/* 204 */ +/* 205 */ /***/ (function(module) { module.exports = JSON.parse("{\"repositories\":\"'repositories' (plural) Not supported. Please pick one as the 'repository' field\",\"missingRepository\":\"No repository field.\",\"brokenGitUrl\":\"Probably broken git url: %s\",\"nonObjectScripts\":\"scripts must be an object\",\"nonStringScript\":\"script values must be string commands\",\"nonArrayFiles\":\"Invalid 'files' member\",\"invalidFilename\":\"Invalid filename in 'files' list: %s\",\"nonArrayBundleDependencies\":\"Invalid 'bundleDependencies' list. Must be array of package names\",\"nonStringBundleDependency\":\"Invalid bundleDependencies member: %s\",\"nonDependencyBundleDependency\":\"Non-dependency in bundleDependencies: %s\",\"nonObjectDependencies\":\"%s field must be an object\",\"nonStringDependency\":\"Invalid dependency: %s %s\",\"deprecatedArrayDependencies\":\"specifying %s as array is deprecated\",\"deprecatedModules\":\"modules field is deprecated\",\"nonArrayKeywords\":\"keywords should be an array of strings\",\"nonStringKeyword\":\"keywords should be an array of strings\",\"conflictingName\":\"%s is also the name of a node core module.\",\"nonStringDescription\":\"'description' field should be a string\",\"missingDescription\":\"No description\",\"missingReadme\":\"No README data\",\"missingLicense\":\"No license field.\",\"nonEmailUrlBugsString\":\"Bug string field must be url, email, or {email,url}\",\"nonUrlBugsUrlField\":\"bugs.url field must be a string url. Deleted.\",\"nonEmailBugsEmailField\":\"bugs.email field must be a string email. Deleted.\",\"emptyNormalizedBugs\":\"Normalized value of bugs field is an empty object. Deleted.\",\"nonUrlHomepage\":\"homepage field must be a string url. Deleted.\",\"invalidLicense\":\"license should be a valid SPDX license expression\",\"typo\":\"%s should probably be %s.\"}"); /***/ }), -/* 205 */ +/* 206 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const writeJsonFile = __webpack_require__(206); -const sortKeys = __webpack_require__(220); +const writeJsonFile = __webpack_require__(207); +const sortKeys = __webpack_require__(221); const dependencyKeys = new Set([ 'dependencies', @@ -20979,18 +21130,18 @@ module.exports.sync = (filePath, data, options) => { /***/ }), -/* 206 */ +/* 207 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const fs = __webpack_require__(207); -const writeFileAtomic = __webpack_require__(211); -const sortKeys = __webpack_require__(220); -const makeDir = __webpack_require__(222); -const pify = __webpack_require__(223); -const detectIndent = __webpack_require__(224); +const fs = __webpack_require__(208); +const writeFileAtomic = __webpack_require__(212); +const sortKeys = __webpack_require__(221); +const makeDir = __webpack_require__(223); +const pify = __webpack_require__(224); +const detectIndent = __webpack_require__(225); const init = (fn, filePath, data, options) => { if (!filePath) { @@ -21062,13 +21213,13 @@ module.exports.sync = (filePath, data, options) => { /***/ }), -/* 207 */ +/* 208 */ /***/ (function(module, exports, __webpack_require__) { var fs = __webpack_require__(132) -var polyfills = __webpack_require__(208) -var legacy = __webpack_require__(209) -var clone = __webpack_require__(210) +var polyfills = __webpack_require__(209) +var legacy = __webpack_require__(210) +var clone = __webpack_require__(211) var queue = [] @@ -21347,7 +21498,7 @@ function retry () { /***/ }), -/* 208 */ +/* 209 */ /***/ (function(module, exports, __webpack_require__) { var constants = __webpack_require__(134) @@ -21682,7 +21833,7 @@ function patch (fs) { /***/ }), -/* 209 */ +/* 210 */ /***/ (function(module, exports, __webpack_require__) { var Stream = __webpack_require__(136).Stream @@ -21806,7 +21957,7 @@ function legacy (fs) { /***/ }), -/* 210 */ +/* 211 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -21832,7 +21983,7 @@ function clone (obj) { /***/ }), -/* 211 */ +/* 212 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -21842,9 +21993,9 @@ module.exports.sync = writeFileSync module.exports._getTmpname = getTmpname // for testing module.exports._cleanupOnExit = cleanupOnExit -var fs = __webpack_require__(212) -var MurmurHash3 = __webpack_require__(216) -var onExit = __webpack_require__(217) +var fs = __webpack_require__(213) +var MurmurHash3 = __webpack_require__(217) +var onExit = __webpack_require__(218) var path = __webpack_require__(4) var activeFiles = {} @@ -21852,7 +22003,7 @@ var activeFiles = {} /* istanbul ignore next */ var threadId = (function getId () { try { - var workerThreads = __webpack_require__(219) + var workerThreads = __webpack_require__(220) /// if we are in main thread, this is set to `0` return workerThreads.threadId @@ -22077,12 +22228,12 @@ function writeFileSync (filename, data, options) { /***/ }), -/* 212 */ +/* 213 */ /***/ (function(module, exports, __webpack_require__) { var fs = __webpack_require__(132) -var polyfills = __webpack_require__(213) -var legacy = __webpack_require__(215) +var polyfills = __webpack_require__(214) +var legacy = __webpack_require__(216) var queue = [] var util = __webpack_require__(111) @@ -22106,7 +22257,7 @@ if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) { }) } -module.exports = patch(__webpack_require__(214)) +module.exports = patch(__webpack_require__(215)) if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH) { module.exports = patch(fs) } @@ -22345,10 +22496,10 @@ function retry () { /***/ }), -/* 213 */ +/* 214 */ /***/ (function(module, exports, __webpack_require__) { -var fs = __webpack_require__(214) +var fs = __webpack_require__(215) var constants = __webpack_require__(134) var origCwd = process.cwd @@ -22681,7 +22832,7 @@ function chownErOk (er) { /***/ }), -/* 214 */ +/* 215 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -22709,7 +22860,7 @@ function clone (obj) { /***/ }), -/* 215 */ +/* 216 */ /***/ (function(module, exports, __webpack_require__) { var Stream = __webpack_require__(136).Stream @@ -22833,7 +22984,7 @@ function legacy (fs) { /***/ }), -/* 216 */ +/* 217 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -22975,14 +23126,14 @@ function legacy (fs) { /***/ }), -/* 217 */ +/* 218 */ /***/ (function(module, exports, __webpack_require__) { // Note: since nyc uses this module to output coverage, any lines // that are in the direct sync flow of nyc's outputCoverage are // ignored, since we can never get coverage for them. var assert = __webpack_require__(138) -var signals = __webpack_require__(218) +var signals = __webpack_require__(219) var EE = __webpack_require__(154) /* istanbul ignore if */ @@ -23138,7 +23289,7 @@ function processEmit (ev, arg) { /***/ }), -/* 218 */ +/* 219 */ /***/ (function(module, exports) { // This is not the set of all possible signals. @@ -23197,18 +23348,18 @@ if (process.platform === 'linux') { /***/ }), -/* 219 */ +/* 220 */ /***/ (function(module, exports) { module.exports = require(undefined); /***/ }), -/* 220 */ +/* 221 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const isPlainObj = __webpack_require__(221); +const isPlainObj = __webpack_require__(222); module.exports = (obj, opts) => { if (!isPlainObj(obj)) { @@ -23265,7 +23416,7 @@ module.exports = (obj, opts) => { /***/ }), -/* 221 */ +/* 222 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -23279,14 +23430,14 @@ module.exports = function (x) { /***/ }), -/* 222 */ +/* 223 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(132); const path = __webpack_require__(4); -const pify = __webpack_require__(223); +const pify = __webpack_require__(224); const semver = __webpack_require__(182); const defaults = { @@ -23425,7 +23576,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 223 */ +/* 224 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -23500,7 +23651,7 @@ module.exports = (input, options) => { /***/ }), -/* 224 */ +/* 225 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -23629,7 +23780,7 @@ module.exports = str => { /***/ }), -/* 225 */ +/* 226 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -23638,7 +23789,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runScriptInPackage", function() { return runScriptInPackage; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runScriptInPackageStreaming", function() { return runScriptInPackageStreaming; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "yarnWorkspacesInfo", function() { return yarnWorkspacesInfo; }); -/* harmony import */ var _child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(226); +/* harmony import */ var _child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(227); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -23715,7 +23866,7 @@ async function yarnWorkspacesInfo(directory) { } /***/ }), -/* 226 */ +/* 227 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -23726,9 +23877,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var stream__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(stream__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(112); /* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(227); +/* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(228); /* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(execa__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var strong_log_transformer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(262); +/* harmony import */ var strong_log_transformer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(263); /* harmony import */ var strong_log_transformer__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(strong_log_transformer__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(142); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } @@ -23814,23 +23965,23 @@ function spawnStreaming(command, args, opts, { } /***/ }), -/* 227 */ +/* 228 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const childProcess = __webpack_require__(228); -const crossSpawn = __webpack_require__(229); -const stripFinalNewline = __webpack_require__(242); -const npmRunPath = __webpack_require__(243); -const onetime = __webpack_require__(244); -const makeError = __webpack_require__(246); -const normalizeStdio = __webpack_require__(251); -const {spawnedKill, spawnedCancel, setupTimeout, setExitHandler} = __webpack_require__(252); -const {handleInput, getSpawnedResult, makeAllStream, validateInputSync} = __webpack_require__(253); -const {mergePromise, getSpawnedPromise} = __webpack_require__(260); -const {joinCommand, parseCommand} = __webpack_require__(261); +const childProcess = __webpack_require__(229); +const crossSpawn = __webpack_require__(230); +const stripFinalNewline = __webpack_require__(243); +const npmRunPath = __webpack_require__(244); +const onetime = __webpack_require__(245); +const makeError = __webpack_require__(247); +const normalizeStdio = __webpack_require__(252); +const {spawnedKill, spawnedCancel, setupTimeout, setExitHandler} = __webpack_require__(253); +const {handleInput, getSpawnedResult, makeAllStream, validateInputSync} = __webpack_require__(254); +const {mergePromise, getSpawnedPromise} = __webpack_require__(261); +const {joinCommand, parseCommand} = __webpack_require__(262); const DEFAULT_MAX_BUFFER = 1000 * 1000 * 100; @@ -24077,21 +24228,21 @@ module.exports.node = (scriptPath, args, options = {}) => { /***/ }), -/* 228 */ +/* 229 */ /***/ (function(module, exports) { module.exports = require("child_process"); /***/ }), -/* 229 */ +/* 230 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const cp = __webpack_require__(228); -const parse = __webpack_require__(230); -const enoent = __webpack_require__(241); +const cp = __webpack_require__(229); +const parse = __webpack_require__(231); +const enoent = __webpack_require__(242); function spawn(command, args, options) { // Parse the arguments @@ -24129,16 +24280,16 @@ module.exports._enoent = enoent; /***/ }), -/* 230 */ +/* 231 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const resolveCommand = __webpack_require__(231); -const escape = __webpack_require__(237); -const readShebang = __webpack_require__(238); +const resolveCommand = __webpack_require__(232); +const escape = __webpack_require__(238); +const readShebang = __webpack_require__(239); const isWin = process.platform === 'win32'; const isExecutableRegExp = /\.(?:com|exe)$/i; @@ -24227,15 +24378,15 @@ module.exports = parse; /***/ }), -/* 231 */ +/* 232 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const which = __webpack_require__(232); -const pathKey = __webpack_require__(236)(); +const which = __webpack_require__(233); +const pathKey = __webpack_require__(237)(); function resolveCommandAttempt(parsed, withoutPathExt) { const cwd = process.cwd(); @@ -24285,7 +24436,7 @@ module.exports = resolveCommand; /***/ }), -/* 232 */ +/* 233 */ /***/ (function(module, exports, __webpack_require__) { const isWindows = process.platform === 'win32' || @@ -24294,7 +24445,7 @@ const isWindows = process.platform === 'win32' || const path = __webpack_require__(4) const COLON = isWindows ? ';' : ':' -const isexe = __webpack_require__(233) +const isexe = __webpack_require__(234) const getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: 'ENOENT' }) @@ -24416,15 +24567,15 @@ which.sync = whichSync /***/ }), -/* 233 */ +/* 234 */ /***/ (function(module, exports, __webpack_require__) { var fs = __webpack_require__(132) var core if (process.platform === 'win32' || global.TESTING_WINDOWS) { - core = __webpack_require__(234) -} else { core = __webpack_require__(235) +} else { + core = __webpack_require__(236) } module.exports = isexe @@ -24479,7 +24630,7 @@ function sync (path, options) { /***/ }), -/* 234 */ +/* 235 */ /***/ (function(module, exports, __webpack_require__) { module.exports = isexe @@ -24527,7 +24678,7 @@ function sync (path, options) { /***/ }), -/* 235 */ +/* 236 */ /***/ (function(module, exports, __webpack_require__) { module.exports = isexe @@ -24574,7 +24725,7 @@ function checkMode (stat, options) { /***/ }), -/* 236 */ +/* 237 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -24597,7 +24748,7 @@ module.exports.default = pathKey; /***/ }), -/* 237 */ +/* 238 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -24649,14 +24800,14 @@ module.exports.argument = escapeArgument; /***/ }), -/* 238 */ +/* 239 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(132); -const shebangCommand = __webpack_require__(239); +const shebangCommand = __webpack_require__(240); function readShebang(command) { // Read the first 150 bytes from the file @@ -24679,12 +24830,12 @@ module.exports = readShebang; /***/ }), -/* 239 */ +/* 240 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const shebangRegex = __webpack_require__(240); +const shebangRegex = __webpack_require__(241); module.exports = (string = '') => { const match = string.match(shebangRegex); @@ -24705,7 +24856,7 @@ module.exports = (string = '') => { /***/ }), -/* 240 */ +/* 241 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -24714,7 +24865,7 @@ module.exports = /^#!(.*)/; /***/ }), -/* 241 */ +/* 242 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -24780,7 +24931,7 @@ module.exports = { /***/ }), -/* 242 */ +/* 243 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -24803,13 +24954,13 @@ module.exports = input => { /***/ }), -/* 243 */ +/* 244 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathKey = __webpack_require__(236); +const pathKey = __webpack_require__(237); const npmRunPath = options => { options = { @@ -24857,12 +25008,12 @@ module.exports.env = options => { /***/ }), -/* 244 */ +/* 245 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const mimicFn = __webpack_require__(245); +const mimicFn = __webpack_require__(246); const calledFunctions = new WeakMap(); @@ -24914,7 +25065,7 @@ module.exports.callCount = fn => { /***/ }), -/* 245 */ +/* 246 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -24934,12 +25085,12 @@ module.exports.default = mimicFn; /***/ }), -/* 246 */ +/* 247 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const {signalsByName} = __webpack_require__(247); +const {signalsByName} = __webpack_require__(248); const getErrorPrefix = ({timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled}) => { if (timedOut) { @@ -25027,14 +25178,14 @@ module.exports = makeError; /***/ }), -/* 247 */ +/* 248 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports,"__esModule",{value:true});exports.signalsByNumber=exports.signalsByName=void 0;var _os=__webpack_require__(121); -var _signals=__webpack_require__(248); -var _realtime=__webpack_require__(250); +var _signals=__webpack_require__(249); +var _realtime=__webpack_require__(251); @@ -25104,14 +25255,14 @@ const signalsByNumber=getSignalsByNumber();exports.signalsByNumber=signalsByNumb //# sourceMappingURL=main.js.map /***/ }), -/* 248 */ +/* 249 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports,"__esModule",{value:true});exports.getSignals=void 0;var _os=__webpack_require__(121); -var _core=__webpack_require__(249); -var _realtime=__webpack_require__(250); +var _core=__webpack_require__(250); +var _realtime=__webpack_require__(251); @@ -25145,7 +25296,7 @@ return{name,number,description,supported,action,forced,standard}; //# sourceMappingURL=signals.js.map /***/ }), -/* 249 */ +/* 250 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -25424,7 +25575,7 @@ standard:"other"}];exports.SIGNALS=SIGNALS; //# sourceMappingURL=core.js.map /***/ }), -/* 250 */ +/* 251 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -25449,7 +25600,7 @@ const SIGRTMAX=64;exports.SIGRTMAX=SIGRTMAX; //# sourceMappingURL=realtime.js.map /***/ }), -/* 251 */ +/* 252 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -25508,13 +25659,13 @@ module.exports.node = opts => { /***/ }), -/* 252 */ +/* 253 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const os = __webpack_require__(121); -const onExit = __webpack_require__(217); +const onExit = __webpack_require__(218); const DEFAULT_FORCE_KILL_TIMEOUT = 1000 * 5; @@ -25627,14 +25778,14 @@ module.exports = { /***/ }), -/* 253 */ +/* 254 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const isStream = __webpack_require__(254); -const getStream = __webpack_require__(255); -const mergeStream = __webpack_require__(259); +const isStream = __webpack_require__(255); +const getStream = __webpack_require__(256); +const mergeStream = __webpack_require__(260); // `input` option const handleInput = (spawned, input) => { @@ -25731,7 +25882,7 @@ module.exports = { /***/ }), -/* 254 */ +/* 255 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -25767,13 +25918,13 @@ module.exports = isStream; /***/ }), -/* 255 */ +/* 256 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pump = __webpack_require__(256); -const bufferStream = __webpack_require__(258); +const pump = __webpack_require__(257); +const bufferStream = __webpack_require__(259); class MaxBufferError extends Error { constructor() { @@ -25832,11 +25983,11 @@ module.exports.MaxBufferError = MaxBufferError; /***/ }), -/* 256 */ +/* 257 */ /***/ (function(module, exports, __webpack_require__) { var once = __webpack_require__(160) -var eos = __webpack_require__(257) +var eos = __webpack_require__(258) var fs = __webpack_require__(132) // we only need fs to get the ReadStream and WriteStream prototypes var noop = function () {} @@ -25920,7 +26071,7 @@ module.exports = pump /***/ }), -/* 257 */ +/* 258 */ /***/ (function(module, exports, __webpack_require__) { var once = __webpack_require__(160); @@ -26013,7 +26164,7 @@ module.exports = eos; /***/ }), -/* 258 */ +/* 259 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26072,7 +26223,7 @@ module.exports = options => { /***/ }), -/* 259 */ +/* 260 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26120,7 +26271,7 @@ module.exports = function (/*streams...*/) { /***/ }), -/* 260 */ +/* 261 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26173,7 +26324,7 @@ module.exports = { /***/ }), -/* 261 */ +/* 262 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26218,7 +26369,7 @@ module.exports = { /***/ }), -/* 262 */ +/* 263 */ /***/ (function(module, exports, __webpack_require__) { // Copyright IBM Corp. 2014,2018. All Rights Reserved. @@ -26226,12 +26377,12 @@ module.exports = { // This file is licensed under the Apache License 2.0. // License text available at https://opensource.org/licenses/Apache-2.0 -module.exports = __webpack_require__(263); -module.exports.cli = __webpack_require__(267); +module.exports = __webpack_require__(264); +module.exports.cli = __webpack_require__(268); /***/ }), -/* 263 */ +/* 264 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26246,9 +26397,9 @@ var stream = __webpack_require__(136); var util = __webpack_require__(111); var fs = __webpack_require__(132); -var through = __webpack_require__(264); -var duplexer = __webpack_require__(265); -var StringDecoder = __webpack_require__(266).StringDecoder; +var through = __webpack_require__(265); +var duplexer = __webpack_require__(266); +var StringDecoder = __webpack_require__(267).StringDecoder; module.exports = Logger; @@ -26437,7 +26588,7 @@ function lineMerger(host) { /***/ }), -/* 264 */ +/* 265 */ /***/ (function(module, exports, __webpack_require__) { var Stream = __webpack_require__(136) @@ -26551,7 +26702,7 @@ function through (write, end, opts) { /***/ }), -/* 265 */ +/* 266 */ /***/ (function(module, exports, __webpack_require__) { var Stream = __webpack_require__(136) @@ -26644,13 +26795,13 @@ function duplex(writer, reader) { /***/ }), -/* 266 */ +/* 267 */ /***/ (function(module, exports) { module.exports = require("string_decoder"); /***/ }), -/* 267 */ +/* 268 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26661,11 +26812,11 @@ module.exports = require("string_decoder"); -var minimist = __webpack_require__(268); +var minimist = __webpack_require__(269); var path = __webpack_require__(4); -var Logger = __webpack_require__(263); -var pkg = __webpack_require__(269); +var Logger = __webpack_require__(264); +var pkg = __webpack_require__(270); module.exports = cli; @@ -26719,7 +26870,7 @@ function usage($0, p) { /***/ }), -/* 268 */ +/* 269 */ /***/ (function(module, exports) { module.exports = function (args, opts) { @@ -26961,13 +27112,13 @@ function isNumber (x) { /***/ }), -/* 269 */ +/* 270 */ /***/ (function(module) { module.exports = JSON.parse("{\"name\":\"strong-log-transformer\",\"version\":\"2.1.0\",\"description\":\"Stream transformer that prefixes lines with timestamps and other things.\",\"author\":\"Ryan Graham \",\"license\":\"Apache-2.0\",\"repository\":{\"type\":\"git\",\"url\":\"git://github.com/strongloop/strong-log-transformer\"},\"keywords\":[\"logging\",\"streams\"],\"bugs\":{\"url\":\"https://github.com/strongloop/strong-log-transformer/issues\"},\"homepage\":\"https://github.com/strongloop/strong-log-transformer\",\"directories\":{\"test\":\"test\"},\"bin\":{\"sl-log-transformer\":\"bin/sl-log-transformer.js\"},\"main\":\"index.js\",\"scripts\":{\"test\":\"tap --100 test/test-*\"},\"dependencies\":{\"duplexer\":\"^0.1.1\",\"minimist\":\"^1.2.0\",\"through\":\"^2.3.4\"},\"devDependencies\":{\"tap\":\"^12.0.1\"},\"engines\":{\"node\":\">=4\"}}"); /***/ }), -/* 270 */ +/* 271 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -26980,7 +27131,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(111); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(271); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(272); /* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(129); /* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(163); /* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(144); @@ -27075,7 +27226,7 @@ function packagesFromGlobPattern({ } /***/ }), -/* 271 */ +/* 272 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -27145,7 +27296,7 @@ function getProjectPaths({ } /***/ }), -/* 272 */ +/* 273 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -27153,13 +27304,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getAllChecksums", function() { return getAllChecksums; }); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(132); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(273); +/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(274); /* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(crypto__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(111); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(227); +/* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(228); /* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(execa__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(274); +/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(275); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -27388,19 +27539,19 @@ async function getAllChecksums(kbn, log) { } /***/ }), -/* 273 */ +/* 274 */ /***/ (function(module, exports) { module.exports = require("crypto"); /***/ }), -/* 274 */ +/* 275 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "readYarnLock", function() { return readYarnLock; }); -/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(275); +/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); /* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(129); /* @@ -27444,7 +27595,7 @@ async function readYarnLock(kbn) { } /***/ }), -/* 275 */ +/* 276 */ /***/ (function(module, exports, __webpack_require__) { module.exports = @@ -29003,7 +29154,7 @@ module.exports = invariant; /* 9 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(273); +module.exports = __webpack_require__(274); /***/ }), /* 10 */, @@ -31327,7 +31478,7 @@ function onceStrict (fn) { /* 63 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(276); +module.exports = __webpack_require__(277); /***/ }), /* 64 */, @@ -32265,7 +32416,7 @@ module.exports.win32 = win32; /* 79 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(277); +module.exports = __webpack_require__(278); /***/ }), /* 80 */, @@ -37722,19 +37873,19 @@ module.exports = process && support(supportLevel); /******/ ]); /***/ }), -/* 276 */ +/* 277 */ /***/ (function(module, exports) { module.exports = require("buffer"); /***/ }), -/* 277 */ +/* 278 */ /***/ (function(module, exports) { module.exports = require("tty"); /***/ }), -/* 278 */ +/* 279 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -37831,15 +37982,15 @@ class BootstrapCacheFile { } /***/ }), -/* 279 */ +/* 280 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CleanCommand", function() { return CleanCommand; }); -/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(280); +/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(281); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(372); +/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(373); /* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(ora__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); @@ -37939,21 +38090,21 @@ const CleanCommand = { }; /***/ }), -/* 280 */ +/* 281 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const {promisify} = __webpack_require__(111); const path = __webpack_require__(4); -const globby = __webpack_require__(281); -const isGlob = __webpack_require__(364); -const slash = __webpack_require__(362); +const globby = __webpack_require__(282); +const isGlob = __webpack_require__(365); +const slash = __webpack_require__(363); const gracefulFs = __webpack_require__(131); -const isPathCwd = __webpack_require__(365); -const isPathInside = __webpack_require__(366); -const rimraf = __webpack_require__(367); -const pMap = __webpack_require__(368); +const isPathCwd = __webpack_require__(366); +const isPathInside = __webpack_require__(367); +const rimraf = __webpack_require__(368); +const pMap = __webpack_require__(369); const rimrafP = promisify(rimraf); @@ -38067,19 +38218,19 @@ module.exports.sync = (patterns, {force, dryRun, cwd = process.cwd(), ...options /***/ }), -/* 281 */ +/* 282 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(132); -const arrayUnion = __webpack_require__(282); -const merge2 = __webpack_require__(283); -const glob = __webpack_require__(284); -const fastGlob = __webpack_require__(289); -const dirGlob = __webpack_require__(358); -const gitignore = __webpack_require__(360); -const {FilterStream, UniqueStream} = __webpack_require__(363); +const arrayUnion = __webpack_require__(283); +const merge2 = __webpack_require__(284); +const glob = __webpack_require__(285); +const fastGlob = __webpack_require__(290); +const dirGlob = __webpack_require__(359); +const gitignore = __webpack_require__(361); +const {FilterStream, UniqueStream} = __webpack_require__(364); const DEFAULT_FILTER = () => false; @@ -38252,7 +38403,7 @@ module.exports.gitignore = gitignore; /***/ }), -/* 282 */ +/* 283 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38264,7 +38415,7 @@ module.exports = (...arguments_) => { /***/ }), -/* 283 */ +/* 284 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38415,7 +38566,7 @@ function pauseStreams (streams, options) { /***/ }), -/* 284 */ +/* 285 */ /***/ (function(module, exports, __webpack_require__) { // Approach: @@ -38464,13 +38615,13 @@ var fs = __webpack_require__(132) var rp = __webpack_require__(146) var minimatch = __webpack_require__(148) var Minimatch = minimatch.Minimatch -var inherits = __webpack_require__(285) +var inherits = __webpack_require__(286) var EE = __webpack_require__(154).EventEmitter var path = __webpack_require__(4) var assert = __webpack_require__(138) var isAbsolute = __webpack_require__(155) -var globSync = __webpack_require__(287) -var common = __webpack_require__(288) +var globSync = __webpack_require__(288) +var common = __webpack_require__(289) var alphasort = common.alphasort var alphasorti = common.alphasorti var setopts = common.setopts @@ -39211,7 +39362,7 @@ Glob.prototype._stat2 = function (f, abs, er, stat, cb) { /***/ }), -/* 285 */ +/* 286 */ /***/ (function(module, exports, __webpack_require__) { try { @@ -39221,12 +39372,12 @@ try { module.exports = util.inherits; } catch (e) { /* istanbul ignore next */ - module.exports = __webpack_require__(286); + module.exports = __webpack_require__(287); } /***/ }), -/* 286 */ +/* 287 */ /***/ (function(module, exports) { if (typeof Object.create === 'function') { @@ -39259,7 +39410,7 @@ if (typeof Object.create === 'function') { /***/ }), -/* 287 */ +/* 288 */ /***/ (function(module, exports, __webpack_require__) { module.exports = globSync @@ -39269,12 +39420,12 @@ var fs = __webpack_require__(132) var rp = __webpack_require__(146) var minimatch = __webpack_require__(148) var Minimatch = minimatch.Minimatch -var Glob = __webpack_require__(284).Glob +var Glob = __webpack_require__(285).Glob var util = __webpack_require__(111) var path = __webpack_require__(4) var assert = __webpack_require__(138) var isAbsolute = __webpack_require__(155) -var common = __webpack_require__(288) +var common = __webpack_require__(289) var alphasort = common.alphasort var alphasorti = common.alphasorti var setopts = common.setopts @@ -39751,7 +39902,7 @@ GlobSync.prototype._makeAbs = function (f) { /***/ }), -/* 288 */ +/* 289 */ /***/ (function(module, exports, __webpack_require__) { exports.alphasort = alphasort @@ -39997,17 +40148,17 @@ function childrenIgnored (self, path) { /***/ }), -/* 289 */ +/* 290 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const taskManager = __webpack_require__(290); -const async_1 = __webpack_require__(319); -const stream_1 = __webpack_require__(354); -const sync_1 = __webpack_require__(355); -const settings_1 = __webpack_require__(357); -const utils = __webpack_require__(291); +const taskManager = __webpack_require__(291); +const async_1 = __webpack_require__(320); +const stream_1 = __webpack_require__(355); +const sync_1 = __webpack_require__(356); +const settings_1 = __webpack_require__(358); +const utils = __webpack_require__(292); async function FastGlob(source, options) { assertPatternsInput(source); const works = getWorks(source, async_1.default, options); @@ -40071,13 +40222,13 @@ module.exports = FastGlob; /***/ }), -/* 290 */ +/* 291 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(291); +const utils = __webpack_require__(292); function generate(patterns, settings) { const positivePatterns = getPositivePatterns(patterns); const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); @@ -40142,30 +40293,30 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 291 */ +/* 292 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const array = __webpack_require__(292); +const array = __webpack_require__(293); exports.array = array; -const errno = __webpack_require__(293); +const errno = __webpack_require__(294); exports.errno = errno; -const fs = __webpack_require__(294); +const fs = __webpack_require__(295); exports.fs = fs; -const path = __webpack_require__(295); +const path = __webpack_require__(296); exports.path = path; -const pattern = __webpack_require__(296); +const pattern = __webpack_require__(297); exports.pattern = pattern; -const stream = __webpack_require__(317); +const stream = __webpack_require__(318); exports.stream = stream; -const string = __webpack_require__(318); +const string = __webpack_require__(319); exports.string = string; /***/ }), -/* 292 */ +/* 293 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40193,7 +40344,7 @@ exports.splitWhen = splitWhen; /***/ }), -/* 293 */ +/* 294 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40206,7 +40357,7 @@ exports.isEnoentCodeError = isEnoentCodeError; /***/ }), -/* 294 */ +/* 295 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40231,7 +40382,7 @@ exports.createDirentFromStats = createDirentFromStats; /***/ }), -/* 295 */ +/* 296 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40270,16 +40421,16 @@ exports.removeLeadingDotSegment = removeLeadingDotSegment; /***/ }), -/* 296 */ +/* 297 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const globParent = __webpack_require__(297); -const micromatch = __webpack_require__(300); -const picomatch = __webpack_require__(311); +const globParent = __webpack_require__(298); +const micromatch = __webpack_require__(301); +const picomatch = __webpack_require__(312); const GLOBSTAR = '**'; const ESCAPE_SYMBOL = '\\'; const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; @@ -40389,13 +40540,13 @@ exports.matchAny = matchAny; /***/ }), -/* 297 */ +/* 298 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isGlob = __webpack_require__(298); +var isGlob = __webpack_require__(299); var pathPosixDirname = __webpack_require__(4).posix.dirname; var isWin32 = __webpack_require__(121).platform() === 'win32'; @@ -40437,7 +40588,7 @@ module.exports = function globParent(str, opts) { /***/ }), -/* 298 */ +/* 299 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -40447,7 +40598,7 @@ module.exports = function globParent(str, opts) { * Released under the MIT License. */ -var isExtglob = __webpack_require__(299); +var isExtglob = __webpack_require__(300); var chars = { '{': '}', '(': ')', '[': ']'}; var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; @@ -40491,7 +40642,7 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 299 */ +/* 300 */ /***/ (function(module, exports) { /*! @@ -40517,16 +40668,16 @@ module.exports = function isExtglob(str) { /***/ }), -/* 300 */ +/* 301 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const util = __webpack_require__(111); -const braces = __webpack_require__(301); -const picomatch = __webpack_require__(311); -const utils = __webpack_require__(314); +const braces = __webpack_require__(302); +const picomatch = __webpack_require__(312); +const utils = __webpack_require__(315); const isEmptyString = val => typeof val === 'string' && (val === '' || val === './'); /** @@ -40991,16 +41142,16 @@ module.exports = micromatch; /***/ }), -/* 301 */ +/* 302 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stringify = __webpack_require__(302); -const compile = __webpack_require__(304); -const expand = __webpack_require__(308); -const parse = __webpack_require__(309); +const stringify = __webpack_require__(303); +const compile = __webpack_require__(305); +const expand = __webpack_require__(309); +const parse = __webpack_require__(310); /** * Expand the given pattern or create a regex-compatible string. @@ -41168,13 +41319,13 @@ module.exports = braces; /***/ }), -/* 302 */ +/* 303 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const utils = __webpack_require__(303); +const utils = __webpack_require__(304); module.exports = (ast, options = {}) => { let stringify = (node, parent = {}) => { @@ -41207,7 +41358,7 @@ module.exports = (ast, options = {}) => { /***/ }), -/* 303 */ +/* 304 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41326,14 +41477,14 @@ exports.flatten = (...args) => { /***/ }), -/* 304 */ +/* 305 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fill = __webpack_require__(305); -const utils = __webpack_require__(303); +const fill = __webpack_require__(306); +const utils = __webpack_require__(304); const compile = (ast, options = {}) => { let walk = (node, parent = {}) => { @@ -41390,7 +41541,7 @@ module.exports = compile; /***/ }), -/* 305 */ +/* 306 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41404,7 +41555,7 @@ module.exports = compile; const util = __webpack_require__(111); -const toRegexRange = __webpack_require__(306); +const toRegexRange = __webpack_require__(307); const isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); @@ -41646,7 +41797,7 @@ module.exports = fill; /***/ }), -/* 306 */ +/* 307 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41659,7 +41810,7 @@ module.exports = fill; -const isNumber = __webpack_require__(307); +const isNumber = __webpack_require__(308); const toRegexRange = (min, max, options) => { if (isNumber(min) === false) { @@ -41941,7 +42092,7 @@ module.exports = toRegexRange; /***/ }), -/* 307 */ +/* 308 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41966,15 +42117,15 @@ module.exports = function(num) { /***/ }), -/* 308 */ +/* 309 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fill = __webpack_require__(305); -const stringify = __webpack_require__(302); -const utils = __webpack_require__(303); +const fill = __webpack_require__(306); +const stringify = __webpack_require__(303); +const utils = __webpack_require__(304); const append = (queue = '', stash = '', enclose = false) => { let result = []; @@ -42086,13 +42237,13 @@ module.exports = expand; /***/ }), -/* 309 */ +/* 310 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stringify = __webpack_require__(302); +const stringify = __webpack_require__(303); /** * Constants @@ -42114,7 +42265,7 @@ const { CHAR_SINGLE_QUOTE, /* ' */ CHAR_NO_BREAK_SPACE, CHAR_ZERO_WIDTH_NOBREAK_SPACE -} = __webpack_require__(310); +} = __webpack_require__(311); /** * parse @@ -42426,7 +42577,7 @@ module.exports = parse; /***/ }), -/* 310 */ +/* 311 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -42490,27 +42641,27 @@ module.exports = { /***/ }), -/* 311 */ +/* 312 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = __webpack_require__(312); +module.exports = __webpack_require__(313); /***/ }), -/* 312 */ +/* 313 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const scan = __webpack_require__(313); -const parse = __webpack_require__(316); -const utils = __webpack_require__(314); -const constants = __webpack_require__(315); +const scan = __webpack_require__(314); +const parse = __webpack_require__(317); +const utils = __webpack_require__(315); +const constants = __webpack_require__(316); const isObject = val => val && typeof val === 'object' && !Array.isArray(val); /** @@ -42846,13 +42997,13 @@ module.exports = picomatch; /***/ }), -/* 313 */ +/* 314 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const utils = __webpack_require__(314); +const utils = __webpack_require__(315); const { CHAR_ASTERISK, /* * */ CHAR_AT, /* @ */ @@ -42869,7 +43020,7 @@ const { CHAR_RIGHT_CURLY_BRACE, /* } */ CHAR_RIGHT_PARENTHESES, /* ) */ CHAR_RIGHT_SQUARE_BRACKET /* ] */ -} = __webpack_require__(315); +} = __webpack_require__(316); const isPathSeparator = code => { return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; @@ -43236,7 +43387,7 @@ module.exports = scan; /***/ }), -/* 314 */ +/* 315 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43249,7 +43400,7 @@ const { REGEX_REMOVE_BACKSLASH, REGEX_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_GLOBAL -} = __webpack_require__(315); +} = __webpack_require__(316); exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str); @@ -43307,7 +43458,7 @@ exports.wrapOutput = (input, state = {}, options = {}) => { /***/ }), -/* 315 */ +/* 316 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43493,14 +43644,14 @@ module.exports = { /***/ }), -/* 316 */ +/* 317 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const constants = __webpack_require__(315); -const utils = __webpack_require__(314); +const constants = __webpack_require__(316); +const utils = __webpack_require__(315); /** * Constants @@ -44578,13 +44729,13 @@ module.exports = parse; /***/ }), -/* 317 */ +/* 318 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const merge2 = __webpack_require__(283); +const merge2 = __webpack_require__(284); function merge(streams) { const mergedStream = merge2(streams); streams.forEach((stream) => { @@ -44601,7 +44752,7 @@ function propagateCloseEventToSources(streams) { /***/ }), -/* 318 */ +/* 319 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44618,14 +44769,14 @@ exports.isEmpty = isEmpty; /***/ }), -/* 319 */ +/* 320 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(320); -const provider_1 = __webpack_require__(347); +const stream_1 = __webpack_require__(321); +const provider_1 = __webpack_require__(348); class ProviderAsync extends provider_1.default { constructor() { super(...arguments); @@ -44653,16 +44804,16 @@ exports.default = ProviderAsync; /***/ }), -/* 320 */ +/* 321 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(136); -const fsStat = __webpack_require__(321); -const fsWalk = __webpack_require__(326); -const reader_1 = __webpack_require__(346); +const fsStat = __webpack_require__(322); +const fsWalk = __webpack_require__(327); +const reader_1 = __webpack_require__(347); class ReaderStream extends reader_1.default { constructor() { super(...arguments); @@ -44715,15 +44866,15 @@ exports.default = ReaderStream; /***/ }), -/* 321 */ +/* 322 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async = __webpack_require__(322); -const sync = __webpack_require__(323); -const settings_1 = __webpack_require__(324); +const async = __webpack_require__(323); +const sync = __webpack_require__(324); +const settings_1 = __webpack_require__(325); exports.Settings = settings_1.default; function stat(path, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -44746,7 +44897,7 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 322 */ +/* 323 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44784,7 +44935,7 @@ function callSuccessCallback(callback, result) { /***/ }), -/* 323 */ +/* 324 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44813,13 +44964,13 @@ exports.read = read; /***/ }), -/* 324 */ +/* 325 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(325); +const fs = __webpack_require__(326); class Settings { constructor(_options = {}) { this._options = _options; @@ -44836,7 +44987,7 @@ exports.default = Settings; /***/ }), -/* 325 */ +/* 326 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44859,16 +45010,16 @@ exports.createFileSystemAdapter = createFileSystemAdapter; /***/ }), -/* 326 */ +/* 327 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(327); -const stream_1 = __webpack_require__(342); -const sync_1 = __webpack_require__(343); -const settings_1 = __webpack_require__(345); +const async_1 = __webpack_require__(328); +const stream_1 = __webpack_require__(343); +const sync_1 = __webpack_require__(344); +const settings_1 = __webpack_require__(346); exports.Settings = settings_1.default; function walk(directory, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -44898,13 +45049,13 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 327 */ +/* 328 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(328); +const async_1 = __webpack_require__(329); class AsyncProvider { constructor(_root, _settings) { this._root = _root; @@ -44935,17 +45086,17 @@ function callSuccessCallback(callback, entries) { /***/ }), -/* 328 */ +/* 329 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = __webpack_require__(154); -const fsScandir = __webpack_require__(329); -const fastq = __webpack_require__(338); -const common = __webpack_require__(340); -const reader_1 = __webpack_require__(341); +const fsScandir = __webpack_require__(330); +const fastq = __webpack_require__(339); +const common = __webpack_require__(341); +const reader_1 = __webpack_require__(342); class AsyncReader extends reader_1.default { constructor(_root, _settings) { super(_root, _settings); @@ -45035,15 +45186,15 @@ exports.default = AsyncReader; /***/ }), -/* 329 */ +/* 330 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async = __webpack_require__(330); -const sync = __webpack_require__(335); -const settings_1 = __webpack_require__(336); +const async = __webpack_require__(331); +const sync = __webpack_require__(336); +const settings_1 = __webpack_require__(337); exports.Settings = settings_1.default; function scandir(path, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -45066,16 +45217,16 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 330 */ +/* 331 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(321); -const rpl = __webpack_require__(331); -const constants_1 = __webpack_require__(332); -const utils = __webpack_require__(333); +const fsStat = __webpack_require__(322); +const rpl = __webpack_require__(332); +const constants_1 = __webpack_require__(333); +const utils = __webpack_require__(334); function read(directory, settings, callback) { if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { return readdirWithFileTypes(directory, settings, callback); @@ -45163,7 +45314,7 @@ function callSuccessCallback(callback, result) { /***/ }), -/* 331 */ +/* 332 */ /***/ (function(module, exports) { module.exports = runParallel @@ -45217,7 +45368,7 @@ function runParallel (tasks, cb) { /***/ }), -/* 332 */ +/* 333 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45237,18 +45388,18 @@ exports.IS_SUPPORT_READDIR_WITH_FILE_TYPES = IS_MATCHED_BY_MAJOR || IS_MATCHED_B /***/ }), -/* 333 */ +/* 334 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(334); +const fs = __webpack_require__(335); exports.fs = fs; /***/ }), -/* 334 */ +/* 335 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45273,15 +45424,15 @@ exports.createDirentFromStats = createDirentFromStats; /***/ }), -/* 335 */ +/* 336 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(321); -const constants_1 = __webpack_require__(332); -const utils = __webpack_require__(333); +const fsStat = __webpack_require__(322); +const constants_1 = __webpack_require__(333); +const utils = __webpack_require__(334); function read(directory, settings) { if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { return readdirWithFileTypes(directory, settings); @@ -45332,15 +45483,15 @@ exports.readdir = readdir; /***/ }), -/* 336 */ +/* 337 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsStat = __webpack_require__(321); -const fs = __webpack_require__(337); +const fsStat = __webpack_require__(322); +const fs = __webpack_require__(338); class Settings { constructor(_options = {}) { this._options = _options; @@ -45363,7 +45514,7 @@ exports.default = Settings; /***/ }), -/* 337 */ +/* 338 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45388,13 +45539,13 @@ exports.createFileSystemAdapter = createFileSystemAdapter; /***/ }), -/* 338 */ +/* 339 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var reusify = __webpack_require__(339) +var reusify = __webpack_require__(340) function fastqueue (context, worker, concurrency) { if (typeof context === 'function') { @@ -45568,7 +45719,7 @@ module.exports = fastqueue /***/ }), -/* 339 */ +/* 340 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45608,7 +45759,7 @@ module.exports = reusify /***/ }), -/* 340 */ +/* 341 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45639,13 +45790,13 @@ exports.joinPathSegments = joinPathSegments; /***/ }), -/* 341 */ +/* 342 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const common = __webpack_require__(340); +const common = __webpack_require__(341); class Reader { constructor(_root, _settings) { this._root = _root; @@ -45657,14 +45808,14 @@ exports.default = Reader; /***/ }), -/* 342 */ +/* 343 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(136); -const async_1 = __webpack_require__(328); +const async_1 = __webpack_require__(329); class StreamProvider { constructor(_root, _settings) { this._root = _root; @@ -45694,13 +45845,13 @@ exports.default = StreamProvider; /***/ }), -/* 343 */ +/* 344 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(344); +const sync_1 = __webpack_require__(345); class SyncProvider { constructor(_root, _settings) { this._root = _root; @@ -45715,15 +45866,15 @@ exports.default = SyncProvider; /***/ }), -/* 344 */ +/* 345 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsScandir = __webpack_require__(329); -const common = __webpack_require__(340); -const reader_1 = __webpack_require__(341); +const fsScandir = __webpack_require__(330); +const common = __webpack_require__(341); +const reader_1 = __webpack_require__(342); class SyncReader extends reader_1.default { constructor() { super(...arguments); @@ -45781,14 +45932,14 @@ exports.default = SyncReader; /***/ }), -/* 345 */ +/* 346 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsScandir = __webpack_require__(329); +const fsScandir = __webpack_require__(330); class Settings { constructor(_options = {}) { this._options = _options; @@ -45814,15 +45965,15 @@ exports.default = Settings; /***/ }), -/* 346 */ +/* 347 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsStat = __webpack_require__(321); -const utils = __webpack_require__(291); +const fsStat = __webpack_require__(322); +const utils = __webpack_require__(292); class Reader { constructor(_settings) { this._settings = _settings; @@ -45854,17 +46005,17 @@ exports.default = Reader; /***/ }), -/* 347 */ +/* 348 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const deep_1 = __webpack_require__(348); -const entry_1 = __webpack_require__(351); -const error_1 = __webpack_require__(352); -const entry_2 = __webpack_require__(353); +const deep_1 = __webpack_require__(349); +const entry_1 = __webpack_require__(352); +const error_1 = __webpack_require__(353); +const entry_2 = __webpack_require__(354); class Provider { constructor(_settings) { this._settings = _settings; @@ -45909,14 +46060,14 @@ exports.default = Provider; /***/ }), -/* 348 */ +/* 349 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(291); -const partial_1 = __webpack_require__(349); +const utils = __webpack_require__(292); +const partial_1 = __webpack_require__(350); class DeepFilter { constructor(_settings, _micromatchOptions) { this._settings = _settings; @@ -45970,13 +46121,13 @@ exports.default = DeepFilter; /***/ }), -/* 349 */ +/* 350 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const matcher_1 = __webpack_require__(350); +const matcher_1 = __webpack_require__(351); class PartialMatcher extends matcher_1.default { match(filepath) { const parts = filepath.split('/'); @@ -46015,13 +46166,13 @@ exports.default = PartialMatcher; /***/ }), -/* 350 */ +/* 351 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(291); +const utils = __webpack_require__(292); class Matcher { constructor(_patterns, _settings, _micromatchOptions) { this._patterns = _patterns; @@ -46072,13 +46223,13 @@ exports.default = Matcher; /***/ }), -/* 351 */ +/* 352 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(291); +const utils = __webpack_require__(292); class EntryFilter { constructor(_settings, _micromatchOptions) { this._settings = _settings; @@ -46134,13 +46285,13 @@ exports.default = EntryFilter; /***/ }), -/* 352 */ +/* 353 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(291); +const utils = __webpack_require__(292); class ErrorFilter { constructor(_settings) { this._settings = _settings; @@ -46156,13 +46307,13 @@ exports.default = ErrorFilter; /***/ }), -/* 353 */ +/* 354 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(291); +const utils = __webpack_require__(292); class EntryTransformer { constructor(_settings) { this._settings = _settings; @@ -46189,15 +46340,15 @@ exports.default = EntryTransformer; /***/ }), -/* 354 */ +/* 355 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(136); -const stream_2 = __webpack_require__(320); -const provider_1 = __webpack_require__(347); +const stream_2 = __webpack_require__(321); +const provider_1 = __webpack_require__(348); class ProviderStream extends provider_1.default { constructor() { super(...arguments); @@ -46227,14 +46378,14 @@ exports.default = ProviderStream; /***/ }), -/* 355 */ +/* 356 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(356); -const provider_1 = __webpack_require__(347); +const sync_1 = __webpack_require__(357); +const provider_1 = __webpack_require__(348); class ProviderSync extends provider_1.default { constructor() { super(...arguments); @@ -46257,15 +46408,15 @@ exports.default = ProviderSync; /***/ }), -/* 356 */ +/* 357 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(321); -const fsWalk = __webpack_require__(326); -const reader_1 = __webpack_require__(346); +const fsStat = __webpack_require__(322); +const fsWalk = __webpack_require__(327); +const reader_1 = __webpack_require__(347); class ReaderSync extends reader_1.default { constructor() { super(...arguments); @@ -46307,7 +46458,7 @@ exports.default = ReaderSync; /***/ }), -/* 357 */ +/* 358 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46366,13 +46517,13 @@ exports.default = Settings; /***/ }), -/* 358 */ +/* 359 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathType = __webpack_require__(359); +const pathType = __webpack_require__(360); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -46448,7 +46599,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 359 */ +/* 360 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46498,7 +46649,7 @@ exports.isSymlinkSync = isTypeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 360 */ +/* 361 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46506,9 +46657,9 @@ exports.isSymlinkSync = isTypeSync.bind(null, 'lstatSync', 'isSymbolicLink'); const {promisify} = __webpack_require__(111); const fs = __webpack_require__(132); const path = __webpack_require__(4); -const fastGlob = __webpack_require__(289); -const gitIgnore = __webpack_require__(361); -const slash = __webpack_require__(362); +const fastGlob = __webpack_require__(290); +const gitIgnore = __webpack_require__(362); +const slash = __webpack_require__(363); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -46622,7 +46773,7 @@ module.exports.sync = options => { /***/ }), -/* 361 */ +/* 362 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -47225,7 +47376,7 @@ if ( /***/ }), -/* 362 */ +/* 363 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -47243,7 +47394,7 @@ module.exports = path => { /***/ }), -/* 363 */ +/* 364 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -47296,7 +47447,7 @@ module.exports = { /***/ }), -/* 364 */ +/* 365 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -47306,7 +47457,7 @@ module.exports = { * Released under the MIT License. */ -var isExtglob = __webpack_require__(299); +var isExtglob = __webpack_require__(300); var chars = { '{': '}', '(': ')', '[': ']'}; var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; @@ -47350,7 +47501,7 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 365 */ +/* 366 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -47372,7 +47523,7 @@ module.exports = path_ => { /***/ }), -/* 366 */ +/* 367 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -47400,7 +47551,7 @@ module.exports = (childPath, parentPath) => { /***/ }), -/* 367 */ +/* 368 */ /***/ (function(module, exports, __webpack_require__) { const assert = __webpack_require__(138) @@ -47408,7 +47559,7 @@ const path = __webpack_require__(4) const fs = __webpack_require__(132) let glob = undefined try { - glob = __webpack_require__(284) + glob = __webpack_require__(285) } catch (_err) { // treat glob as optional. } @@ -47774,12 +47925,12 @@ rimraf.sync = rimrafSync /***/ }), -/* 368 */ +/* 369 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const AggregateError = __webpack_require__(369); +const AggregateError = __webpack_require__(370); module.exports = async ( iterable, @@ -47862,13 +48013,13 @@ module.exports = async ( /***/ }), -/* 369 */ +/* 370 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const indentString = __webpack_require__(370); -const cleanStack = __webpack_require__(371); +const indentString = __webpack_require__(371); +const cleanStack = __webpack_require__(372); const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); @@ -47916,7 +48067,7 @@ module.exports = AggregateError; /***/ }), -/* 370 */ +/* 371 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -47958,7 +48109,7 @@ module.exports = (string, count = 1, options) => { /***/ }), -/* 371 */ +/* 372 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48005,15 +48156,15 @@ module.exports = (stack, options) => { /***/ }), -/* 372 */ +/* 373 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const chalk = __webpack_require__(373); -const cliCursor = __webpack_require__(377); -const cliSpinners = __webpack_require__(381); -const logSymbols = __webpack_require__(383); +const chalk = __webpack_require__(374); +const cliCursor = __webpack_require__(378); +const cliSpinners = __webpack_require__(382); +const logSymbols = __webpack_require__(384); class Ora { constructor(options) { @@ -48160,16 +48311,16 @@ module.exports.promise = (action, options) => { /***/ }), -/* 373 */ +/* 374 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const escapeStringRegexp = __webpack_require__(113); -const ansiStyles = __webpack_require__(374); -const stdoutColor = __webpack_require__(375).stdout; +const ansiStyles = __webpack_require__(375); +const stdoutColor = __webpack_require__(376).stdout; -const template = __webpack_require__(376); +const template = __webpack_require__(377); const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); @@ -48395,7 +48546,7 @@ module.exports.default = module.exports; // For TypeScript /***/ }), -/* 374 */ +/* 375 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48568,7 +48719,7 @@ Object.defineProperty(module, 'exports', { /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(115)(module))) /***/ }), -/* 375 */ +/* 376 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48710,7 +48861,7 @@ module.exports = { /***/ }), -/* 376 */ +/* 377 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48845,12 +48996,12 @@ module.exports = (chalk, tmp) => { /***/ }), -/* 377 */ +/* 378 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const restoreCursor = __webpack_require__(378); +const restoreCursor = __webpack_require__(379); let hidden = false; @@ -48891,13 +49042,13 @@ exports.toggle = (force, stream) => { /***/ }), -/* 378 */ +/* 379 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const onetime = __webpack_require__(379); -const signalExit = __webpack_require__(217); +const onetime = __webpack_require__(380); +const signalExit = __webpack_require__(218); module.exports = onetime(() => { signalExit(() => { @@ -48907,12 +49058,12 @@ module.exports = onetime(() => { /***/ }), -/* 379 */ +/* 380 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const mimicFn = __webpack_require__(380); +const mimicFn = __webpack_require__(381); module.exports = (fn, opts) => { // TODO: Remove this in v3 @@ -48953,7 +49104,7 @@ module.exports = (fn, opts) => { /***/ }), -/* 380 */ +/* 381 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48969,27 +49120,27 @@ module.exports = (to, from) => { /***/ }), -/* 381 */ +/* 382 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = __webpack_require__(382); +module.exports = __webpack_require__(383); /***/ }), -/* 382 */ +/* 383 */ /***/ (function(module) { module.exports = JSON.parse("{\"dots\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠹\",\"⠸\",\"⠼\",\"⠴\",\"⠦\",\"⠧\",\"⠇\",\"⠏\"]},\"dots2\":{\"interval\":80,\"frames\":[\"⣾\",\"⣽\",\"⣻\",\"⢿\",\"⡿\",\"⣟\",\"⣯\",\"⣷\"]},\"dots3\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠚\",\"⠞\",\"⠖\",\"⠦\",\"⠴\",\"⠲\",\"⠳\",\"⠓\"]},\"dots4\":{\"interval\":80,\"frames\":[\"⠄\",\"⠆\",\"⠇\",\"⠋\",\"⠙\",\"⠸\",\"⠰\",\"⠠\",\"⠰\",\"⠸\",\"⠙\",\"⠋\",\"⠇\",\"⠆\"]},\"dots5\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\"]},\"dots6\":{\"interval\":80,\"frames\":[\"⠁\",\"⠉\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠤\",\"⠄\",\"⠄\",\"⠤\",\"⠴\",\"⠲\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠚\",\"⠙\",\"⠉\",\"⠁\"]},\"dots7\":{\"interval\":80,\"frames\":[\"⠈\",\"⠉\",\"⠋\",\"⠓\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠖\",\"⠦\",\"⠤\",\"⠠\",\"⠠\",\"⠤\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\",\"⠉\",\"⠈\"]},\"dots8\":{\"interval\":80,\"frames\":[\"⠁\",\"⠁\",\"⠉\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠤\",\"⠄\",\"⠄\",\"⠤\",\"⠠\",\"⠠\",\"⠤\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\",\"⠉\",\"⠈\",\"⠈\"]},\"dots9\":{\"interval\":80,\"frames\":[\"⢹\",\"⢺\",\"⢼\",\"⣸\",\"⣇\",\"⡧\",\"⡗\",\"⡏\"]},\"dots10\":{\"interval\":80,\"frames\":[\"⢄\",\"⢂\",\"⢁\",\"⡁\",\"⡈\",\"⡐\",\"⡠\"]},\"dots11\":{\"interval\":100,\"frames\":[\"⠁\",\"⠂\",\"⠄\",\"⡀\",\"⢀\",\"⠠\",\"⠐\",\"⠈\"]},\"dots12\":{\"interval\":80,\"frames\":[\"⢀⠀\",\"⡀⠀\",\"⠄⠀\",\"⢂⠀\",\"⡂⠀\",\"⠅⠀\",\"⢃⠀\",\"⡃⠀\",\"⠍⠀\",\"⢋⠀\",\"⡋⠀\",\"⠍⠁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⢈⠩\",\"⡀⢙\",\"⠄⡙\",\"⢂⠩\",\"⡂⢘\",\"⠅⡘\",\"⢃⠨\",\"⡃⢐\",\"⠍⡐\",\"⢋⠠\",\"⡋⢀\",\"⠍⡁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⠈⠩\",\"⠀⢙\",\"⠀⡙\",\"⠀⠩\",\"⠀⢘\",\"⠀⡘\",\"⠀⠨\",\"⠀⢐\",\"⠀⡐\",\"⠀⠠\",\"⠀⢀\",\"⠀⡀\"]},\"line\":{\"interval\":130,\"frames\":[\"-\",\"\\\\\",\"|\",\"/\"]},\"line2\":{\"interval\":100,\"frames\":[\"⠂\",\"-\",\"–\",\"—\",\"–\",\"-\"]},\"pipe\":{\"interval\":100,\"frames\":[\"┤\",\"┘\",\"┴\",\"└\",\"├\",\"┌\",\"┬\",\"┐\"]},\"simpleDots\":{\"interval\":400,\"frames\":[\". \",\".. \",\"...\",\" \"]},\"simpleDotsScrolling\":{\"interval\":200,\"frames\":[\". \",\".. \",\"...\",\" ..\",\" .\",\" \"]},\"star\":{\"interval\":70,\"frames\":[\"✶\",\"✸\",\"✹\",\"✺\",\"✹\",\"✷\"]},\"star2\":{\"interval\":80,\"frames\":[\"+\",\"x\",\"*\"]},\"flip\":{\"interval\":70,\"frames\":[\"_\",\"_\",\"_\",\"-\",\"`\",\"`\",\"'\",\"´\",\"-\",\"_\",\"_\",\"_\"]},\"hamburger\":{\"interval\":100,\"frames\":[\"☱\",\"☲\",\"☴\"]},\"growVertical\":{\"interval\":120,\"frames\":[\"▁\",\"▃\",\"▄\",\"▅\",\"▆\",\"▇\",\"▆\",\"▅\",\"▄\",\"▃\"]},\"growHorizontal\":{\"interval\":120,\"frames\":[\"▏\",\"▎\",\"▍\",\"▌\",\"▋\",\"▊\",\"▉\",\"▊\",\"▋\",\"▌\",\"▍\",\"▎\"]},\"balloon\":{\"interval\":140,\"frames\":[\" \",\".\",\"o\",\"O\",\"@\",\"*\",\" \"]},\"balloon2\":{\"interval\":120,\"frames\":[\".\",\"o\",\"O\",\"°\",\"O\",\"o\",\".\"]},\"noise\":{\"interval\":100,\"frames\":[\"▓\",\"▒\",\"░\"]},\"bounce\":{\"interval\":120,\"frames\":[\"⠁\",\"⠂\",\"⠄\",\"⠂\"]},\"boxBounce\":{\"interval\":120,\"frames\":[\"▖\",\"▘\",\"▝\",\"▗\"]},\"boxBounce2\":{\"interval\":100,\"frames\":[\"▌\",\"▀\",\"▐\",\"▄\"]},\"triangle\":{\"interval\":50,\"frames\":[\"◢\",\"◣\",\"◤\",\"◥\"]},\"arc\":{\"interval\":100,\"frames\":[\"◜\",\"◠\",\"◝\",\"◞\",\"◡\",\"◟\"]},\"circle\":{\"interval\":120,\"frames\":[\"◡\",\"⊙\",\"◠\"]},\"squareCorners\":{\"interval\":180,\"frames\":[\"◰\",\"◳\",\"◲\",\"◱\"]},\"circleQuarters\":{\"interval\":120,\"frames\":[\"◴\",\"◷\",\"◶\",\"◵\"]},\"circleHalves\":{\"interval\":50,\"frames\":[\"◐\",\"◓\",\"◑\",\"◒\"]},\"squish\":{\"interval\":100,\"frames\":[\"╫\",\"╪\"]},\"toggle\":{\"interval\":250,\"frames\":[\"⊶\",\"⊷\"]},\"toggle2\":{\"interval\":80,\"frames\":[\"▫\",\"▪\"]},\"toggle3\":{\"interval\":120,\"frames\":[\"□\",\"■\"]},\"toggle4\":{\"interval\":100,\"frames\":[\"■\",\"□\",\"▪\",\"▫\"]},\"toggle5\":{\"interval\":100,\"frames\":[\"▮\",\"▯\"]},\"toggle6\":{\"interval\":300,\"frames\":[\"ဝ\",\"၀\"]},\"toggle7\":{\"interval\":80,\"frames\":[\"⦾\",\"⦿\"]},\"toggle8\":{\"interval\":100,\"frames\":[\"◍\",\"◌\"]},\"toggle9\":{\"interval\":100,\"frames\":[\"◉\",\"◎\"]},\"toggle10\":{\"interval\":100,\"frames\":[\"㊂\",\"㊀\",\"㊁\"]},\"toggle11\":{\"interval\":50,\"frames\":[\"⧇\",\"⧆\"]},\"toggle12\":{\"interval\":120,\"frames\":[\"☗\",\"☖\"]},\"toggle13\":{\"interval\":80,\"frames\":[\"=\",\"*\",\"-\"]},\"arrow\":{\"interval\":100,\"frames\":[\"←\",\"↖\",\"↑\",\"↗\",\"→\",\"↘\",\"↓\",\"↙\"]},\"arrow2\":{\"interval\":80,\"frames\":[\"⬆️ \",\"↗️ \",\"➡️ \",\"↘️ \",\"⬇️ \",\"↙️ \",\"⬅️ \",\"↖️ \"]},\"arrow3\":{\"interval\":120,\"frames\":[\"▹▹▹▹▹\",\"▸▹▹▹▹\",\"▹▸▹▹▹\",\"▹▹▸▹▹\",\"▹▹▹▸▹\",\"▹▹▹▹▸\"]},\"bouncingBar\":{\"interval\":80,\"frames\":[\"[ ]\",\"[= ]\",\"[== ]\",\"[=== ]\",\"[ ===]\",\"[ ==]\",\"[ =]\",\"[ ]\",\"[ =]\",\"[ ==]\",\"[ ===]\",\"[====]\",\"[=== ]\",\"[== ]\",\"[= ]\"]},\"bouncingBall\":{\"interval\":80,\"frames\":[\"( ● )\",\"( ● )\",\"( ● )\",\"( ● )\",\"( ●)\",\"( ● )\",\"( ● )\",\"( ● )\",\"( ● )\",\"(● )\"]},\"smiley\":{\"interval\":200,\"frames\":[\"😄 \",\"😝 \"]},\"monkey\":{\"interval\":300,\"frames\":[\"🙈 \",\"🙈 \",\"🙉 \",\"🙊 \"]},\"hearts\":{\"interval\":100,\"frames\":[\"💛 \",\"💙 \",\"💜 \",\"💚 \",\"❤️ \"]},\"clock\":{\"interval\":100,\"frames\":[\"🕐 \",\"🕑 \",\"🕒 \",\"🕓 \",\"🕔 \",\"🕕 \",\"🕖 \",\"🕗 \",\"🕘 \",\"🕙 \",\"🕚 \"]},\"earth\":{\"interval\":180,\"frames\":[\"🌍 \",\"🌎 \",\"🌏 \"]},\"moon\":{\"interval\":80,\"frames\":[\"🌑 \",\"🌒 \",\"🌓 \",\"🌔 \",\"🌕 \",\"🌖 \",\"🌗 \",\"🌘 \"]},\"runner\":{\"interval\":140,\"frames\":[\"🚶 \",\"🏃 \"]},\"pong\":{\"interval\":80,\"frames\":[\"▐⠂ ▌\",\"▐⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂▌\",\"▐ ⠠▌\",\"▐ ⡀▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐⠠ ▌\"]},\"shark\":{\"interval\":120,\"frames\":[\"▐|\\\\____________▌\",\"▐_|\\\\___________▌\",\"▐__|\\\\__________▌\",\"▐___|\\\\_________▌\",\"▐____|\\\\________▌\",\"▐_____|\\\\_______▌\",\"▐______|\\\\______▌\",\"▐_______|\\\\_____▌\",\"▐________|\\\\____▌\",\"▐_________|\\\\___▌\",\"▐__________|\\\\__▌\",\"▐___________|\\\\_▌\",\"▐____________|\\\\▌\",\"▐____________/|▌\",\"▐___________/|_▌\",\"▐__________/|__▌\",\"▐_________/|___▌\",\"▐________/|____▌\",\"▐_______/|_____▌\",\"▐______/|______▌\",\"▐_____/|_______▌\",\"▐____/|________▌\",\"▐___/|_________▌\",\"▐__/|__________▌\",\"▐_/|___________▌\",\"▐/|____________▌\"]},\"dqpb\":{\"interval\":100,\"frames\":[\"d\",\"q\",\"p\",\"b\"]},\"weather\":{\"interval\":100,\"frames\":[\"☀️ \",\"☀️ \",\"☀️ \",\"🌤 \",\"⛅️ \",\"🌥 \",\"☁️ \",\"🌧 \",\"🌨 \",\"🌧 \",\"🌨 \",\"🌧 \",\"🌨 \",\"⛈ \",\"🌨 \",\"🌧 \",\"🌨 \",\"☁️ \",\"🌥 \",\"⛅️ \",\"🌤 \",\"☀️ \",\"☀️ \"]},\"christmas\":{\"interval\":400,\"frames\":[\"🌲\",\"🎄\"]}}"); /***/ }), -/* 383 */ +/* 384 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const chalk = __webpack_require__(384); +const chalk = __webpack_require__(385); const isSupported = process.platform !== 'win32' || process.env.CI || process.env.TERM === 'xterm-256color'; @@ -49011,16 +49162,16 @@ module.exports = isSupported ? main : fallbacks; /***/ }), -/* 384 */ +/* 385 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const escapeStringRegexp = __webpack_require__(113); -const ansiStyles = __webpack_require__(385); -const stdoutColor = __webpack_require__(386).stdout; +const ansiStyles = __webpack_require__(386); +const stdoutColor = __webpack_require__(387).stdout; -const template = __webpack_require__(387); +const template = __webpack_require__(388); const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); @@ -49246,7 +49397,7 @@ module.exports.default = module.exports; // For TypeScript /***/ }), -/* 385 */ +/* 386 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -49419,7 +49570,7 @@ Object.defineProperty(module, 'exports', { /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(115)(module))) /***/ }), -/* 386 */ +/* 387 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -49561,7 +49712,7 @@ module.exports = { /***/ }), -/* 387 */ +/* 388 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -49696,7 +49847,7 @@ module.exports = (chalk, tmp) => { /***/ }), -/* 388 */ +/* 389 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -49757,7 +49908,7 @@ const RunCommand = { }; /***/ }), -/* 389 */ +/* 390 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -49767,7 +49918,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(142); /* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(143); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(144); -/* harmony import */ var _utils_watch__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(390); +/* harmony import */ var _utils_watch__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(391); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -49852,14 +50003,14 @@ const WatchCommand = { }; /***/ }), -/* 390 */ +/* 391 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "waitUntilWatchIsReady", function() { return waitUntilWatchIsReady; }); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(8); -/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(391); +/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(392); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -49926,141 +50077,141 @@ function waitUntilWatchIsReady(stream, opts = {}) { } /***/ }), -/* 391 */ +/* 392 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(392); +/* harmony import */ var _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(393); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "audit", function() { return _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__["audit"]; }); -/* harmony import */ var _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(393); +/* harmony import */ var _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(394); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "auditTime", function() { return _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__["auditTime"]; }); -/* harmony import */ var _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(394); +/* harmony import */ var _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(395); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buffer", function() { return _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__["buffer"]; }); -/* harmony import */ var _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(395); +/* harmony import */ var _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(396); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferCount", function() { return _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__["bufferCount"]; }); -/* harmony import */ var _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(396); +/* harmony import */ var _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(397); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferTime", function() { return _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__["bufferTime"]; }); -/* harmony import */ var _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(397); +/* harmony import */ var _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(398); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferToggle", function() { return _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__["bufferToggle"]; }); -/* harmony import */ var _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(398); +/* harmony import */ var _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(399); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferWhen", function() { return _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__["bufferWhen"]; }); -/* harmony import */ var _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(399); +/* harmony import */ var _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(400); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "catchError", function() { return _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__["catchError"]; }); -/* harmony import */ var _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(400); +/* harmony import */ var _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(401); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineAll", function() { return _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__["combineAll"]; }); -/* harmony import */ var _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(401); +/* harmony import */ var _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(402); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineLatest", function() { return _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__["combineLatest"]; }); -/* harmony import */ var _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(402); +/* harmony import */ var _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(403); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concat", function() { return _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__["concat"]; }); /* harmony import */ var _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(80); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatAll", function() { return _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__["concatAll"]; }); -/* harmony import */ var _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(403); +/* harmony import */ var _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(404); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMap", function() { return _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__["concatMap"]; }); -/* harmony import */ var _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(404); +/* harmony import */ var _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(405); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__["concatMapTo"]; }); -/* harmony import */ var _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(405); +/* harmony import */ var _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(406); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "count", function() { return _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__["count"]; }); -/* harmony import */ var _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(406); +/* harmony import */ var _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(407); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "debounce", function() { return _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__["debounce"]; }); -/* harmony import */ var _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(407); +/* harmony import */ var _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(408); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "debounceTime", function() { return _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__["debounceTime"]; }); -/* harmony import */ var _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(408); +/* harmony import */ var _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(409); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "defaultIfEmpty", function() { return _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__["defaultIfEmpty"]; }); -/* harmony import */ var _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(409); +/* harmony import */ var _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(410); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "delay", function() { return _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__["delay"]; }); -/* harmony import */ var _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(411); +/* harmony import */ var _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(412); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "delayWhen", function() { return _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__["delayWhen"]; }); -/* harmony import */ var _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(412); +/* harmony import */ var _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(413); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "dematerialize", function() { return _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__["dematerialize"]; }); -/* harmony import */ var _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(413); +/* harmony import */ var _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(414); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinct", function() { return _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__["distinct"]; }); -/* harmony import */ var _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(414); +/* harmony import */ var _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(415); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilChanged", function() { return _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__["distinctUntilChanged"]; }); -/* harmony import */ var _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(415); +/* harmony import */ var _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(416); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilKeyChanged", function() { return _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__["distinctUntilKeyChanged"]; }); -/* harmony import */ var _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(416); +/* harmony import */ var _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(417); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "elementAt", function() { return _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__["elementAt"]; }); -/* harmony import */ var _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(419); +/* harmony import */ var _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(420); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "endWith", function() { return _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__["endWith"]; }); -/* harmony import */ var _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(420); +/* harmony import */ var _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(421); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "every", function() { return _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__["every"]; }); -/* harmony import */ var _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(421); +/* harmony import */ var _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(422); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaust", function() { return _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__["exhaust"]; }); -/* harmony import */ var _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(422); +/* harmony import */ var _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(423); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaustMap", function() { return _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__["exhaustMap"]; }); -/* harmony import */ var _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(423); +/* harmony import */ var _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(424); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "expand", function() { return _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__["expand"]; }); /* harmony import */ var _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(104); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "filter", function() { return _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__["filter"]; }); -/* harmony import */ var _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(424); +/* harmony import */ var _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(425); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "finalize", function() { return _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__["finalize"]; }); -/* harmony import */ var _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(425); +/* harmony import */ var _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(426); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "find", function() { return _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__["find"]; }); -/* harmony import */ var _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(426); +/* harmony import */ var _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(427); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__["findIndex"]; }); -/* harmony import */ var _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(427); +/* harmony import */ var _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(428); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "first", function() { return _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__["first"]; }); /* harmony import */ var _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(31); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "groupBy", function() { return _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__["groupBy"]; }); -/* harmony import */ var _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(428); +/* harmony import */ var _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(429); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ignoreElements", function() { return _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__["ignoreElements"]; }); -/* harmony import */ var _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(429); +/* harmony import */ var _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(430); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "isEmpty", function() { return _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__["isEmpty"]; }); -/* harmony import */ var _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(430); +/* harmony import */ var _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(431); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "last", function() { return _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__["last"]; }); /* harmony import */ var _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(66); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "map", function() { return _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__["map"]; }); -/* harmony import */ var _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(432); +/* harmony import */ var _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(433); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mapTo", function() { return _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__["mapTo"]; }); -/* harmony import */ var _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(433); +/* harmony import */ var _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(434); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "materialize", function() { return _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__["materialize"]; }); -/* harmony import */ var _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(434); +/* harmony import */ var _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(435); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "max", function() { return _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__["max"]; }); -/* harmony import */ var _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(437); +/* harmony import */ var _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(438); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "merge", function() { return _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__["merge"]; }); /* harmony import */ var _internal_operators_mergeAll__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(81); @@ -50071,175 +50222,175 @@ __webpack_require__.r(__webpack_exports__); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "flatMap", function() { return _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__["mergeMap"]; }); -/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(438); +/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(439); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeMapTo", function() { return _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__["mergeMapTo"]; }); -/* harmony import */ var _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(439); +/* harmony import */ var _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(440); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeScan", function() { return _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__["mergeScan"]; }); -/* harmony import */ var _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(440); +/* harmony import */ var _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(441); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "min", function() { return _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__["min"]; }); -/* harmony import */ var _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(441); +/* harmony import */ var _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(442); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "multicast", function() { return _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__["multicast"]; }); /* harmony import */ var _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(41); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "observeOn", function() { return _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__["observeOn"]; }); -/* harmony import */ var _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(442); +/* harmony import */ var _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(443); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "onErrorResumeNext", function() { return _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__["onErrorResumeNext"]; }); -/* harmony import */ var _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(443); +/* harmony import */ var _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(444); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pairwise", function() { return _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__["pairwise"]; }); -/* harmony import */ var _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(444); +/* harmony import */ var _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(445); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "partition", function() { return _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__["partition"]; }); -/* harmony import */ var _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(445); +/* harmony import */ var _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(446); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pluck", function() { return _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__["pluck"]; }); -/* harmony import */ var _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(446); +/* harmony import */ var _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(447); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publish", function() { return _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__["publish"]; }); -/* harmony import */ var _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(447); +/* harmony import */ var _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(448); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishBehavior", function() { return _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__["publishBehavior"]; }); -/* harmony import */ var _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(448); +/* harmony import */ var _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(449); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__["publishLast"]; }); -/* harmony import */ var _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(449); +/* harmony import */ var _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(450); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishReplay", function() { return _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__["publishReplay"]; }); -/* harmony import */ var _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(450); +/* harmony import */ var _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(451); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "race", function() { return _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__["race"]; }); -/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(435); +/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(436); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__["reduce"]; }); -/* harmony import */ var _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(451); +/* harmony import */ var _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(452); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeat", function() { return _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__["repeat"]; }); -/* harmony import */ var _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(452); +/* harmony import */ var _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(453); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeatWhen", function() { return _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__["repeatWhen"]; }); -/* harmony import */ var _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__ = __webpack_require__(453); +/* harmony import */ var _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__ = __webpack_require__(454); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retry", function() { return _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__["retry"]; }); -/* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(454); +/* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(455); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retryWhen", function() { return _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__["retryWhen"]; }); /* harmony import */ var _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__ = __webpack_require__(30); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "refCount", function() { return _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__["refCount"]; }); -/* harmony import */ var _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__ = __webpack_require__(455); +/* harmony import */ var _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__ = __webpack_require__(456); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sample", function() { return _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__["sample"]; }); -/* harmony import */ var _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(456); +/* harmony import */ var _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(457); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sampleTime", function() { return _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__["sampleTime"]; }); -/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(436); +/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(437); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "scan", function() { return _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__["scan"]; }); -/* harmony import */ var _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__ = __webpack_require__(457); +/* harmony import */ var _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__ = __webpack_require__(458); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sequenceEqual", function() { return _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__["sequenceEqual"]; }); -/* harmony import */ var _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__ = __webpack_require__(458); +/* harmony import */ var _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__ = __webpack_require__(459); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "share", function() { return _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__["share"]; }); -/* harmony import */ var _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__ = __webpack_require__(459); +/* harmony import */ var _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__ = __webpack_require__(460); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "shareReplay", function() { return _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__["shareReplay"]; }); -/* harmony import */ var _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__ = __webpack_require__(460); +/* harmony import */ var _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__ = __webpack_require__(461); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "single", function() { return _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__["single"]; }); -/* harmony import */ var _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__ = __webpack_require__(461); +/* harmony import */ var _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__ = __webpack_require__(462); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skip", function() { return _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__["skip"]; }); -/* harmony import */ var _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__ = __webpack_require__(462); +/* harmony import */ var _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__ = __webpack_require__(463); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipLast", function() { return _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__["skipLast"]; }); -/* harmony import */ var _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__ = __webpack_require__(463); +/* harmony import */ var _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__ = __webpack_require__(464); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipUntil", function() { return _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__["skipUntil"]; }); -/* harmony import */ var _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__ = __webpack_require__(464); +/* harmony import */ var _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__ = __webpack_require__(465); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipWhile", function() { return _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__["skipWhile"]; }); -/* harmony import */ var _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__ = __webpack_require__(465); +/* harmony import */ var _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__ = __webpack_require__(466); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "startWith", function() { return _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__["startWith"]; }); -/* harmony import */ var _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__ = __webpack_require__(466); +/* harmony import */ var _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__ = __webpack_require__(467); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "subscribeOn", function() { return _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__["subscribeOn"]; }); -/* harmony import */ var _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__ = __webpack_require__(468); +/* harmony import */ var _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__ = __webpack_require__(469); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchAll", function() { return _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__["switchAll"]; }); -/* harmony import */ var _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__ = __webpack_require__(469); +/* harmony import */ var _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__ = __webpack_require__(470); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMap", function() { return _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__["switchMap"]; }); -/* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(470); +/* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(471); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMapTo", function() { return _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__["switchMapTo"]; }); -/* harmony import */ var _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__ = __webpack_require__(418); +/* harmony import */ var _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__ = __webpack_require__(419); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "take", function() { return _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__["take"]; }); -/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(431); +/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(432); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeLast", function() { return _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__["takeLast"]; }); -/* harmony import */ var _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__ = __webpack_require__(471); +/* harmony import */ var _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__ = __webpack_require__(472); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeUntil", function() { return _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__["takeUntil"]; }); -/* harmony import */ var _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__ = __webpack_require__(472); +/* harmony import */ var _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__ = __webpack_require__(473); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeWhile", function() { return _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__["takeWhile"]; }); -/* harmony import */ var _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__ = __webpack_require__(473); +/* harmony import */ var _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__ = __webpack_require__(474); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "tap", function() { return _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__["tap"]; }); -/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(474); +/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(475); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throttle", function() { return _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__["throttle"]; }); -/* harmony import */ var _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__ = __webpack_require__(475); +/* harmony import */ var _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__ = __webpack_require__(476); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throttleTime", function() { return _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__["throttleTime"]; }); -/* harmony import */ var _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__ = __webpack_require__(417); +/* harmony import */ var _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__ = __webpack_require__(418); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throwIfEmpty", function() { return _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__["throwIfEmpty"]; }); -/* harmony import */ var _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__ = __webpack_require__(476); +/* harmony import */ var _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__ = __webpack_require__(477); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeInterval", function() { return _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__["timeInterval"]; }); -/* harmony import */ var _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__ = __webpack_require__(477); +/* harmony import */ var _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__ = __webpack_require__(478); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeout", function() { return _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__["timeout"]; }); -/* harmony import */ var _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__ = __webpack_require__(478); +/* harmony import */ var _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__ = __webpack_require__(479); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeoutWith", function() { return _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__["timeoutWith"]; }); -/* harmony import */ var _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__ = __webpack_require__(479); +/* harmony import */ var _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__ = __webpack_require__(480); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timestamp", function() { return _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__["timestamp"]; }); -/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(480); +/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(481); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__["toArray"]; }); -/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(481); +/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(482); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "window", function() { return _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__["window"]; }); -/* harmony import */ var _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__ = __webpack_require__(482); +/* harmony import */ var _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__ = __webpack_require__(483); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowCount", function() { return _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__["windowCount"]; }); -/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(483); +/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(484); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowTime", function() { return _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__["windowTime"]; }); -/* harmony import */ var _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__ = __webpack_require__(484); +/* harmony import */ var _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__ = __webpack_require__(485); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowToggle", function() { return _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__["windowToggle"]; }); -/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(485); +/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(486); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowWhen", function() { return _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__["windowWhen"]; }); -/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(486); +/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(487); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "withLatestFrom", function() { return _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__["withLatestFrom"]; }); -/* harmony import */ var _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__ = __webpack_require__(487); +/* harmony import */ var _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__ = __webpack_require__(488); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zip", function() { return _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__["zip"]; }); -/* harmony import */ var _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__ = __webpack_require__(488); +/* harmony import */ var _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__ = __webpack_require__(489); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zipAll", function() { return _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__["zipAll"]; }); /** PURE_IMPORTS_START PURE_IMPORTS_END */ @@ -50351,7 +50502,7 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 392 */ +/* 393 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50432,14 +50583,14 @@ var AuditSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 393 */ +/* 394 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "auditTime", function() { return auditTime; }); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(55); -/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(392); +/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(393); /* harmony import */ var _observable_timer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(107); /** PURE_IMPORTS_START _scheduler_async,_audit,_observable_timer PURE_IMPORTS_END */ @@ -50455,7 +50606,7 @@ function auditTime(duration, scheduler) { /***/ }), -/* 394 */ +/* 395 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50504,7 +50655,7 @@ var BufferSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 395 */ +/* 396 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50605,7 +50756,7 @@ var BufferSkipCountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 396 */ +/* 397 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50766,7 +50917,7 @@ function dispatchBufferClose(arg) { /***/ }), -/* 397 */ +/* 398 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50886,7 +51037,7 @@ var BufferToggleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 398 */ +/* 399 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50981,7 +51132,7 @@ var BufferWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 399 */ +/* 400 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51042,7 +51193,7 @@ var CatchSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 400 */ +/* 401 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51058,7 +51209,7 @@ function combineAll(project) { /***/ }), -/* 401 */ +/* 402 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51090,7 +51241,7 @@ function combineLatest() { /***/ }), -/* 402 */ +/* 403 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51110,7 +51261,7 @@ function concat() { /***/ }), -/* 403 */ +/* 404 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51126,13 +51277,13 @@ function concatMap(project, resultSelector) { /***/ }), -/* 404 */ +/* 405 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return concatMapTo; }); -/* harmony import */ var _concatMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(403); +/* harmony import */ var _concatMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(404); /** PURE_IMPORTS_START _concatMap PURE_IMPORTS_END */ function concatMapTo(innerObservable, resultSelector) { @@ -51142,7 +51293,7 @@ function concatMapTo(innerObservable, resultSelector) { /***/ }), -/* 405 */ +/* 406 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51207,7 +51358,7 @@ var CountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 406 */ +/* 407 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51295,7 +51446,7 @@ var DebounceSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 407 */ +/* 408 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51371,7 +51522,7 @@ function dispatchNext(subscriber) { /***/ }), -/* 408 */ +/* 409 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51421,7 +51572,7 @@ var DefaultIfEmptySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 409 */ +/* 410 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51429,7 +51580,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "delay", function() { return delay; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(410); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(411); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11); /* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(42); /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_Subscriber,_Notification PURE_IMPORTS_END */ @@ -51528,7 +51679,7 @@ var DelayMessage = /*@__PURE__*/ (function () { /***/ }), -/* 410 */ +/* 411 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51542,7 +51693,7 @@ function isDate(value) { /***/ }), -/* 411 */ +/* 412 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51688,7 +51839,7 @@ var SubscriptionDelaySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 412 */ +/* 413 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51726,7 +51877,7 @@ var DeMaterializeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 413 */ +/* 414 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51804,7 +51955,7 @@ var DistinctSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 414 */ +/* 415 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51875,13 +52026,13 @@ var DistinctUntilChangedSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 415 */ +/* 416 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "distinctUntilKeyChanged", function() { return distinctUntilKeyChanged; }); -/* harmony import */ var _distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(414); +/* harmony import */ var _distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(415); /** PURE_IMPORTS_START _distinctUntilChanged PURE_IMPORTS_END */ function distinctUntilKeyChanged(key, compare) { @@ -51891,7 +52042,7 @@ function distinctUntilKeyChanged(key, compare) { /***/ }), -/* 416 */ +/* 417 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51899,9 +52050,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "elementAt", function() { return elementAt; }); /* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(62); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(104); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(417); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(408); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(418); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(418); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(409); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(419); /** PURE_IMPORTS_START _util_ArgumentOutOfRangeError,_filter,_throwIfEmpty,_defaultIfEmpty,_take PURE_IMPORTS_END */ @@ -51923,7 +52074,7 @@ function elementAt(index, defaultValue) { /***/ }), -/* 417 */ +/* 418 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51989,7 +52140,7 @@ function defaultErrorFactory() { /***/ }), -/* 418 */ +/* 419 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52051,7 +52202,7 @@ var TakeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 419 */ +/* 420 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52073,7 +52224,7 @@ function endWith() { /***/ }), -/* 420 */ +/* 421 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52135,7 +52286,7 @@ var EverySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 421 */ +/* 422 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52192,7 +52343,7 @@ var SwitchFirstSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 422 */ +/* 423 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52289,7 +52440,7 @@ var ExhaustMapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 423 */ +/* 424 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52408,7 +52559,7 @@ var ExpandSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 424 */ +/* 425 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52446,7 +52597,7 @@ var FinallySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 425 */ +/* 426 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52518,13 +52669,13 @@ var FindValueSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 426 */ +/* 427 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return findIndex; }); -/* harmony import */ var _operators_find__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(425); +/* harmony import */ var _operators_find__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(426); /** PURE_IMPORTS_START _operators_find PURE_IMPORTS_END */ function findIndex(predicate, thisArg) { @@ -52534,7 +52685,7 @@ function findIndex(predicate, thisArg) { /***/ }), -/* 427 */ +/* 428 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52542,9 +52693,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "first", function() { return first; }); /* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(63); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(104); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(418); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(408); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(417); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(419); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(409); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(418); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(60); /** PURE_IMPORTS_START _util_EmptyError,_filter,_take,_defaultIfEmpty,_throwIfEmpty,_util_identity PURE_IMPORTS_END */ @@ -52561,7 +52712,7 @@ function first(predicate, defaultValue) { /***/ }), -/* 428 */ +/* 429 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52598,7 +52749,7 @@ var IgnoreElementsSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 429 */ +/* 430 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52642,7 +52793,7 @@ var IsEmptySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 430 */ +/* 431 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52650,9 +52801,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "last", function() { return last; }); /* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(63); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(104); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(431); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(417); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(408); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(432); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(418); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(409); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(60); /** PURE_IMPORTS_START _util_EmptyError,_filter,_takeLast,_throwIfEmpty,_defaultIfEmpty,_util_identity PURE_IMPORTS_END */ @@ -52669,7 +52820,7 @@ function last(predicate, defaultValue) { /***/ }), -/* 431 */ +/* 432 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52746,7 +52897,7 @@ var TakeLastSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 432 */ +/* 433 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52785,7 +52936,7 @@ var MapToSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 433 */ +/* 434 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52835,13 +52986,13 @@ var MaterializeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 434 */ +/* 435 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "max", function() { return max; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(435); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(436); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function max(comparer) { @@ -52854,15 +53005,15 @@ function max(comparer) { /***/ }), -/* 435 */ +/* 436 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return reduce; }); -/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(436); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(431); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(408); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(437); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(432); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(409); /* harmony import */ var _util_pipe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(24); /** PURE_IMPORTS_START _scan,_takeLast,_defaultIfEmpty,_util_pipe PURE_IMPORTS_END */ @@ -52883,7 +53034,7 @@ function reduce(accumulator, seed) { /***/ }), -/* 436 */ +/* 437 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52965,7 +53116,7 @@ var ScanSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 437 */ +/* 438 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52985,7 +53136,7 @@ function merge() { /***/ }), -/* 438 */ +/* 439 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53010,7 +53161,7 @@ function mergeMapTo(innerObservable, resultSelector, concurrent) { /***/ }), -/* 439 */ +/* 440 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53122,13 +53273,13 @@ var MergeScanSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 440 */ +/* 441 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "min", function() { return min; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(435); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(436); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function min(comparer) { @@ -53141,7 +53292,7 @@ function min(comparer) { /***/ }), -/* 441 */ +/* 442 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53190,7 +53341,7 @@ var MulticastOperator = /*@__PURE__*/ (function () { /***/ }), -/* 442 */ +/* 443 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53281,7 +53432,7 @@ var OnErrorResumeNextSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 443 */ +/* 444 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53329,7 +53480,7 @@ var PairwiseSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 444 */ +/* 445 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53352,7 +53503,7 @@ function partition(predicate, thisArg) { /***/ }), -/* 445 */ +/* 446 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53392,14 +53543,14 @@ function plucker(props, length) { /***/ }), -/* 446 */ +/* 447 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publish", function() { return publish; }); /* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(27); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(441); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(442); /** PURE_IMPORTS_START _Subject,_multicast PURE_IMPORTS_END */ @@ -53412,14 +53563,14 @@ function publish(selector) { /***/ }), -/* 447 */ +/* 448 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishBehavior", function() { return publishBehavior; }); /* harmony import */ var _BehaviorSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(32); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(441); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(442); /** PURE_IMPORTS_START _BehaviorSubject,_multicast PURE_IMPORTS_END */ @@ -53430,14 +53581,14 @@ function publishBehavior(value) { /***/ }), -/* 448 */ +/* 449 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return publishLast; }); /* harmony import */ var _AsyncSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(50); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(441); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(442); /** PURE_IMPORTS_START _AsyncSubject,_multicast PURE_IMPORTS_END */ @@ -53448,14 +53599,14 @@ function publishLast() { /***/ }), -/* 449 */ +/* 450 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishReplay", function() { return publishReplay; }); /* harmony import */ var _ReplaySubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(33); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(441); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(442); /** PURE_IMPORTS_START _ReplaySubject,_multicast PURE_IMPORTS_END */ @@ -53471,7 +53622,7 @@ function publishReplay(bufferSize, windowTime, selectorOrScheduler, scheduler) { /***/ }), -/* 450 */ +/* 451 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53498,7 +53649,7 @@ function race() { /***/ }), -/* 451 */ +/* 452 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53563,7 +53714,7 @@ var RepeatSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 452 */ +/* 453 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53659,7 +53810,7 @@ var RepeatWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 453 */ +/* 454 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53712,7 +53863,7 @@ var RetrySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 454 */ +/* 455 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53800,7 +53951,7 @@ var RetryWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 455 */ +/* 456 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53857,7 +54008,7 @@ var SampleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 456 */ +/* 457 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53917,7 +54068,7 @@ function dispatchNotification(state) { /***/ }), -/* 457 */ +/* 458 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54040,13 +54191,13 @@ var SequenceEqualCompareToSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 458 */ +/* 459 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "share", function() { return share; }); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(441); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(442); /* harmony import */ var _refCount__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(30); /* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(27); /** PURE_IMPORTS_START _multicast,_refCount,_Subject PURE_IMPORTS_END */ @@ -54063,7 +54214,7 @@ function share() { /***/ }), -/* 459 */ +/* 460 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54127,7 +54278,7 @@ function shareReplayOperator(_a) { /***/ }), -/* 460 */ +/* 461 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54207,7 +54358,7 @@ var SingleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 461 */ +/* 462 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54249,7 +54400,7 @@ var SkipSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 462 */ +/* 463 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54311,7 +54462,7 @@ var SkipLastSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 463 */ +/* 464 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54368,7 +54519,7 @@ var SkipUntilSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 464 */ +/* 465 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54424,7 +54575,7 @@ var SkipWhileSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 465 */ +/* 466 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54453,13 +54604,13 @@ function startWith() { /***/ }), -/* 466 */ +/* 467 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subscribeOn", function() { return subscribeOn; }); -/* harmony import */ var _observable_SubscribeOnObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(467); +/* harmony import */ var _observable_SubscribeOnObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(468); /** PURE_IMPORTS_START _observable_SubscribeOnObservable PURE_IMPORTS_END */ function subscribeOn(scheduler, delay) { @@ -54484,7 +54635,7 @@ var SubscribeOnOperator = /*@__PURE__*/ (function () { /***/ }), -/* 467 */ +/* 468 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54548,13 +54699,13 @@ var SubscribeOnObservable = /*@__PURE__*/ (function (_super) { /***/ }), -/* 468 */ +/* 469 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchAll", function() { return switchAll; }); -/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(469); +/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(470); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(60); /** PURE_IMPORTS_START _switchMap,_util_identity PURE_IMPORTS_END */ @@ -54566,7 +54717,7 @@ function switchAll() { /***/ }), -/* 469 */ +/* 470 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54657,13 +54808,13 @@ var SwitchMapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 470 */ +/* 471 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchMapTo", function() { return switchMapTo; }); -/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(469); +/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(470); /** PURE_IMPORTS_START _switchMap PURE_IMPORTS_END */ function switchMapTo(innerObservable, resultSelector) { @@ -54673,7 +54824,7 @@ function switchMapTo(innerObservable, resultSelector) { /***/ }), -/* 471 */ +/* 472 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54723,7 +54874,7 @@ var TakeUntilSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 472 */ +/* 473 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54791,7 +54942,7 @@ var TakeWhileSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 473 */ +/* 474 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54879,7 +55030,7 @@ var TapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 474 */ +/* 475 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54983,7 +55134,7 @@ var ThrottleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 475 */ +/* 476 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54992,7 +55143,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(55); -/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(474); +/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(475); /** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async,_throttle PURE_IMPORTS_END */ @@ -55081,7 +55232,7 @@ function dispatchNext(arg) { /***/ }), -/* 476 */ +/* 477 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55089,7 +55240,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeInterval", function() { return timeInterval; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TimeInterval", function() { return TimeInterval; }); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(55); -/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(436); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(437); /* harmony import */ var _observable_defer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(90); /* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(66); /** PURE_IMPORTS_START _scheduler_async,_scan,_observable_defer,_map PURE_IMPORTS_END */ @@ -55125,7 +55276,7 @@ var TimeInterval = /*@__PURE__*/ (function () { /***/ }), -/* 477 */ +/* 478 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55133,7 +55284,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeout", function() { return timeout; }); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(55); /* harmony import */ var _util_TimeoutError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(64); -/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(478); +/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(479); /* harmony import */ var _observable_throwError__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(49); /** PURE_IMPORTS_START _scheduler_async,_util_TimeoutError,_timeoutWith,_observable_throwError PURE_IMPORTS_END */ @@ -55150,7 +55301,7 @@ function timeout(due, scheduler) { /***/ }), -/* 478 */ +/* 479 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55158,7 +55309,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeoutWith", function() { return timeoutWith; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(410); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(411); /* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(69); /* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(70); /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -55232,7 +55383,7 @@ var TimeoutWithSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 479 */ +/* 480 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55262,13 +55413,13 @@ var Timestamp = /*@__PURE__*/ (function () { /***/ }), -/* 480 */ +/* 481 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return toArray; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(435); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(436); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function toArrayReducer(arr, item, index) { @@ -55285,7 +55436,7 @@ function toArray() { /***/ }), -/* 481 */ +/* 482 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55365,7 +55516,7 @@ var WindowSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 482 */ +/* 483 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55455,7 +55606,7 @@ var WindowCountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 483 */ +/* 484 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55625,7 +55776,7 @@ function dispatchWindowClose(state) { /***/ }), -/* 484 */ +/* 485 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55768,7 +55919,7 @@ var WindowToggleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 485 */ +/* 486 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55865,7 +56016,7 @@ var WindowSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 486 */ +/* 487 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55960,7 +56111,7 @@ var WithLatestFromSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 487 */ +/* 488 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55982,7 +56133,7 @@ function zip() { /***/ }), -/* 488 */ +/* 489 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55998,7 +56149,7 @@ function zipAll(project) { /***/ }), -/* 489 */ +/* 490 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56007,8 +56158,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(161); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(142); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(144); -/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(490); -/* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(491); +/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(491); +/* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(492); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } @@ -56090,7 +56241,7 @@ function toArray(value) { } /***/ }), -/* 490 */ +/* 491 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56243,7 +56394,7 @@ function addProjectToTree(tree, pathParts, project) { } /***/ }), -/* 491 */ +/* 492 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56251,12 +56402,12 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Kibana", function() { return Kibana; }); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(492); +/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(493); /* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(multimatch__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(496); +/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(497); /* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(is_path_inside__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(144); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(271); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(272); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } @@ -56397,15 +56548,15 @@ class Kibana { } /***/ }), -/* 492 */ +/* 493 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const minimatch = __webpack_require__(148); -const arrayUnion = __webpack_require__(493); -const arrayDiffer = __webpack_require__(494); -const arrify = __webpack_require__(495); +const arrayUnion = __webpack_require__(494); +const arrayDiffer = __webpack_require__(495); +const arrify = __webpack_require__(496); module.exports = (list, patterns, options = {}) => { list = arrify(list); @@ -56429,7 +56580,7 @@ module.exports = (list, patterns, options = {}) => { /***/ }), -/* 493 */ +/* 494 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56441,7 +56592,7 @@ module.exports = (...arguments_) => { /***/ }), -/* 494 */ +/* 495 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56456,7 +56607,7 @@ module.exports = arrayDiffer; /***/ }), -/* 495 */ +/* 496 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56486,7 +56637,7 @@ module.exports = arrify; /***/ }), -/* 496 */ +/* 497 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56514,15 +56665,15 @@ module.exports = (childPath, parentPath) => { /***/ }), -/* 497 */ +/* 498 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _build_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(498); +/* harmony import */ var _build_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(499); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _build_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildProductionProjects"]; }); -/* harmony import */ var _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(734); +/* harmony import */ var _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(735); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "prepareExternalProjectDependencies", function() { return _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__["prepareExternalProjectDependencies"]; }); /* @@ -56547,19 +56698,19 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 498 */ +/* 499 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return buildProductionProjects; }); -/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(499); +/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(500); /* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cpy__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(280); +/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(281); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(271); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(272); /* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(129); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(142); /* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(163); @@ -56695,7 +56846,7 @@ async function copyToBuild(project, kibanaRoot, buildRoot) { } /***/ }), -/* 499 */ +/* 500 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56703,13 +56854,13 @@ async function copyToBuild(project, kibanaRoot, buildRoot) { const EventEmitter = __webpack_require__(154); const path = __webpack_require__(4); const os = __webpack_require__(121); -const pAll = __webpack_require__(500); -const arrify = __webpack_require__(502); -const globby = __webpack_require__(503); -const isGlob = __webpack_require__(718); -const cpFile = __webpack_require__(719); -const junk = __webpack_require__(731); -const CpyError = __webpack_require__(732); +const pAll = __webpack_require__(501); +const arrify = __webpack_require__(503); +const globby = __webpack_require__(504); +const isGlob = __webpack_require__(719); +const cpFile = __webpack_require__(720); +const junk = __webpack_require__(732); +const CpyError = __webpack_require__(733); const defaultOptions = { ignoreJunk: true @@ -56828,12 +56979,12 @@ module.exports = (source, destination, { /***/ }), -/* 500 */ +/* 501 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pMap = __webpack_require__(501); +const pMap = __webpack_require__(502); module.exports = (iterable, options) => pMap(iterable, element => element(), options); // TODO: Remove this for the next major release @@ -56841,7 +56992,7 @@ module.exports.default = module.exports; /***/ }), -/* 501 */ +/* 502 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56920,7 +57071,7 @@ module.exports.default = pMap; /***/ }), -/* 502 */ +/* 503 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56950,17 +57101,17 @@ module.exports = arrify; /***/ }), -/* 503 */ +/* 504 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(132); -const arrayUnion = __webpack_require__(504); -const glob = __webpack_require__(506); -const fastGlob = __webpack_require__(511); -const dirGlob = __webpack_require__(711); -const gitignore = __webpack_require__(714); +const arrayUnion = __webpack_require__(505); +const glob = __webpack_require__(507); +const fastGlob = __webpack_require__(512); +const dirGlob = __webpack_require__(712); +const gitignore = __webpack_require__(715); const DEFAULT_FILTER = () => false; @@ -57105,12 +57256,12 @@ module.exports.gitignore = gitignore; /***/ }), -/* 504 */ +/* 505 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var arrayUniq = __webpack_require__(505); +var arrayUniq = __webpack_require__(506); module.exports = function () { return arrayUniq([].concat.apply([], arguments)); @@ -57118,7 +57269,7 @@ module.exports = function () { /***/ }), -/* 505 */ +/* 506 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -57187,7 +57338,7 @@ if ('Set' in global) { /***/ }), -/* 506 */ +/* 507 */ /***/ (function(module, exports, __webpack_require__) { // Approach: @@ -57236,13 +57387,13 @@ var fs = __webpack_require__(132) var rp = __webpack_require__(146) var minimatch = __webpack_require__(148) var Minimatch = minimatch.Minimatch -var inherits = __webpack_require__(507) +var inherits = __webpack_require__(508) var EE = __webpack_require__(154).EventEmitter var path = __webpack_require__(4) var assert = __webpack_require__(138) var isAbsolute = __webpack_require__(155) -var globSync = __webpack_require__(509) -var common = __webpack_require__(510) +var globSync = __webpack_require__(510) +var common = __webpack_require__(511) var alphasort = common.alphasort var alphasorti = common.alphasorti var setopts = common.setopts @@ -57983,7 +58134,7 @@ Glob.prototype._stat2 = function (f, abs, er, stat, cb) { /***/ }), -/* 507 */ +/* 508 */ /***/ (function(module, exports, __webpack_require__) { try { @@ -57993,12 +58144,12 @@ try { module.exports = util.inherits; } catch (e) { /* istanbul ignore next */ - module.exports = __webpack_require__(508); + module.exports = __webpack_require__(509); } /***/ }), -/* 508 */ +/* 509 */ /***/ (function(module, exports) { if (typeof Object.create === 'function') { @@ -58031,7 +58182,7 @@ if (typeof Object.create === 'function') { /***/ }), -/* 509 */ +/* 510 */ /***/ (function(module, exports, __webpack_require__) { module.exports = globSync @@ -58041,12 +58192,12 @@ var fs = __webpack_require__(132) var rp = __webpack_require__(146) var minimatch = __webpack_require__(148) var Minimatch = minimatch.Minimatch -var Glob = __webpack_require__(506).Glob +var Glob = __webpack_require__(507).Glob var util = __webpack_require__(111) var path = __webpack_require__(4) var assert = __webpack_require__(138) var isAbsolute = __webpack_require__(155) -var common = __webpack_require__(510) +var common = __webpack_require__(511) var alphasort = common.alphasort var alphasorti = common.alphasorti var setopts = common.setopts @@ -58523,7 +58674,7 @@ GlobSync.prototype._makeAbs = function (f) { /***/ }), -/* 510 */ +/* 511 */ /***/ (function(module, exports, __webpack_require__) { exports.alphasort = alphasort @@ -58769,10 +58920,10 @@ function childrenIgnored (self, path) { /***/ }), -/* 511 */ +/* 512 */ /***/ (function(module, exports, __webpack_require__) { -const pkg = __webpack_require__(512); +const pkg = __webpack_require__(513); module.exports = pkg.async; module.exports.default = pkg.async; @@ -58785,19 +58936,19 @@ module.exports.generateTasks = pkg.generateTasks; /***/ }), -/* 512 */ +/* 513 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var optionsManager = __webpack_require__(513); -var taskManager = __webpack_require__(514); -var reader_async_1 = __webpack_require__(682); -var reader_stream_1 = __webpack_require__(706); -var reader_sync_1 = __webpack_require__(707); -var arrayUtils = __webpack_require__(709); -var streamUtils = __webpack_require__(710); +var optionsManager = __webpack_require__(514); +var taskManager = __webpack_require__(515); +var reader_async_1 = __webpack_require__(683); +var reader_stream_1 = __webpack_require__(707); +var reader_sync_1 = __webpack_require__(708); +var arrayUtils = __webpack_require__(710); +var streamUtils = __webpack_require__(711); /** * Synchronous API. */ @@ -58863,7 +59014,7 @@ function isString(source) { /***/ }), -/* 513 */ +/* 514 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -58901,13 +59052,13 @@ exports.prepare = prepare; /***/ }), -/* 514 */ +/* 515 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var patternUtils = __webpack_require__(515); +var patternUtils = __webpack_require__(516); /** * Generate tasks based on parent directory of each pattern. */ @@ -58998,16 +59149,16 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 515 */ +/* 516 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(4); -var globParent = __webpack_require__(516); -var isGlob = __webpack_require__(519); -var micromatch = __webpack_require__(520); +var globParent = __webpack_require__(517); +var isGlob = __webpack_require__(520); +var micromatch = __webpack_require__(521); var GLOBSTAR = '**'; /** * Return true for static pattern. @@ -59153,15 +59304,15 @@ exports.matchAny = matchAny; /***/ }), -/* 516 */ +/* 517 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var path = __webpack_require__(4); -var isglob = __webpack_require__(517); -var pathDirname = __webpack_require__(518); +var isglob = __webpack_require__(518); +var pathDirname = __webpack_require__(519); var isWin32 = __webpack_require__(121).platform() === 'win32'; module.exports = function globParent(str) { @@ -59184,7 +59335,7 @@ module.exports = function globParent(str) { /***/ }), -/* 517 */ +/* 518 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -59194,7 +59345,7 @@ module.exports = function globParent(str) { * Licensed under the MIT License. */ -var isExtglob = __webpack_require__(299); +var isExtglob = __webpack_require__(300); module.exports = function isGlob(str) { if (typeof str !== 'string' || str === '') { @@ -59215,7 +59366,7 @@ module.exports = function isGlob(str) { /***/ }), -/* 518 */ +/* 519 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59365,7 +59516,7 @@ module.exports.win32 = win32; /***/ }), -/* 519 */ +/* 520 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -59375,7 +59526,7 @@ module.exports.win32 = win32; * Released under the MIT License. */ -var isExtglob = __webpack_require__(299); +var isExtglob = __webpack_require__(300); var chars = { '{': '}', '(': ')', '[': ']'}; module.exports = function isGlob(str, options) { @@ -59417,7 +59568,7 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 520 */ +/* 521 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59428,18 +59579,18 @@ module.exports = function isGlob(str, options) { */ var util = __webpack_require__(111); -var braces = __webpack_require__(521); -var toRegex = __webpack_require__(634); -var extend = __webpack_require__(642); +var braces = __webpack_require__(522); +var toRegex = __webpack_require__(635); +var extend = __webpack_require__(643); /** * Local dependencies */ -var compilers = __webpack_require__(645); -var parsers = __webpack_require__(678); -var cache = __webpack_require__(679); -var utils = __webpack_require__(680); +var compilers = __webpack_require__(646); +var parsers = __webpack_require__(679); +var cache = __webpack_require__(680); +var utils = __webpack_require__(681); var MAX_LENGTH = 1024 * 64; /** @@ -60301,7 +60452,7 @@ module.exports = micromatch; /***/ }), -/* 521 */ +/* 522 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60311,18 +60462,18 @@ module.exports = micromatch; * Module dependencies */ -var toRegex = __webpack_require__(522); -var unique = __webpack_require__(536); -var extend = __webpack_require__(531); +var toRegex = __webpack_require__(523); +var unique = __webpack_require__(537); +var extend = __webpack_require__(532); /** * Local dependencies */ -var compilers = __webpack_require__(537); -var parsers = __webpack_require__(554); -var Braces = __webpack_require__(564); -var utils = __webpack_require__(538); +var compilers = __webpack_require__(538); +var parsers = __webpack_require__(555); +var Braces = __webpack_require__(565); +var utils = __webpack_require__(539); var MAX_LENGTH = 1024 * 64; var cache = {}; @@ -60626,15 +60777,15 @@ module.exports = braces; /***/ }), -/* 522 */ +/* 523 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(523); -var extend = __webpack_require__(531); -var not = __webpack_require__(533); +var define = __webpack_require__(524); +var extend = __webpack_require__(532); +var not = __webpack_require__(534); var MAX_LENGTH = 1024 * 64; /** @@ -60781,7 +60932,7 @@ module.exports.makeRe = makeRe; /***/ }), -/* 523 */ +/* 524 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60794,7 +60945,7 @@ module.exports.makeRe = makeRe; -var isDescriptor = __webpack_require__(524); +var isDescriptor = __webpack_require__(525); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -60819,7 +60970,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 524 */ +/* 525 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60832,9 +60983,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(525); -var isAccessor = __webpack_require__(526); -var isData = __webpack_require__(529); +var typeOf = __webpack_require__(526); +var isAccessor = __webpack_require__(527); +var isData = __webpack_require__(530); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -60848,7 +60999,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 525 */ +/* 526 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -61001,7 +61152,7 @@ function isBuffer(val) { /***/ }), -/* 526 */ +/* 527 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61014,7 +61165,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(527); +var typeOf = __webpack_require__(528); // accessor descriptor properties var accessor = { @@ -61077,10 +61228,10 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 527 */ +/* 528 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(528); +var isBuffer = __webpack_require__(529); var toString = Object.prototype.toString; /** @@ -61199,7 +61350,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 528 */ +/* 529 */ /***/ (function(module, exports) { /*! @@ -61226,7 +61377,7 @@ function isSlowBuffer (obj) { /***/ }), -/* 529 */ +/* 530 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61239,7 +61390,7 @@ function isSlowBuffer (obj) { -var typeOf = __webpack_require__(530); +var typeOf = __webpack_require__(531); // data descriptor properties var data = { @@ -61288,10 +61439,10 @@ module.exports = isDataDescriptor; /***/ }), -/* 530 */ +/* 531 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(528); +var isBuffer = __webpack_require__(529); var toString = Object.prototype.toString; /** @@ -61410,13 +61561,13 @@ module.exports = function kindOf(val) { /***/ }), -/* 531 */ +/* 532 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(532); +var isObject = __webpack_require__(533); module.exports = function extend(o/*, objects*/) { if (!isObject(o)) { o = {}; } @@ -61450,7 +61601,7 @@ function hasOwn(obj, key) { /***/ }), -/* 532 */ +/* 533 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61470,13 +61621,13 @@ module.exports = function isExtendable(val) { /***/ }), -/* 533 */ +/* 534 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(534); +var extend = __webpack_require__(535); /** * The main export is a function that takes a `pattern` string and an `options` object. @@ -61543,13 +61694,13 @@ module.exports = toRegex; /***/ }), -/* 534 */ +/* 535 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(535); +var isObject = __webpack_require__(536); module.exports = function extend(o/*, objects*/) { if (!isObject(o)) { o = {}; } @@ -61583,7 +61734,7 @@ function hasOwn(obj, key) { /***/ }), -/* 535 */ +/* 536 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61603,7 +61754,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 536 */ +/* 537 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61653,13 +61804,13 @@ module.exports.immutable = function uniqueImmutable(arr) { /***/ }), -/* 537 */ +/* 538 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(538); +var utils = __webpack_require__(539); module.exports = function(braces, options) { braces.compiler @@ -61942,25 +62093,25 @@ function hasQueue(node) { /***/ }), -/* 538 */ +/* 539 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var splitString = __webpack_require__(539); +var splitString = __webpack_require__(540); var utils = module.exports; /** * Module dependencies */ -utils.extend = __webpack_require__(531); -utils.flatten = __webpack_require__(545); -utils.isObject = __webpack_require__(543); -utils.fillRange = __webpack_require__(546); -utils.repeat = __webpack_require__(553); -utils.unique = __webpack_require__(536); +utils.extend = __webpack_require__(532); +utils.flatten = __webpack_require__(546); +utils.isObject = __webpack_require__(544); +utils.fillRange = __webpack_require__(547); +utils.repeat = __webpack_require__(554); +utils.unique = __webpack_require__(537); utils.define = function(obj, key, val) { Object.defineProperty(obj, key, { @@ -62292,7 +62443,7 @@ utils.escapeRegex = function(str) { /***/ }), -/* 539 */ +/* 540 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62305,7 +62456,7 @@ utils.escapeRegex = function(str) { -var extend = __webpack_require__(540); +var extend = __webpack_require__(541); module.exports = function(str, options, fn) { if (typeof str !== 'string') { @@ -62470,14 +62621,14 @@ function keepEscaping(opts, str, idx) { /***/ }), -/* 540 */ +/* 541 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(541); -var assignSymbols = __webpack_require__(544); +var isExtendable = __webpack_require__(542); +var assignSymbols = __webpack_require__(545); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -62537,7 +62688,7 @@ function isEnum(obj, key) { /***/ }), -/* 541 */ +/* 542 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62550,7 +62701,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(542); +var isPlainObject = __webpack_require__(543); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -62558,7 +62709,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 542 */ +/* 543 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62571,7 +62722,7 @@ module.exports = function isExtendable(val) { -var isObject = __webpack_require__(543); +var isObject = __webpack_require__(544); function isObjectObject(o) { return isObject(o) === true @@ -62602,7 +62753,7 @@ module.exports = function isPlainObject(o) { /***/ }), -/* 543 */ +/* 544 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62621,7 +62772,7 @@ module.exports = function isObject(val) { /***/ }), -/* 544 */ +/* 545 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62668,7 +62819,7 @@ module.exports = function(receiver, objects) { /***/ }), -/* 545 */ +/* 546 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62697,7 +62848,7 @@ function flat(arr, res) { /***/ }), -/* 546 */ +/* 547 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62711,10 +62862,10 @@ function flat(arr, res) { var util = __webpack_require__(111); -var isNumber = __webpack_require__(547); -var extend = __webpack_require__(549); -var repeat = __webpack_require__(551); -var toRegex = __webpack_require__(552); +var isNumber = __webpack_require__(548); +var extend = __webpack_require__(550); +var repeat = __webpack_require__(552); +var toRegex = __webpack_require__(553); /** * Return a range of numbers or letters. @@ -62912,7 +63063,7 @@ module.exports = fillRange; /***/ }), -/* 547 */ +/* 548 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62925,7 +63076,7 @@ module.exports = fillRange; -var typeOf = __webpack_require__(548); +var typeOf = __webpack_require__(549); module.exports = function isNumber(num) { var type = typeOf(num); @@ -62941,10 +63092,10 @@ module.exports = function isNumber(num) { /***/ }), -/* 548 */ +/* 549 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(528); +var isBuffer = __webpack_require__(529); var toString = Object.prototype.toString; /** @@ -63063,13 +63214,13 @@ module.exports = function kindOf(val) { /***/ }), -/* 549 */ +/* 550 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(550); +var isObject = __webpack_require__(551); module.exports = function extend(o/*, objects*/) { if (!isObject(o)) { o = {}; } @@ -63103,7 +63254,7 @@ function hasOwn(obj, key) { /***/ }), -/* 550 */ +/* 551 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63123,7 +63274,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 551 */ +/* 552 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63200,7 +63351,7 @@ function repeat(str, num) { /***/ }), -/* 552 */ +/* 553 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63213,8 +63364,8 @@ function repeat(str, num) { -var repeat = __webpack_require__(551); -var isNumber = __webpack_require__(547); +var repeat = __webpack_require__(552); +var isNumber = __webpack_require__(548); var cache = {}; function toRegexRange(min, max, options) { @@ -63501,7 +63652,7 @@ module.exports = toRegexRange; /***/ }), -/* 553 */ +/* 554 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63526,14 +63677,14 @@ module.exports = function repeat(ele, num) { /***/ }), -/* 554 */ +/* 555 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Node = __webpack_require__(555); -var utils = __webpack_require__(538); +var Node = __webpack_require__(556); +var utils = __webpack_require__(539); /** * Braces parsers @@ -63893,15 +64044,15 @@ function concatNodes(pos, node, parent, options) { /***/ }), -/* 555 */ +/* 556 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(543); -var define = __webpack_require__(556); -var utils = __webpack_require__(563); +var isObject = __webpack_require__(544); +var define = __webpack_require__(557); +var utils = __webpack_require__(564); var ownNames; /** @@ -64392,7 +64543,7 @@ exports = module.exports = Node; /***/ }), -/* 556 */ +/* 557 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64405,7 +64556,7 @@ exports = module.exports = Node; -var isDescriptor = __webpack_require__(557); +var isDescriptor = __webpack_require__(558); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -64430,7 +64581,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 557 */ +/* 558 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64443,9 +64594,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(558); -var isAccessor = __webpack_require__(559); -var isData = __webpack_require__(561); +var typeOf = __webpack_require__(559); +var isAccessor = __webpack_require__(560); +var isData = __webpack_require__(562); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -64459,7 +64610,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 558 */ +/* 559 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -64594,7 +64745,7 @@ function isBuffer(val) { /***/ }), -/* 559 */ +/* 560 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64607,7 +64758,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(560); +var typeOf = __webpack_require__(561); // accessor descriptor properties var accessor = { @@ -64670,7 +64821,7 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 560 */ +/* 561 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -64805,7 +64956,7 @@ function isBuffer(val) { /***/ }), -/* 561 */ +/* 562 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64818,7 +64969,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(562); +var typeOf = __webpack_require__(563); module.exports = function isDataDescriptor(obj, prop) { // data descriptor properties @@ -64861,7 +65012,7 @@ module.exports = function isDataDescriptor(obj, prop) { /***/ }), -/* 562 */ +/* 563 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -64996,13 +65147,13 @@ function isBuffer(val) { /***/ }), -/* 563 */ +/* 564 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(548); +var typeOf = __webpack_require__(549); var utils = module.exports; /** @@ -66022,17 +66173,17 @@ function assert(val, message) { /***/ }), -/* 564 */ +/* 565 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(531); -var Snapdragon = __webpack_require__(565); -var compilers = __webpack_require__(537); -var parsers = __webpack_require__(554); -var utils = __webpack_require__(538); +var extend = __webpack_require__(532); +var Snapdragon = __webpack_require__(566); +var compilers = __webpack_require__(538); +var parsers = __webpack_require__(555); +var utils = __webpack_require__(539); /** * Customize Snapdragon parser and renderer @@ -66133,17 +66284,17 @@ module.exports = Braces; /***/ }), -/* 565 */ +/* 566 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Base = __webpack_require__(566); -var define = __webpack_require__(592); -var Compiler = __webpack_require__(602); -var Parser = __webpack_require__(631); -var utils = __webpack_require__(611); +var Base = __webpack_require__(567); +var define = __webpack_require__(593); +var Compiler = __webpack_require__(603); +var Parser = __webpack_require__(632); +var utils = __webpack_require__(612); var regexCache = {}; var cache = {}; @@ -66314,20 +66465,20 @@ module.exports.Parser = Parser; /***/ }), -/* 566 */ +/* 567 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(111); -var define = __webpack_require__(567); -var CacheBase = __webpack_require__(568); -var Emitter = __webpack_require__(569); -var isObject = __webpack_require__(543); -var merge = __webpack_require__(586); -var pascal = __webpack_require__(589); -var cu = __webpack_require__(590); +var define = __webpack_require__(568); +var CacheBase = __webpack_require__(569); +var Emitter = __webpack_require__(570); +var isObject = __webpack_require__(544); +var merge = __webpack_require__(587); +var pascal = __webpack_require__(590); +var cu = __webpack_require__(591); /** * Optionally define a custom `cache` namespace to use. @@ -66756,7 +66907,7 @@ module.exports.namespace = namespace; /***/ }), -/* 567 */ +/* 568 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66769,7 +66920,7 @@ module.exports.namespace = namespace; -var isDescriptor = __webpack_require__(557); +var isDescriptor = __webpack_require__(558); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -66794,21 +66945,21 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 568 */ +/* 569 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(543); -var Emitter = __webpack_require__(569); -var visit = __webpack_require__(570); -var toPath = __webpack_require__(573); -var union = __webpack_require__(574); -var del = __webpack_require__(578); -var get = __webpack_require__(576); -var has = __webpack_require__(583); -var set = __webpack_require__(577); +var isObject = __webpack_require__(544); +var Emitter = __webpack_require__(570); +var visit = __webpack_require__(571); +var toPath = __webpack_require__(574); +var union = __webpack_require__(575); +var del = __webpack_require__(579); +var get = __webpack_require__(577); +var has = __webpack_require__(584); +var set = __webpack_require__(578); /** * Create a `Cache` constructor that when instantiated will @@ -67062,7 +67213,7 @@ module.exports.namespace = namespace; /***/ }), -/* 569 */ +/* 570 */ /***/ (function(module, exports, __webpack_require__) { @@ -67231,7 +67382,7 @@ Emitter.prototype.hasListeners = function(event){ /***/ }), -/* 570 */ +/* 571 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67244,8 +67395,8 @@ Emitter.prototype.hasListeners = function(event){ -var visit = __webpack_require__(571); -var mapVisit = __webpack_require__(572); +var visit = __webpack_require__(572); +var mapVisit = __webpack_require__(573); module.exports = function(collection, method, val) { var result; @@ -67268,7 +67419,7 @@ module.exports = function(collection, method, val) { /***/ }), -/* 571 */ +/* 572 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67281,7 +67432,7 @@ module.exports = function(collection, method, val) { -var isObject = __webpack_require__(543); +var isObject = __webpack_require__(544); module.exports = function visit(thisArg, method, target, val) { if (!isObject(thisArg) && typeof thisArg !== 'function') { @@ -67308,14 +67459,14 @@ module.exports = function visit(thisArg, method, target, val) { /***/ }), -/* 572 */ +/* 573 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(111); -var visit = __webpack_require__(571); +var visit = __webpack_require__(572); /** * Map `visit` over an array of objects. @@ -67352,7 +67503,7 @@ function isObject(val) { /***/ }), -/* 573 */ +/* 574 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67365,7 +67516,7 @@ function isObject(val) { -var typeOf = __webpack_require__(548); +var typeOf = __webpack_require__(549); module.exports = function toPath(args) { if (typeOf(args) !== 'arguments') { @@ -67392,16 +67543,16 @@ function filter(arr) { /***/ }), -/* 574 */ +/* 575 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(535); -var union = __webpack_require__(575); -var get = __webpack_require__(576); -var set = __webpack_require__(577); +var isObject = __webpack_require__(536); +var union = __webpack_require__(576); +var get = __webpack_require__(577); +var set = __webpack_require__(578); module.exports = function unionValue(obj, prop, value) { if (!isObject(obj)) { @@ -67429,7 +67580,7 @@ function arrayify(val) { /***/ }), -/* 575 */ +/* 576 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67465,7 +67616,7 @@ module.exports = function union(init) { /***/ }), -/* 576 */ +/* 577 */ /***/ (function(module, exports) { /*! @@ -67521,7 +67672,7 @@ function toString(val) { /***/ }), -/* 577 */ +/* 578 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67534,10 +67685,10 @@ function toString(val) { -var split = __webpack_require__(539); -var extend = __webpack_require__(534); -var isPlainObject = __webpack_require__(542); -var isObject = __webpack_require__(535); +var split = __webpack_require__(540); +var extend = __webpack_require__(535); +var isPlainObject = __webpack_require__(543); +var isObject = __webpack_require__(536); module.exports = function(obj, prop, val) { if (!isObject(obj)) { @@ -67583,7 +67734,7 @@ function isValidKey(key) { /***/ }), -/* 578 */ +/* 579 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67596,8 +67747,8 @@ function isValidKey(key) { -var isObject = __webpack_require__(543); -var has = __webpack_require__(579); +var isObject = __webpack_require__(544); +var has = __webpack_require__(580); module.exports = function unset(obj, prop) { if (!isObject(obj)) { @@ -67622,7 +67773,7 @@ module.exports = function unset(obj, prop) { /***/ }), -/* 579 */ +/* 580 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67635,9 +67786,9 @@ module.exports = function unset(obj, prop) { -var isObject = __webpack_require__(580); -var hasValues = __webpack_require__(582); -var get = __webpack_require__(576); +var isObject = __webpack_require__(581); +var hasValues = __webpack_require__(583); +var get = __webpack_require__(577); module.exports = function(obj, prop, noZero) { if (isObject(obj)) { @@ -67648,7 +67799,7 @@ module.exports = function(obj, prop, noZero) { /***/ }), -/* 580 */ +/* 581 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67661,7 +67812,7 @@ module.exports = function(obj, prop, noZero) { -var isArray = __webpack_require__(581); +var isArray = __webpack_require__(582); module.exports = function isObject(val) { return val != null && typeof val === 'object' && isArray(val) === false; @@ -67669,7 +67820,7 @@ module.exports = function isObject(val) { /***/ }), -/* 581 */ +/* 582 */ /***/ (function(module, exports) { var toString = {}.toString; @@ -67680,7 +67831,7 @@ module.exports = Array.isArray || function (arr) { /***/ }), -/* 582 */ +/* 583 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67723,7 +67874,7 @@ module.exports = function hasValue(o, noZero) { /***/ }), -/* 583 */ +/* 584 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67736,9 +67887,9 @@ module.exports = function hasValue(o, noZero) { -var isObject = __webpack_require__(543); -var hasValues = __webpack_require__(584); -var get = __webpack_require__(576); +var isObject = __webpack_require__(544); +var hasValues = __webpack_require__(585); +var get = __webpack_require__(577); module.exports = function(val, prop) { return hasValues(isObject(val) && prop ? get(val, prop) : val); @@ -67746,7 +67897,7 @@ module.exports = function(val, prop) { /***/ }), -/* 584 */ +/* 585 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67759,8 +67910,8 @@ module.exports = function(val, prop) { -var typeOf = __webpack_require__(585); -var isNumber = __webpack_require__(547); +var typeOf = __webpack_require__(586); +var isNumber = __webpack_require__(548); module.exports = function hasValue(val) { // is-number checks for NaN and other edge cases @@ -67813,10 +67964,10 @@ module.exports = function hasValue(val) { /***/ }), -/* 585 */ +/* 586 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(528); +var isBuffer = __webpack_require__(529); var toString = Object.prototype.toString; /** @@ -67938,14 +68089,14 @@ module.exports = function kindOf(val) { /***/ }), -/* 586 */ +/* 587 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(587); -var forIn = __webpack_require__(588); +var isExtendable = __webpack_require__(588); +var forIn = __webpack_require__(589); function mixinDeep(target, objects) { var len = arguments.length, i = 0; @@ -68009,7 +68160,7 @@ module.exports = mixinDeep; /***/ }), -/* 587 */ +/* 588 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68022,7 +68173,7 @@ module.exports = mixinDeep; -var isPlainObject = __webpack_require__(542); +var isPlainObject = __webpack_require__(543); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -68030,7 +68181,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 588 */ +/* 589 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68053,7 +68204,7 @@ module.exports = function forIn(obj, fn, thisArg) { /***/ }), -/* 589 */ +/* 590 */ /***/ (function(module, exports) { /*! @@ -68080,14 +68231,14 @@ module.exports = pascalcase; /***/ }), -/* 590 */ +/* 591 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(111); -var utils = __webpack_require__(591); +var utils = __webpack_require__(592); /** * Expose class utils @@ -68452,7 +68603,7 @@ cu.bubble = function(Parent, events) { /***/ }), -/* 591 */ +/* 592 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68466,10 +68617,10 @@ var utils = {}; * Lazily required module dependencies */ -utils.union = __webpack_require__(575); -utils.define = __webpack_require__(592); -utils.isObj = __webpack_require__(543); -utils.staticExtend = __webpack_require__(599); +utils.union = __webpack_require__(576); +utils.define = __webpack_require__(593); +utils.isObj = __webpack_require__(544); +utils.staticExtend = __webpack_require__(600); /** @@ -68480,7 +68631,7 @@ module.exports = utils; /***/ }), -/* 592 */ +/* 593 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68493,7 +68644,7 @@ module.exports = utils; -var isDescriptor = __webpack_require__(593); +var isDescriptor = __webpack_require__(594); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -68518,7 +68669,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 593 */ +/* 594 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68531,9 +68682,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(594); -var isAccessor = __webpack_require__(595); -var isData = __webpack_require__(597); +var typeOf = __webpack_require__(595); +var isAccessor = __webpack_require__(596); +var isData = __webpack_require__(598); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -68547,7 +68698,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 594 */ +/* 595 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -68700,7 +68851,7 @@ function isBuffer(val) { /***/ }), -/* 595 */ +/* 596 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68713,7 +68864,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(596); +var typeOf = __webpack_require__(597); // accessor descriptor properties var accessor = { @@ -68776,10 +68927,10 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 596 */ +/* 597 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(528); +var isBuffer = __webpack_require__(529); var toString = Object.prototype.toString; /** @@ -68898,7 +69049,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 597 */ +/* 598 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68911,7 +69062,7 @@ module.exports = function kindOf(val) { -var typeOf = __webpack_require__(598); +var typeOf = __webpack_require__(599); // data descriptor properties var data = { @@ -68960,10 +69111,10 @@ module.exports = isDataDescriptor; /***/ }), -/* 598 */ +/* 599 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(528); +var isBuffer = __webpack_require__(529); var toString = Object.prototype.toString; /** @@ -69082,7 +69233,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 599 */ +/* 600 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69095,8 +69246,8 @@ module.exports = function kindOf(val) { -var copy = __webpack_require__(600); -var define = __webpack_require__(592); +var copy = __webpack_require__(601); +var define = __webpack_require__(593); var util = __webpack_require__(111); /** @@ -69179,15 +69330,15 @@ module.exports = extend; /***/ }), -/* 600 */ +/* 601 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(548); -var copyDescriptor = __webpack_require__(601); -var define = __webpack_require__(592); +var typeOf = __webpack_require__(549); +var copyDescriptor = __webpack_require__(602); +var define = __webpack_require__(593); /** * Copy static properties, prototype properties, and descriptors from one object to another. @@ -69360,7 +69511,7 @@ module.exports.has = has; /***/ }), -/* 601 */ +/* 602 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69448,16 +69599,16 @@ function isObject(val) { /***/ }), -/* 602 */ +/* 603 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(603); -var define = __webpack_require__(592); -var debug = __webpack_require__(605)('snapdragon:compiler'); -var utils = __webpack_require__(611); +var use = __webpack_require__(604); +var define = __webpack_require__(593); +var debug = __webpack_require__(606)('snapdragon:compiler'); +var utils = __webpack_require__(612); /** * Create a new `Compiler` with the given `options`. @@ -69611,7 +69762,7 @@ Compiler.prototype = { // source map support if (opts.sourcemap) { - var sourcemaps = __webpack_require__(630); + var sourcemaps = __webpack_require__(631); sourcemaps(this); this.mapVisit(this.ast.nodes); this.applySourceMaps(); @@ -69632,7 +69783,7 @@ module.exports = Compiler; /***/ }), -/* 603 */ +/* 604 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69645,7 +69796,7 @@ module.exports = Compiler; -var utils = __webpack_require__(604); +var utils = __webpack_require__(605); module.exports = function base(app, opts) { if (!utils.isObject(app) && typeof app !== 'function') { @@ -69760,7 +69911,7 @@ module.exports = function base(app, opts) { /***/ }), -/* 604 */ +/* 605 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69774,8 +69925,8 @@ var utils = {}; * Lazily required module dependencies */ -utils.define = __webpack_require__(592); -utils.isObject = __webpack_require__(543); +utils.define = __webpack_require__(593); +utils.isObject = __webpack_require__(544); utils.isString = function(val) { @@ -69790,7 +69941,7 @@ module.exports = utils; /***/ }), -/* 605 */ +/* 606 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -69799,14 +69950,14 @@ module.exports = utils; */ if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(606); + module.exports = __webpack_require__(607); } else { - module.exports = __webpack_require__(609); + module.exports = __webpack_require__(610); } /***/ }), -/* 606 */ +/* 607 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -69815,7 +69966,7 @@ if (typeof process !== 'undefined' && process.type === 'renderer') { * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(607); +exports = module.exports = __webpack_require__(608); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; @@ -69997,7 +70148,7 @@ function localstorage() { /***/ }), -/* 607 */ +/* 608 */ /***/ (function(module, exports, __webpack_require__) { @@ -70013,7 +70164,7 @@ exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; -exports.humanize = __webpack_require__(608); +exports.humanize = __webpack_require__(609); /** * The currently active debug mode names, and names to skip. @@ -70205,7 +70356,7 @@ function coerce(val) { /***/ }), -/* 608 */ +/* 609 */ /***/ (function(module, exports) { /** @@ -70363,14 +70514,14 @@ function plural(ms, n, name) { /***/ }), -/* 609 */ +/* 610 */ /***/ (function(module, exports, __webpack_require__) { /** * Module dependencies. */ -var tty = __webpack_require__(277); +var tty = __webpack_require__(278); var util = __webpack_require__(111); /** @@ -70379,7 +70530,7 @@ var util = __webpack_require__(111); * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(607); +exports = module.exports = __webpack_require__(608); exports.init = init; exports.log = log; exports.formatArgs = formatArgs; @@ -70558,7 +70709,7 @@ function createWritableStdioStream (fd) { case 'PIPE': case 'TCP': - var net = __webpack_require__(610); + var net = __webpack_require__(611); stream = new net.Socket({ fd: fd, readable: false, @@ -70617,13 +70768,13 @@ exports.enable(load()); /***/ }), -/* 610 */ +/* 611 */ /***/ (function(module, exports) { module.exports = require("net"); /***/ }), -/* 611 */ +/* 612 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70633,9 +70784,9 @@ module.exports = require("net"); * Module dependencies */ -exports.extend = __webpack_require__(534); -exports.SourceMap = __webpack_require__(612); -exports.sourceMapResolve = __webpack_require__(623); +exports.extend = __webpack_require__(535); +exports.SourceMap = __webpack_require__(613); +exports.sourceMapResolve = __webpack_require__(624); /** * Convert backslash in the given string to forward slashes @@ -70678,7 +70829,7 @@ exports.last = function(arr, n) { /***/ }), -/* 612 */ +/* 613 */ /***/ (function(module, exports, __webpack_require__) { /* @@ -70686,13 +70837,13 @@ exports.last = function(arr, n) { * Licensed under the New BSD license. See LICENSE.txt or: * http://opensource.org/licenses/BSD-3-Clause */ -exports.SourceMapGenerator = __webpack_require__(613).SourceMapGenerator; -exports.SourceMapConsumer = __webpack_require__(619).SourceMapConsumer; -exports.SourceNode = __webpack_require__(622).SourceNode; +exports.SourceMapGenerator = __webpack_require__(614).SourceMapGenerator; +exports.SourceMapConsumer = __webpack_require__(620).SourceMapConsumer; +exports.SourceNode = __webpack_require__(623).SourceNode; /***/ }), -/* 613 */ +/* 614 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -70702,10 +70853,10 @@ exports.SourceNode = __webpack_require__(622).SourceNode; * http://opensource.org/licenses/BSD-3-Clause */ -var base64VLQ = __webpack_require__(614); -var util = __webpack_require__(616); -var ArraySet = __webpack_require__(617).ArraySet; -var MappingList = __webpack_require__(618).MappingList; +var base64VLQ = __webpack_require__(615); +var util = __webpack_require__(617); +var ArraySet = __webpack_require__(618).ArraySet; +var MappingList = __webpack_require__(619).MappingList; /** * An instance of the SourceMapGenerator represents a source map which is @@ -71114,7 +71265,7 @@ exports.SourceMapGenerator = SourceMapGenerator; /***/ }), -/* 614 */ +/* 615 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -71154,7 +71305,7 @@ exports.SourceMapGenerator = SourceMapGenerator; * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var base64 = __webpack_require__(615); +var base64 = __webpack_require__(616); // A single base 64 digit can contain 6 bits of data. For the base 64 variable // length quantities we use in the source map spec, the first bit is the sign, @@ -71260,7 +71411,7 @@ exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { /***/ }), -/* 615 */ +/* 616 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -71333,7 +71484,7 @@ exports.decode = function (charCode) { /***/ }), -/* 616 */ +/* 617 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -71756,7 +71907,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate /***/ }), -/* 617 */ +/* 618 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -71766,7 +71917,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(616); +var util = __webpack_require__(617); var has = Object.prototype.hasOwnProperty; var hasNativeMap = typeof Map !== "undefined"; @@ -71883,7 +72034,7 @@ exports.ArraySet = ArraySet; /***/ }), -/* 618 */ +/* 619 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -71893,7 +72044,7 @@ exports.ArraySet = ArraySet; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(616); +var util = __webpack_require__(617); /** * Determine whether mappingB is after mappingA with respect to generated @@ -71968,7 +72119,7 @@ exports.MappingList = MappingList; /***/ }), -/* 619 */ +/* 620 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -71978,11 +72129,11 @@ exports.MappingList = MappingList; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(616); -var binarySearch = __webpack_require__(620); -var ArraySet = __webpack_require__(617).ArraySet; -var base64VLQ = __webpack_require__(614); -var quickSort = __webpack_require__(621).quickSort; +var util = __webpack_require__(617); +var binarySearch = __webpack_require__(621); +var ArraySet = __webpack_require__(618).ArraySet; +var base64VLQ = __webpack_require__(615); +var quickSort = __webpack_require__(622).quickSort; function SourceMapConsumer(aSourceMap) { var sourceMap = aSourceMap; @@ -73056,7 +73207,7 @@ exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer; /***/ }), -/* 620 */ +/* 621 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -73173,7 +73324,7 @@ exports.search = function search(aNeedle, aHaystack, aCompare, aBias) { /***/ }), -/* 621 */ +/* 622 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -73293,7 +73444,7 @@ exports.quickSort = function (ary, comparator) { /***/ }), -/* 622 */ +/* 623 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -73303,8 +73454,8 @@ exports.quickSort = function (ary, comparator) { * http://opensource.org/licenses/BSD-3-Clause */ -var SourceMapGenerator = __webpack_require__(613).SourceMapGenerator; -var util = __webpack_require__(616); +var SourceMapGenerator = __webpack_require__(614).SourceMapGenerator; +var util = __webpack_require__(617); // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other // operating systems these days (capturing the result). @@ -73712,17 +73863,17 @@ exports.SourceNode = SourceNode; /***/ }), -/* 623 */ +/* 624 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014, 2015, 2016, 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var sourceMappingURL = __webpack_require__(624) -var resolveUrl = __webpack_require__(625) -var decodeUriComponent = __webpack_require__(626) -var urix = __webpack_require__(628) -var atob = __webpack_require__(629) +var sourceMappingURL = __webpack_require__(625) +var resolveUrl = __webpack_require__(626) +var decodeUriComponent = __webpack_require__(627) +var urix = __webpack_require__(629) +var atob = __webpack_require__(630) @@ -74020,7 +74171,7 @@ module.exports = { /***/ }), -/* 624 */ +/* 625 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;// Copyright 2014 Simon Lydell @@ -74083,7 +74234,7 @@ void (function(root, factory) { /***/ }), -/* 625 */ +/* 626 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -74101,13 +74252,13 @@ module.exports = resolveUrl /***/ }), -/* 626 */ +/* 627 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var decodeUriComponent = __webpack_require__(627) +var decodeUriComponent = __webpack_require__(628) function customDecodeUriComponent(string) { // `decodeUriComponent` turns `+` into ` `, but that's not wanted. @@ -74118,7 +74269,7 @@ module.exports = customDecodeUriComponent /***/ }), -/* 627 */ +/* 628 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74219,7 +74370,7 @@ module.exports = function (encodedURI) { /***/ }), -/* 628 */ +/* 629 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -74242,7 +74393,7 @@ module.exports = urix /***/ }), -/* 629 */ +/* 630 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74256,7 +74407,7 @@ module.exports = atob.atob = atob; /***/ }), -/* 630 */ +/* 631 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74264,8 +74415,8 @@ module.exports = atob.atob = atob; var fs = __webpack_require__(132); var path = __webpack_require__(4); -var define = __webpack_require__(592); -var utils = __webpack_require__(611); +var define = __webpack_require__(593); +var utils = __webpack_require__(612); /** * Expose `mixin()`. @@ -74408,19 +74559,19 @@ exports.comment = function(node) { /***/ }), -/* 631 */ +/* 632 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(603); +var use = __webpack_require__(604); var util = __webpack_require__(111); -var Cache = __webpack_require__(632); -var define = __webpack_require__(592); -var debug = __webpack_require__(605)('snapdragon:parser'); -var Position = __webpack_require__(633); -var utils = __webpack_require__(611); +var Cache = __webpack_require__(633); +var define = __webpack_require__(593); +var debug = __webpack_require__(606)('snapdragon:parser'); +var Position = __webpack_require__(634); +var utils = __webpack_require__(612); /** * Create a new `Parser` with the given `input` and `options`. @@ -74948,7 +75099,7 @@ module.exports = Parser; /***/ }), -/* 632 */ +/* 633 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75055,13 +75206,13 @@ MapCache.prototype.del = function mapDelete(key) { /***/ }), -/* 633 */ +/* 634 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(592); +var define = __webpack_require__(593); /** * Store position for a node @@ -75076,16 +75227,16 @@ module.exports = function Position(start, parser) { /***/ }), -/* 634 */ +/* 635 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var safe = __webpack_require__(635); -var define = __webpack_require__(641); -var extend = __webpack_require__(642); -var not = __webpack_require__(644); +var safe = __webpack_require__(636); +var define = __webpack_require__(642); +var extend = __webpack_require__(643); +var not = __webpack_require__(645); var MAX_LENGTH = 1024 * 64; /** @@ -75238,10 +75389,10 @@ module.exports.makeRe = makeRe; /***/ }), -/* 635 */ +/* 636 */ /***/ (function(module, exports, __webpack_require__) { -var parse = __webpack_require__(636); +var parse = __webpack_require__(637); var types = parse.types; module.exports = function (re, opts) { @@ -75287,13 +75438,13 @@ function isRegExp (x) { /***/ }), -/* 636 */ +/* 637 */ /***/ (function(module, exports, __webpack_require__) { -var util = __webpack_require__(637); -var types = __webpack_require__(638); -var sets = __webpack_require__(639); -var positions = __webpack_require__(640); +var util = __webpack_require__(638); +var types = __webpack_require__(639); +var sets = __webpack_require__(640); +var positions = __webpack_require__(641); module.exports = function(regexpStr) { @@ -75575,11 +75726,11 @@ module.exports.types = types; /***/ }), -/* 637 */ +/* 638 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(638); -var sets = __webpack_require__(639); +var types = __webpack_require__(639); +var sets = __webpack_require__(640); // All of these are private and only used by randexp. @@ -75692,7 +75843,7 @@ exports.error = function(regexp, msg) { /***/ }), -/* 638 */ +/* 639 */ /***/ (function(module, exports) { module.exports = { @@ -75708,10 +75859,10 @@ module.exports = { /***/ }), -/* 639 */ +/* 640 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(638); +var types = __webpack_require__(639); var INTS = function() { return [{ type: types.RANGE , from: 48, to: 57 }]; @@ -75796,10 +75947,10 @@ exports.anyChar = function() { /***/ }), -/* 640 */ +/* 641 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(638); +var types = __webpack_require__(639); exports.wordBoundary = function() { return { type: types.POSITION, value: 'b' }; @@ -75819,7 +75970,7 @@ exports.end = function() { /***/ }), -/* 641 */ +/* 642 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75832,8 +75983,8 @@ exports.end = function() { -var isobject = __webpack_require__(543); -var isDescriptor = __webpack_require__(557); +var isobject = __webpack_require__(544); +var isDescriptor = __webpack_require__(558); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -75864,14 +76015,14 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 642 */ +/* 643 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(643); -var assignSymbols = __webpack_require__(544); +var isExtendable = __webpack_require__(644); +var assignSymbols = __webpack_require__(545); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -75931,7 +76082,7 @@ function isEnum(obj, key) { /***/ }), -/* 643 */ +/* 644 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75944,7 +76095,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(542); +var isPlainObject = __webpack_require__(543); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -75952,14 +76103,14 @@ module.exports = function isExtendable(val) { /***/ }), -/* 644 */ +/* 645 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(642); -var safe = __webpack_require__(635); +var extend = __webpack_require__(643); +var safe = __webpack_require__(636); /** * The main export is a function that takes a `pattern` string and an `options` object. @@ -76031,14 +76182,14 @@ module.exports = toRegex; /***/ }), -/* 645 */ +/* 646 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var nanomatch = __webpack_require__(646); -var extglob = __webpack_require__(662); +var nanomatch = __webpack_require__(647); +var extglob = __webpack_require__(663); module.exports = function(snapdragon) { var compilers = snapdragon.compiler.compilers; @@ -76115,7 +76266,7 @@ function escapeExtglobs(compiler) { /***/ }), -/* 646 */ +/* 647 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76126,17 +76277,17 @@ function escapeExtglobs(compiler) { */ var util = __webpack_require__(111); -var toRegex = __webpack_require__(647); -var extend = __webpack_require__(648); +var toRegex = __webpack_require__(648); +var extend = __webpack_require__(649); /** * Local dependencies */ -var compilers = __webpack_require__(650); -var parsers = __webpack_require__(651); -var cache = __webpack_require__(654); -var utils = __webpack_require__(656); +var compilers = __webpack_require__(651); +var parsers = __webpack_require__(652); +var cache = __webpack_require__(655); +var utils = __webpack_require__(657); var MAX_LENGTH = 1024 * 64; /** @@ -76960,15 +77111,15 @@ module.exports = nanomatch; /***/ }), -/* 647 */ +/* 648 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(592); -var extend = __webpack_require__(534); -var not = __webpack_require__(533); +var define = __webpack_require__(593); +var extend = __webpack_require__(535); +var not = __webpack_require__(534); var MAX_LENGTH = 1024 * 64; /** @@ -77115,14 +77266,14 @@ module.exports.makeRe = makeRe; /***/ }), -/* 648 */ +/* 649 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(649); -var assignSymbols = __webpack_require__(544); +var isExtendable = __webpack_require__(650); +var assignSymbols = __webpack_require__(545); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -77182,7 +77333,7 @@ function isEnum(obj, key) { /***/ }), -/* 649 */ +/* 650 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77195,7 +77346,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(542); +var isPlainObject = __webpack_require__(543); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -77203,7 +77354,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 650 */ +/* 651 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77549,15 +77700,15 @@ module.exports = function(nanomatch, options) { /***/ }), -/* 651 */ +/* 652 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regexNot = __webpack_require__(533); -var toRegex = __webpack_require__(647); -var isOdd = __webpack_require__(652); +var regexNot = __webpack_require__(534); +var toRegex = __webpack_require__(648); +var isOdd = __webpack_require__(653); /** * Characters to use in negation regex (we want to "not" match @@ -77943,7 +78094,7 @@ module.exports.not = NOT_REGEX; /***/ }), -/* 652 */ +/* 653 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77956,7 +78107,7 @@ module.exports.not = NOT_REGEX; -var isNumber = __webpack_require__(653); +var isNumber = __webpack_require__(654); module.exports = function isOdd(i) { if (!isNumber(i)) { @@ -77970,7 +78121,7 @@ module.exports = function isOdd(i) { /***/ }), -/* 653 */ +/* 654 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77998,14 +78149,14 @@ module.exports = function isNumber(num) { /***/ }), -/* 654 */ +/* 655 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(655))(); +module.exports = new (__webpack_require__(656))(); /***/ }), -/* 655 */ +/* 656 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78018,7 +78169,7 @@ module.exports = new (__webpack_require__(655))(); -var MapCache = __webpack_require__(632); +var MapCache = __webpack_require__(633); /** * Create a new `FragmentCache` with an optional object to use for `caches`. @@ -78140,7 +78291,7 @@ exports = module.exports = FragmentCache; /***/ }), -/* 656 */ +/* 657 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78153,14 +78304,14 @@ var path = __webpack_require__(4); * Module dependencies */ -var isWindows = __webpack_require__(657)(); -var Snapdragon = __webpack_require__(565); -utils.define = __webpack_require__(658); -utils.diff = __webpack_require__(659); -utils.extend = __webpack_require__(648); -utils.pick = __webpack_require__(660); -utils.typeOf = __webpack_require__(661); -utils.unique = __webpack_require__(536); +var isWindows = __webpack_require__(658)(); +var Snapdragon = __webpack_require__(566); +utils.define = __webpack_require__(659); +utils.diff = __webpack_require__(660); +utils.extend = __webpack_require__(649); +utils.pick = __webpack_require__(661); +utils.typeOf = __webpack_require__(662); +utils.unique = __webpack_require__(537); /** * Returns true if the given value is effectively an empty string @@ -78526,7 +78677,7 @@ utils.unixify = function(options) { /***/ }), -/* 657 */ +/* 658 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! @@ -78554,7 +78705,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ /***/ }), -/* 658 */ +/* 659 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78567,8 +78718,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ -var isobject = __webpack_require__(543); -var isDescriptor = __webpack_require__(557); +var isobject = __webpack_require__(544); +var isDescriptor = __webpack_require__(558); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -78599,7 +78750,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 659 */ +/* 660 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78653,7 +78804,7 @@ function diffArray(one, two) { /***/ }), -/* 660 */ +/* 661 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78666,7 +78817,7 @@ function diffArray(one, two) { -var isObject = __webpack_require__(543); +var isObject = __webpack_require__(544); module.exports = function pick(obj, keys) { if (!isObject(obj) && typeof obj !== 'function') { @@ -78695,7 +78846,7 @@ module.exports = function pick(obj, keys) { /***/ }), -/* 661 */ +/* 662 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -78830,7 +78981,7 @@ function isBuffer(val) { /***/ }), -/* 662 */ +/* 663 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78840,18 +78991,18 @@ function isBuffer(val) { * Module dependencies */ -var extend = __webpack_require__(534); -var unique = __webpack_require__(536); -var toRegex = __webpack_require__(647); +var extend = __webpack_require__(535); +var unique = __webpack_require__(537); +var toRegex = __webpack_require__(648); /** * Local dependencies */ -var compilers = __webpack_require__(663); -var parsers = __webpack_require__(674); -var Extglob = __webpack_require__(677); -var utils = __webpack_require__(676); +var compilers = __webpack_require__(664); +var parsers = __webpack_require__(675); +var Extglob = __webpack_require__(678); +var utils = __webpack_require__(677); var MAX_LENGTH = 1024 * 64; /** @@ -79168,13 +79319,13 @@ module.exports = extglob; /***/ }), -/* 663 */ +/* 664 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(664); +var brackets = __webpack_require__(665); /** * Extglob compilers @@ -79344,7 +79495,7 @@ module.exports = function(extglob) { /***/ }), -/* 664 */ +/* 665 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79354,17 +79505,17 @@ module.exports = function(extglob) { * Local dependencies */ -var compilers = __webpack_require__(665); -var parsers = __webpack_require__(667); +var compilers = __webpack_require__(666); +var parsers = __webpack_require__(668); /** * Module dependencies */ -var debug = __webpack_require__(669)('expand-brackets'); -var extend = __webpack_require__(534); -var Snapdragon = __webpack_require__(565); -var toRegex = __webpack_require__(647); +var debug = __webpack_require__(670)('expand-brackets'); +var extend = __webpack_require__(535); +var Snapdragon = __webpack_require__(566); +var toRegex = __webpack_require__(648); /** * Parses the given POSIX character class `pattern` and returns a @@ -79562,13 +79713,13 @@ module.exports = brackets; /***/ }), -/* 665 */ +/* 666 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var posix = __webpack_require__(666); +var posix = __webpack_require__(667); module.exports = function(brackets) { brackets.compiler @@ -79656,7 +79807,7 @@ module.exports = function(brackets) { /***/ }), -/* 666 */ +/* 667 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79685,14 +79836,14 @@ module.exports = { /***/ }), -/* 667 */ +/* 668 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(668); -var define = __webpack_require__(592); +var utils = __webpack_require__(669); +var define = __webpack_require__(593); /** * Text regex @@ -79911,14 +80062,14 @@ module.exports.TEXT_REGEX = TEXT_REGEX; /***/ }), -/* 668 */ +/* 669 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var toRegex = __webpack_require__(647); -var regexNot = __webpack_require__(533); +var toRegex = __webpack_require__(648); +var regexNot = __webpack_require__(534); var cached; /** @@ -79952,7 +80103,7 @@ exports.createRegex = function(pattern, include) { /***/ }), -/* 669 */ +/* 670 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -79961,14 +80112,14 @@ exports.createRegex = function(pattern, include) { */ if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(670); + module.exports = __webpack_require__(671); } else { - module.exports = __webpack_require__(673); + module.exports = __webpack_require__(674); } /***/ }), -/* 670 */ +/* 671 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -79977,7 +80128,7 @@ if (typeof process !== 'undefined' && process.type === 'renderer') { * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(671); +exports = module.exports = __webpack_require__(672); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; @@ -80159,7 +80310,7 @@ function localstorage() { /***/ }), -/* 671 */ +/* 672 */ /***/ (function(module, exports, __webpack_require__) { @@ -80175,7 +80326,7 @@ exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; -exports.humanize = __webpack_require__(672); +exports.humanize = __webpack_require__(673); /** * The currently active debug mode names, and names to skip. @@ -80367,7 +80518,7 @@ function coerce(val) { /***/ }), -/* 672 */ +/* 673 */ /***/ (function(module, exports) { /** @@ -80525,14 +80676,14 @@ function plural(ms, n, name) { /***/ }), -/* 673 */ +/* 674 */ /***/ (function(module, exports, __webpack_require__) { /** * Module dependencies. */ -var tty = __webpack_require__(277); +var tty = __webpack_require__(278); var util = __webpack_require__(111); /** @@ -80541,7 +80692,7 @@ var util = __webpack_require__(111); * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(671); +exports = module.exports = __webpack_require__(672); exports.init = init; exports.log = log; exports.formatArgs = formatArgs; @@ -80720,7 +80871,7 @@ function createWritableStdioStream (fd) { case 'PIPE': case 'TCP': - var net = __webpack_require__(610); + var net = __webpack_require__(611); stream = new net.Socket({ fd: fd, readable: false, @@ -80779,15 +80930,15 @@ exports.enable(load()); /***/ }), -/* 674 */ +/* 675 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(664); -var define = __webpack_require__(675); -var utils = __webpack_require__(676); +var brackets = __webpack_require__(665); +var define = __webpack_require__(676); +var utils = __webpack_require__(677); /** * Characters to use in text regex (we want to "not" match @@ -80942,7 +81093,7 @@ module.exports = parsers; /***/ }), -/* 675 */ +/* 676 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80955,7 +81106,7 @@ module.exports = parsers; -var isDescriptor = __webpack_require__(557); +var isDescriptor = __webpack_require__(558); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -80980,14 +81131,14 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 676 */ +/* 677 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regex = __webpack_require__(533); -var Cache = __webpack_require__(655); +var regex = __webpack_require__(534); +var Cache = __webpack_require__(656); /** * Utils @@ -81056,7 +81207,7 @@ utils.createRegex = function(str) { /***/ }), -/* 677 */ +/* 678 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81066,16 +81217,16 @@ utils.createRegex = function(str) { * Module dependencies */ -var Snapdragon = __webpack_require__(565); -var define = __webpack_require__(675); -var extend = __webpack_require__(534); +var Snapdragon = __webpack_require__(566); +var define = __webpack_require__(676); +var extend = __webpack_require__(535); /** * Local dependencies */ -var compilers = __webpack_require__(663); -var parsers = __webpack_require__(674); +var compilers = __webpack_require__(664); +var parsers = __webpack_require__(675); /** * Customize Snapdragon parser and renderer @@ -81141,16 +81292,16 @@ module.exports = Extglob; /***/ }), -/* 678 */ +/* 679 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extglob = __webpack_require__(662); -var nanomatch = __webpack_require__(646); -var regexNot = __webpack_require__(533); -var toRegex = __webpack_require__(634); +var extglob = __webpack_require__(663); +var nanomatch = __webpack_require__(647); +var regexNot = __webpack_require__(534); +var toRegex = __webpack_require__(635); var not; /** @@ -81231,14 +81382,14 @@ function textRegex(pattern) { /***/ }), -/* 679 */ +/* 680 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(655))(); +module.exports = new (__webpack_require__(656))(); /***/ }), -/* 680 */ +/* 681 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81251,13 +81402,13 @@ var path = __webpack_require__(4); * Module dependencies */ -var Snapdragon = __webpack_require__(565); -utils.define = __webpack_require__(641); -utils.diff = __webpack_require__(659); -utils.extend = __webpack_require__(642); -utils.pick = __webpack_require__(660); -utils.typeOf = __webpack_require__(681); -utils.unique = __webpack_require__(536); +var Snapdragon = __webpack_require__(566); +utils.define = __webpack_require__(642); +utils.diff = __webpack_require__(660); +utils.extend = __webpack_require__(643); +utils.pick = __webpack_require__(661); +utils.typeOf = __webpack_require__(682); +utils.unique = __webpack_require__(537); /** * Returns true if the platform is windows, or `path.sep` is `\\`. @@ -81554,7 +81705,7 @@ utils.unixify = function(options) { /***/ }), -/* 681 */ +/* 682 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -81689,7 +81840,7 @@ function isBuffer(val) { /***/ }), -/* 682 */ +/* 683 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81708,9 +81859,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(683); -var reader_1 = __webpack_require__(696); -var fs_stream_1 = __webpack_require__(700); +var readdir = __webpack_require__(684); +var reader_1 = __webpack_require__(697); +var fs_stream_1 = __webpack_require__(701); var ReaderAsync = /** @class */ (function (_super) { __extends(ReaderAsync, _super); function ReaderAsync() { @@ -81771,15 +81922,15 @@ exports.default = ReaderAsync; /***/ }), -/* 683 */ +/* 684 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readdirSync = __webpack_require__(684); -const readdirAsync = __webpack_require__(692); -const readdirStream = __webpack_require__(695); +const readdirSync = __webpack_require__(685); +const readdirAsync = __webpack_require__(693); +const readdirStream = __webpack_require__(696); module.exports = exports = readdirAsyncPath; exports.readdir = exports.readdirAsync = exports.async = readdirAsyncPath; @@ -81863,7 +82014,7 @@ function readdirStreamStat (dir, options) { /***/ }), -/* 684 */ +/* 685 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81871,11 +82022,11 @@ function readdirStreamStat (dir, options) { module.exports = readdirSync; -const DirectoryReader = __webpack_require__(685); +const DirectoryReader = __webpack_require__(686); let syncFacade = { - fs: __webpack_require__(690), - forEach: __webpack_require__(691), + fs: __webpack_require__(691), + forEach: __webpack_require__(692), sync: true }; @@ -81904,7 +82055,7 @@ function readdirSync (dir, options, internalOptions) { /***/ }), -/* 685 */ +/* 686 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81913,9 +82064,9 @@ function readdirSync (dir, options, internalOptions) { const Readable = __webpack_require__(136).Readable; const EventEmitter = __webpack_require__(154).EventEmitter; const path = __webpack_require__(4); -const normalizeOptions = __webpack_require__(686); -const stat = __webpack_require__(688); -const call = __webpack_require__(689); +const normalizeOptions = __webpack_require__(687); +const stat = __webpack_require__(689); +const call = __webpack_require__(690); /** * Asynchronously reads the contents of a directory and streams the results @@ -82291,14 +82442,14 @@ module.exports = DirectoryReader; /***/ }), -/* 686 */ +/* 687 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const globToRegExp = __webpack_require__(687); +const globToRegExp = __webpack_require__(688); module.exports = normalizeOptions; @@ -82475,7 +82626,7 @@ function normalizeOptions (options, internalOptions) { /***/ }), -/* 687 */ +/* 688 */ /***/ (function(module, exports) { module.exports = function (glob, opts) { @@ -82612,13 +82763,13 @@ module.exports = function (glob, opts) { /***/ }), -/* 688 */ +/* 689 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const call = __webpack_require__(689); +const call = __webpack_require__(690); module.exports = stat; @@ -82693,7 +82844,7 @@ function symlinkStat (fs, path, lstats, callback) { /***/ }), -/* 689 */ +/* 690 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82754,14 +82905,14 @@ function callOnce (fn) { /***/ }), -/* 690 */ +/* 691 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(132); -const call = __webpack_require__(689); +const call = __webpack_require__(690); /** * A facade around {@link fs.readdirSync} that allows it to be called @@ -82825,7 +82976,7 @@ exports.lstat = function (path, callback) { /***/ }), -/* 691 */ +/* 692 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82854,7 +83005,7 @@ function syncForEach (array, iterator, done) { /***/ }), -/* 692 */ +/* 693 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82862,12 +83013,12 @@ function syncForEach (array, iterator, done) { module.exports = readdirAsync; -const maybe = __webpack_require__(693); -const DirectoryReader = __webpack_require__(685); +const maybe = __webpack_require__(694); +const DirectoryReader = __webpack_require__(686); let asyncFacade = { fs: __webpack_require__(132), - forEach: __webpack_require__(694), + forEach: __webpack_require__(695), async: true }; @@ -82909,7 +83060,7 @@ function readdirAsync (dir, options, callback, internalOptions) { /***/ }), -/* 693 */ +/* 694 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82936,7 +83087,7 @@ module.exports = function maybe (cb, promise) { /***/ }), -/* 694 */ +/* 695 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82972,7 +83123,7 @@ function asyncForEach (array, iterator, done) { /***/ }), -/* 695 */ +/* 696 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82980,11 +83131,11 @@ function asyncForEach (array, iterator, done) { module.exports = readdirStream; -const DirectoryReader = __webpack_require__(685); +const DirectoryReader = __webpack_require__(686); let streamFacade = { fs: __webpack_require__(132), - forEach: __webpack_require__(694), + forEach: __webpack_require__(695), async: true }; @@ -83004,16 +83155,16 @@ function readdirStream (dir, options, internalOptions) { /***/ }), -/* 696 */ +/* 697 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(4); -var deep_1 = __webpack_require__(697); -var entry_1 = __webpack_require__(699); -var pathUtil = __webpack_require__(698); +var deep_1 = __webpack_require__(698); +var entry_1 = __webpack_require__(700); +var pathUtil = __webpack_require__(699); var Reader = /** @class */ (function () { function Reader(options) { this.options = options; @@ -83079,14 +83230,14 @@ exports.default = Reader; /***/ }), -/* 697 */ +/* 698 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(698); -var patternUtils = __webpack_require__(515); +var pathUtils = __webpack_require__(699); +var patternUtils = __webpack_require__(516); var DeepFilter = /** @class */ (function () { function DeepFilter(options, micromatchOptions) { this.options = options; @@ -83169,7 +83320,7 @@ exports.default = DeepFilter; /***/ }), -/* 698 */ +/* 699 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83200,14 +83351,14 @@ exports.makeAbsolute = makeAbsolute; /***/ }), -/* 699 */ +/* 700 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(698); -var patternUtils = __webpack_require__(515); +var pathUtils = __webpack_require__(699); +var patternUtils = __webpack_require__(516); var EntryFilter = /** @class */ (function () { function EntryFilter(options, micromatchOptions) { this.options = options; @@ -83292,7 +83443,7 @@ exports.default = EntryFilter; /***/ }), -/* 700 */ +/* 701 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83312,8 +83463,8 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(136); -var fsStat = __webpack_require__(701); -var fs_1 = __webpack_require__(705); +var fsStat = __webpack_require__(702); +var fs_1 = __webpack_require__(706); var FileSystemStream = /** @class */ (function (_super) { __extends(FileSystemStream, _super); function FileSystemStream() { @@ -83363,14 +83514,14 @@ exports.default = FileSystemStream; /***/ }), -/* 701 */ +/* 702 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const optionsManager = __webpack_require__(702); -const statProvider = __webpack_require__(704); +const optionsManager = __webpack_require__(703); +const statProvider = __webpack_require__(705); /** * Asynchronous API. */ @@ -83401,13 +83552,13 @@ exports.statSync = statSync; /***/ }), -/* 702 */ +/* 703 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsAdapter = __webpack_require__(703); +const fsAdapter = __webpack_require__(704); function prepare(opts) { const options = Object.assign({ fs: fsAdapter.getFileSystemAdapter(opts ? opts.fs : undefined), @@ -83420,7 +83571,7 @@ exports.prepare = prepare; /***/ }), -/* 703 */ +/* 704 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83443,7 +83594,7 @@ exports.getFileSystemAdapter = getFileSystemAdapter; /***/ }), -/* 704 */ +/* 705 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83495,7 +83646,7 @@ exports.isFollowedSymlink = isFollowedSymlink; /***/ }), -/* 705 */ +/* 706 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83526,7 +83677,7 @@ exports.default = FileSystem; /***/ }), -/* 706 */ +/* 707 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83546,9 +83697,9 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(136); -var readdir = __webpack_require__(683); -var reader_1 = __webpack_require__(696); -var fs_stream_1 = __webpack_require__(700); +var readdir = __webpack_require__(684); +var reader_1 = __webpack_require__(697); +var fs_stream_1 = __webpack_require__(701); var TransformStream = /** @class */ (function (_super) { __extends(TransformStream, _super); function TransformStream(reader) { @@ -83616,7 +83767,7 @@ exports.default = ReaderStream; /***/ }), -/* 707 */ +/* 708 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83635,9 +83786,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(683); -var reader_1 = __webpack_require__(696); -var fs_sync_1 = __webpack_require__(708); +var readdir = __webpack_require__(684); +var reader_1 = __webpack_require__(697); +var fs_sync_1 = __webpack_require__(709); var ReaderSync = /** @class */ (function (_super) { __extends(ReaderSync, _super); function ReaderSync() { @@ -83697,7 +83848,7 @@ exports.default = ReaderSync; /***/ }), -/* 708 */ +/* 709 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83716,8 +83867,8 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var fsStat = __webpack_require__(701); -var fs_1 = __webpack_require__(705); +var fsStat = __webpack_require__(702); +var fs_1 = __webpack_require__(706); var FileSystemSync = /** @class */ (function (_super) { __extends(FileSystemSync, _super); function FileSystemSync() { @@ -83763,7 +83914,7 @@ exports.default = FileSystemSync; /***/ }), -/* 709 */ +/* 710 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83779,13 +83930,13 @@ exports.flatten = flatten; /***/ }), -/* 710 */ +/* 711 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var merge2 = __webpack_require__(283); +var merge2 = __webpack_require__(284); /** * Merge multiple streams and propagate their errors into one stream in parallel. */ @@ -83800,13 +83951,13 @@ exports.merge = merge; /***/ }), -/* 711 */ +/* 712 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathType = __webpack_require__(712); +const pathType = __webpack_require__(713); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -83872,13 +84023,13 @@ module.exports.sync = (input, opts) => { /***/ }), -/* 712 */ +/* 713 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(132); -const pify = __webpack_require__(713); +const pify = __webpack_require__(714); function type(fn, fn2, fp) { if (typeof fp !== 'string') { @@ -83921,7 +84072,7 @@ exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 713 */ +/* 714 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84012,17 +84163,17 @@ module.exports = (obj, opts) => { /***/ }), -/* 714 */ +/* 715 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(132); const path = __webpack_require__(4); -const fastGlob = __webpack_require__(511); -const gitIgnore = __webpack_require__(715); -const pify = __webpack_require__(716); -const slash = __webpack_require__(717); +const fastGlob = __webpack_require__(512); +const gitIgnore = __webpack_require__(716); +const pify = __webpack_require__(717); +const slash = __webpack_require__(718); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -84120,7 +84271,7 @@ module.exports.sync = options => { /***/ }), -/* 715 */ +/* 716 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -84589,7 +84740,7 @@ module.exports = options => new IgnoreBase(options) /***/ }), -/* 716 */ +/* 717 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84664,7 +84815,7 @@ module.exports = (input, options) => { /***/ }), -/* 717 */ +/* 718 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84682,7 +84833,7 @@ module.exports = input => { /***/ }), -/* 718 */ +/* 719 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -84692,7 +84843,7 @@ module.exports = input => { * Released under the MIT License. */ -var isExtglob = __webpack_require__(299); +var isExtglob = __webpack_require__(300); var chars = { '{': '}', '(': ')', '[': ']'}; var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; @@ -84736,17 +84887,17 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 719 */ +/* 720 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); const {constants: fsConstants} = __webpack_require__(132); -const pEvent = __webpack_require__(720); -const CpFileError = __webpack_require__(723); -const fs = __webpack_require__(727); -const ProgressEmitter = __webpack_require__(730); +const pEvent = __webpack_require__(721); +const CpFileError = __webpack_require__(724); +const fs = __webpack_require__(728); +const ProgressEmitter = __webpack_require__(731); const cpFileAsync = async (source, destination, options, progressEmitter) => { let readError; @@ -84860,12 +85011,12 @@ module.exports.sync = (source, destination, options) => { /***/ }), -/* 720 */ +/* 721 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pTimeout = __webpack_require__(721); +const pTimeout = __webpack_require__(722); const symbolAsyncIterator = Symbol.asyncIterator || '@@asyncIterator'; @@ -85156,12 +85307,12 @@ module.exports.iterator = (emitter, event, options) => { /***/ }), -/* 721 */ +/* 722 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pFinally = __webpack_require__(722); +const pFinally = __webpack_require__(723); class TimeoutError extends Error { constructor(message) { @@ -85207,7 +85358,7 @@ module.exports.TimeoutError = TimeoutError; /***/ }), -/* 722 */ +/* 723 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85229,12 +85380,12 @@ module.exports = (promise, onFinally) => { /***/ }), -/* 723 */ +/* 724 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(724); +const NestedError = __webpack_require__(725); class CpFileError extends NestedError { constructor(message, nested) { @@ -85248,10 +85399,10 @@ module.exports = CpFileError; /***/ }), -/* 724 */ +/* 725 */ /***/ (function(module, exports, __webpack_require__) { -var inherits = __webpack_require__(725); +var inherits = __webpack_require__(726); var NestedError = function (message, nested) { this.nested = nested; @@ -85302,7 +85453,7 @@ module.exports = NestedError; /***/ }), -/* 725 */ +/* 726 */ /***/ (function(module, exports, __webpack_require__) { try { @@ -85310,12 +85461,12 @@ try { if (typeof util.inherits !== 'function') throw ''; module.exports = util.inherits; } catch (e) { - module.exports = __webpack_require__(726); + module.exports = __webpack_require__(727); } /***/ }), -/* 726 */ +/* 727 */ /***/ (function(module, exports) { if (typeof Object.create === 'function') { @@ -85344,16 +85495,16 @@ if (typeof Object.create === 'function') { /***/ }), -/* 727 */ +/* 728 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const {promisify} = __webpack_require__(111); const fs = __webpack_require__(131); -const makeDir = __webpack_require__(728); -const pEvent = __webpack_require__(720); -const CpFileError = __webpack_require__(723); +const makeDir = __webpack_require__(729); +const pEvent = __webpack_require__(721); +const CpFileError = __webpack_require__(724); const stat = promisify(fs.stat); const lstat = promisify(fs.lstat); @@ -85450,7 +85601,7 @@ exports.copyFileSync = (source, destination, flags) => { /***/ }), -/* 728 */ +/* 729 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85458,7 +85609,7 @@ exports.copyFileSync = (source, destination, flags) => { const fs = __webpack_require__(132); const path = __webpack_require__(4); const {promisify} = __webpack_require__(111); -const semver = __webpack_require__(729); +const semver = __webpack_require__(730); const useNativeRecursiveOption = semver.satisfies(process.version, '>=10.12.0'); @@ -85613,7 +85764,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 729 */ +/* 730 */ /***/ (function(module, exports) { exports = module.exports = SemVer @@ -87215,7 +87366,7 @@ function coerce (version, options) { /***/ }), -/* 730 */ +/* 731 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87256,7 +87407,7 @@ module.exports = ProgressEmitter; /***/ }), -/* 731 */ +/* 732 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87302,12 +87453,12 @@ exports.default = module.exports; /***/ }), -/* 732 */ +/* 733 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(733); +const NestedError = __webpack_require__(734); class CpyError extends NestedError { constructor(message, nested) { @@ -87321,7 +87472,7 @@ module.exports = CpyError; /***/ }), -/* 733 */ +/* 734 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(111).inherits; @@ -87377,7 +87528,7 @@ module.exports = NestedError; /***/ }), -/* 734 */ +/* 735 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; diff --git a/src/core/public/core_system.ts b/src/core/public/core_system.ts index aa52212344f4d..d6172b77d3ca5 100644 --- a/src/core/public/core_system.ts +++ b/src/core/public/core_system.ts @@ -300,6 +300,10 @@ export class CoreSystem { plugins: mapToObject(plugins.contracts), targetDomElement: rendering.legacyTargetDomElement, }); + + return { + application, + }; } catch (error) { if (this.fatalErrorsSetup) { this.fatalErrorsSetup.add(error); diff --git a/src/core/public/kbn_bootstrap.ts b/src/core/public/kbn_bootstrap.ts index caeb95a540de3..0f86061816701 100644 --- a/src/core/public/kbn_bootstrap.ts +++ b/src/core/public/kbn_bootstrap.ts @@ -38,11 +38,17 @@ export function __kbnBootstrap__() { * `apmConfig` would be populated with relavant APM RUM agent * configuration if server is started with `ELASTIC_APM_ACTIVE=true` */ - if (process.env.IS_KIBANA_DISTRIBUTABLE !== 'true' && injectedMetadata.vars.apmConfig != null) { + const apmConfig = injectedMetadata.vars.apmConfig; + const APM_ENABLED = process.env.IS_KIBANA_DISTRIBUTABLE !== 'true' && apmConfig != null; + + if (APM_ENABLED) { // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-var-requires - const { init } = require('@elastic/apm-rum'); - init(injectedMetadata.vars.apmConfig); + const { init, apm } = require('@elastic/apm-rum'); + if (apmConfig.globalLabels) { + apm.addLabels(apmConfig.globalLabels); + } + init(apmConfig); } i18n @@ -60,6 +66,22 @@ export function __kbnBootstrap__() { setup.fatalErrors.add(i18nError); } - await coreSystem.start(); + const start = await coreSystem.start(); + + if (APM_ENABLED && start) { + /** + * Register listeners for navigation changes and capture them as + * route-change transactions after Kibana app is bootstrapped + */ + start.application.currentAppId$.subscribe((appId) => { + const apmInstance = (window as any).elasticApm; + if (appId && apmInstance && typeof apmInstance.startTransaction === 'function') { + apmInstance.startTransaction(`/app/${appId}`, 'route-change', { + managed: true, + canReuse: true, + }); + } + }); + } }); } diff --git a/src/core/public/plugins/plugin_reader.test.ts b/src/core/public/plugins/plugin_reader.test.ts index d4324f81de8e6..b3bc84f30daac 100644 --- a/src/core/public/plugins/plugin_reader.test.ts +++ b/src/core/public/plugins/plugin_reader.test.ts @@ -17,25 +17,37 @@ * under the License. */ -import { CoreWindow, read, UnknownPluginInitializer } from './plugin_reader'; +import { CoreWindow, read } from './plugin_reader'; + +const coreWindow: CoreWindow & { + __kbnBundles__: { stub(key: string, value: any): void }; +} = window as any; -const coreWindow: CoreWindow = window as any; beforeEach(() => { - coreWindow.__kbnBundles__ = {}; + const stubs = new Map(); + coreWindow.__kbnBundles__ = { + get(key) { + return stubs.get(key); + }, + has(key) { + return stubs.has(key); + }, + stub(key, value) { + stubs.set(key, value); + }, + }; }); it('handles undefined plugin exports', () => { - coreWindow.__kbnBundles__['plugin/foo'] = undefined; - expect(() => { read('foo'); }).toThrowError(`Definition of plugin "foo" not found and may have failed to load.`); }); it('handles plugin exports with a "plugin" export that is not a function', () => { - coreWindow.__kbnBundles__['plugin/foo'] = { + coreWindow.__kbnBundles__.stub('plugin/foo/public', { plugin: 1234, - } as any; + }); expect(() => { read('foo'); @@ -43,11 +55,8 @@ it('handles plugin exports with a "plugin" export that is not a function', () => }); it('returns the plugin initializer when the "plugin" named export is a function', () => { - const plugin: UnknownPluginInitializer = () => { - return undefined as any; - }; - - coreWindow.__kbnBundles__['plugin/foo'] = { plugin }; + const plugin = () => {}; + coreWindow.__kbnBundles__.stub('plugin/foo/public', { plugin }); expect(read('foo')).toBe(plugin); }); diff --git a/src/core/public/plugins/plugin_reader.ts b/src/core/public/plugins/plugin_reader.ts index 1907dfa6a3e99..d80bda7483775 100644 --- a/src/core/public/plugins/plugin_reader.ts +++ b/src/core/public/plugins/plugin_reader.ts @@ -31,7 +31,8 @@ export type UnknownPluginInitializer = PluginInitializer; // (undocumented) - start(): Promise; + start(): Promise<{ + application: InternalApplicationStart; + } | undefined>; // (undocumented) stop(): void; } @@ -1587,4 +1589,8 @@ export interface UserProvidedValues { } +// Warnings were encountered during analysis: +// +// src/core/public/core_system.ts:216:21 - (ae-forgotten-export) The symbol "InternalApplicationStart" needs to be exported by the entry point index.d.ts + ``` diff --git a/src/core/server/plugins/discovery/plugin_manifest_parser.ts b/src/core/server/plugins/discovery/plugin_manifest_parser.ts index 27c3ca5a71e16..f2c3a29eca0ac 100644 --- a/src/core/server/plugins/discovery/plugin_manifest_parser.ts +++ b/src/core/server/plugins/discovery/plugin_manifest_parser.ts @@ -57,6 +57,7 @@ const KNOWN_MANIFEST_FIELDS = (() => { optionalPlugins: true, ui: true, server: true, + extraPublicDirs: true, }; return new Set(Object.keys(manifestFields)); @@ -70,7 +71,11 @@ const KNOWN_MANIFEST_FIELDS = (() => { * @param packageInfo Kibana package info. * @internal */ -export async function parseManifest(pluginPath: string, packageInfo: PackageInfo, log: Logger) { +export async function parseManifest( + pluginPath: string, + packageInfo: PackageInfo, + log: Logger +): Promise { const manifestPath = resolve(pluginPath, MANIFEST_FILE_NAME); let manifestContent; @@ -130,6 +135,19 @@ export async function parseManifest(pluginPath: string, packageInfo: PackageInfo ); } + if ( + manifest.extraPublicDirs && + (!Array.isArray(manifest.extraPublicDirs) || + !manifest.extraPublicDirs.every((dir) => typeof dir === 'string')) + ) { + throw PluginDiscoveryError.invalidManifest( + manifestPath, + new Error( + `The "extraPublicDirs" in plugin manifest for "${manifest.id}" should be an array of strings.` + ) + ); + } + const expectedKibanaVersion = typeof manifest.kibanaVersion === 'string' && manifest.kibanaVersion ? manifest.kibanaVersion @@ -175,6 +193,7 @@ export async function parseManifest(pluginPath: string, packageInfo: PackageInfo optionalPlugins: Array.isArray(manifest.optionalPlugins) ? manifest.optionalPlugins : [], ui: includesUiPlugin, server: includesServerPlugin, + extraPublicDirs: manifest.extraPublicDirs, }; } diff --git a/src/core/server/plugins/types.ts b/src/core/server/plugins/types.ts index 4fa4e1780e596..2ca5c9f6ed3c5 100644 --- a/src/core/server/plugins/types.ts +++ b/src/core/server/plugins/types.ts @@ -153,6 +153,14 @@ export interface PluginManifest { * Specifies whether plugin includes some server-side specific functionality. */ readonly server: boolean; + + /** + * Specifies directory names that can be imported by other ui-plugins built + * using the same instance of the @kbn/optimizer. A temporary measure we plan + * to replace with better mechanisms for sharing static code between plugins + * @deprecated + */ + readonly extraPublicDirs?: string[]; } /** diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 78cc02d39e6c4..ecfa09fbd37f3 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -1520,6 +1520,8 @@ export interface PluginInitializerContext { // @public export interface PluginManifest { readonly configPath: ConfigPath; + // @deprecated + readonly extraPublicDirs?: string[]; readonly id: PluginName; readonly kibanaVersion: string; readonly optionalPlugins: readonly PluginName[]; @@ -2549,8 +2551,8 @@ export const validBodyOutput: readonly ["data", "stream"]; // src/core/server/legacy/types.ts:165:3 - (ae-forgotten-export) The symbol "LegacyNavLinkSpec" needs to be exported by the entry point index.d.ts // src/core/server/legacy/types.ts:166:3 - (ae-forgotten-export) The symbol "LegacyAppSpec" needs to be exported by the entry point index.d.ts // src/core/server/legacy/types.ts:167:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:230:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:230:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:232:3 - (ae-forgotten-export) The symbol "PathConfigType" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:238:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:238:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:240:3 - (ae-forgotten-export) The symbol "PathConfigType" needs to be exported by the entry point index.d.ts ``` diff --git a/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_graph.hjson b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_graph.hjson index 81267cd514f1f..db19c937ca990 100644 --- a/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_graph.hjson +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_graph.hjson @@ -1,7 +1,7 @@ { // Adapted from Vega's https://vega.github.io/vega/examples/stacked-area-chart/ - $schema: https://vega.github.io/schema/vega/v3.0.json + $schema: https://vega.github.io/schema/vega/v5.json data: [ { name: table diff --git a/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_map_test.hjson b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_map_test.hjson index cef73c0155794..633b8658ad849 100644 --- a/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_map_test.hjson +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_map_test.hjson @@ -1,7 +1,7 @@ # This graph creates a single rectangle for the whole graph on top of a map # Note that the actual map tiles are not loaded { - $schema: https://vega.github.io/schema/vega/v3.0.json + $schema: https://vega.github.io/schema/vega/v5.json config: { kibana: {type: "map", mapStyle: false} } diff --git a/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_tooltip_test.hjson b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_tooltip_test.hjson index 78e92a00384c6..77465c8b3f007 100644 --- a/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_tooltip_test.hjson +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_tooltip_test.hjson @@ -24,7 +24,7 @@ ] } ] - $schema: https://vega.github.io/schema/vega/v3.json + $schema: https://vega.github.io/schema/vega/v5.json marks: [ { from: {data: "table"} diff --git a/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_visualization.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_visualization.js index 485390dc50a79..17610702a0bc7 100644 --- a/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_visualization.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_visualization.js @@ -333,7 +333,7 @@ describe('VegaVisualizations', () => { vegaVis = new VegaVisualization(domNode, vis); const vegaParser = new VegaParser( `{ - "$schema": "https://vega.github.io/schema/vega/v3.json", + "$schema": "https://vega.github.io/schema/vega/v5.json", "marks": [ { "type": "text", @@ -366,11 +366,6 @@ describe('VegaVisualizations', () => { await vegaVis.render(vegaParser, vis.params, { data: true }); const vegaView = vegaVis._vegaView._view; expect(vegaView.height()).to.be(250.00000001); - - vegaView.height(250); - await vegaView.runAsync(); - // as soon as this test fails, the workaround with the subpixel value can be removed. - expect(vegaView.height()).to.be(0); } finally { vegaVis.destroy(); } diff --git a/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vegalite_graph.hjson b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vegalite_graph.hjson index fd7eb1ae7d878..2132b0f77e6bc 100644 --- a/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vegalite_graph.hjson +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vegalite_graph.hjson @@ -1,5 +1,5 @@ { - $schema: https://vega.github.io/schema/vega-lite/v2.json + $schema: https://vega.github.io/schema/vega-lite/v4.json data: { format: {property: "aggregations.time_buckets.buckets"} values: { diff --git a/src/legacy/ui/apm/index.js b/src/legacy/ui/apm/index.js index f1074091e2624..c43b7b01d1159 100644 --- a/src/legacy/ui/apm/index.js +++ b/src/legacy/ui/apm/index.js @@ -30,21 +30,15 @@ export function apmInit(config) { return apmEnabled ? `init(${config})` : ''; } -export function getApmConfig(app) { +export function getApmConfig(requestPath) { if (!apmEnabled) { return null; } - /** - * we use the injected app metadata from the server to extract the - * app id to be used for page-load transaction - */ - const appId = app.getId(); - const config = { ...getConfig('kibana-frontend'), ...{ active: true, - pageLoadTransactionName: appId, + pageLoadTransactionName: requestPath, }, }; /** diff --git a/src/legacy/ui/ui_exports/ui_export_defaults.js b/src/legacy/ui/ui_exports/ui_export_defaults.js index d60bf7df899e3..f7ee9aa056762 100644 --- a/src/legacy/ui/ui_exports/ui_export_defaults.js +++ b/src/legacy/ui/ui_exports/ui_export_defaults.js @@ -24,7 +24,7 @@ export const UI_EXPORT_DEFAULTS = { webpackNoParseRules: [ /node_modules[\/\\](angular|elasticsearch-browser)[\/\\]/, /node_modules[\/\\](mocha|moment)[\/\\]/, - /node_modules[\/\\]vega-lib[\/\\]build[\/\\]vega\.js$/, + /node_modules[\/\\]vega[\/\\]build[\/\\]vega\.js$/, ], webpackAliases: { diff --git a/src/legacy/ui/ui_render/bootstrap/app_bootstrap.js b/src/legacy/ui/ui_render/bootstrap/app_bootstrap.js index 0e6936dd64a15..19f75317883d7 100644 --- a/src/legacy/ui/ui_render/bootstrap/app_bootstrap.js +++ b/src/legacy/ui/ui_render/bootstrap/app_bootstrap.js @@ -22,9 +22,11 @@ import { createHash } from 'crypto'; import { readFile } from 'fs'; import { resolve } from 'path'; +import { kbnBundlesLoaderSource } from './kbn_bundles_loader_source'; + export class AppBootstrap { constructor({ templateData }) { - this.templateData = templateData; + this.templateData = { ...templateData, kbnBundlesLoaderSource }; this._rawTemplate = undefined; } diff --git a/src/legacy/ui/ui_render/bootstrap/kbn_bundles_loader_source.js b/src/legacy/ui/ui_render/bootstrap/kbn_bundles_loader_source.js new file mode 100644 index 0000000000000..cb5488118cc77 --- /dev/null +++ b/src/legacy/ui/ui_render/bootstrap/kbn_bundles_loader_source.js @@ -0,0 +1,51 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + kbnBundlesLoaderSource: `(${kbnBundlesLoader.toString()})();`, +}; + +function kbnBundlesLoader() { + var modules = {}; + + function has(prop) { + return Object.prototype.hasOwnProperty.call(modules, prop); + } + + function define(key, bundleRequire, bundleModuleKey) { + if (has(key)) { + throw new Error('__kbnBundles__ already has a module defined for "' + key + '"'); + } + + modules[key] = { + bundleRequire, + bundleModuleKey, + }; + } + + function get(key) { + if (!has(key)) { + throw new Error('__kbnBundles__ does not have a module defined for "' + key + '"'); + } + + return modules[key].bundleRequire(modules[key].bundleModuleKey); + } + + return { has: has, define: define, get: get }; +} diff --git a/src/legacy/ui/ui_render/bootstrap/template.js.hbs b/src/legacy/ui/ui_render/bootstrap/template.js.hbs index e8f05b46f7061..ca2e944489a73 100644 --- a/src/legacy/ui/ui_render/bootstrap/template.js.hbs +++ b/src/legacy/ui/ui_render/bootstrap/template.js.hbs @@ -3,6 +3,7 @@ window.__kbnStrictCsp__ = kbnCsp.strictCsp; window.__kbnDarkMode__ = {{darkMode}}; window.__kbnThemeVersion__ = "{{themeVersion}}"; window.__kbnPublicPath__ = {{publicPathMap}}; +window.__kbnBundles__ = {{kbnBundlesLoaderSource}} if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) { var legacyBrowserError = document.getElementById('kbn_legacy_browser_error'); @@ -78,12 +79,7 @@ if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) { {{/each}} ], function () { {{#unless legacyBundlePath}} - if (!__kbnBundles__ || !__kbnBundles__['entry/core'] || typeof __kbnBundles__['entry/core'].__kbnBootstrap__ !== 'function') { - console.error('entry/core bundle did not load correctly'); - failure(); - } else { - __kbnBundles__['entry/core'].__kbnBootstrap__() - } + __kbnBundles__.get('entry/core/public').__kbnBootstrap__(); {{/unless}} load([ diff --git a/src/legacy/ui/ui_render/ui_render_mixin.js b/src/legacy/ui/ui_render/ui_render_mixin.js index b09d4861b343b..b4f8255297240 100644 --- a/src/legacy/ui/ui_render/ui_render_mixin.js +++ b/src/legacy/ui/ui_render/ui_render_mixin.js @@ -26,8 +26,6 @@ import { AppBootstrap } from './bootstrap'; import { getApmConfig } from '../apm'; import { DllCompiler } from '../../../optimize/dynamic_dll_plugin'; -const uniq = (...items) => Array.from(new Set(items)); - /** * @typedef {import('../../server/kbn_server').default} KbnServer * @typedef {import('../../server/kbn_server').ResponseToolkit} ResponseToolkit @@ -150,15 +148,7 @@ export function uiRenderMixin(kbnServer, server, config) { ]), ]; - const kpPluginIds = uniq( - // load these plugins first, they are "shared" and other bundles access their - // public/index exports without considering topographic sorting by plugin deps (for now) - 'kibanaUtils', - 'kibanaReact', - 'data', - 'esUiShared', - ...kbnServer.newPlatform.__internals.uiPlugins.public.keys() - ); + const kpPluginIds = Array.from(kbnServer.newPlatform.__internals.uiPlugins.public.keys()); const jsDependencyPaths = [ ...UiSharedDeps.jsDepFilenames.map( @@ -251,7 +241,7 @@ export function uiRenderMixin(kbnServer, server, config) { savedObjects.getClient(h.request) ); const vars = await legacy.getVars(app.getId(), h.request, { - apmConfig: getApmConfig(app), + apmConfig: getApmConfig(h.request.path), ...overrides, }); const content = await rendering.render(h.request, uiSettings, { diff --git a/src/plugins/data/kibana.json b/src/plugins/data/kibana.json index f5df747f17e1e..3e5d96a4bc47b 100644 --- a/src/plugins/data/kibana.json +++ b/src/plugins/data/kibana.json @@ -7,5 +7,6 @@ "expressions", "uiActions" ], - "optionalPlugins": ["usageCollection"] + "optionalPlugins": ["usageCollection"], + "extraPublicDirs": ["common", "common/utils/abort_utils"] } diff --git a/src/plugins/data/public/ui/filter_bar/filter_editor/generic_combo_box.tsx b/src/plugins/data/public/ui/filter_bar/filter_editor/generic_combo_box.tsx index 66adbfe9a6fc3..c66518c18a9ea 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_editor/generic_combo_box.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_editor/generic_combo_box.tsx @@ -59,6 +59,7 @@ export function GenericComboBox(props: GenericComboBoxProps) { options={euiOptions} selectedOptions={selectedEuiOptions} onChange={onComboBoxChange} + sortMatchesBy="startsWith" {...otherProps} /> ); diff --git a/src/plugins/embeddable/kibana.json b/src/plugins/embeddable/kibana.json index 535527b4d09db..06b0e88da334f 100644 --- a/src/plugins/embeddable/kibana.json +++ b/src/plugins/embeddable/kibana.json @@ -6,5 +6,8 @@ "requiredPlugins": [ "inspector", "uiActions" + ], + "extraPublicDirs": [ + "public/lib/test_samples" ] } diff --git a/src/plugins/es_ui_shared/kibana.json b/src/plugins/es_ui_shared/kibana.json index 5f772b3c83da9..980f43ea46a68 100644 --- a/src/plugins/es_ui_shared/kibana.json +++ b/src/plugins/es_ui_shared/kibana.json @@ -2,5 +2,13 @@ "id": "esUiShared", "version": "kibana", "ui": true, - "server": true + "server": true, + "extraPublicDirs": [ + "static/ace_x_json/hooks", + "static/validators/string", + "static/forms/hook_form_lib", + "static/forms/helpers", + "static/forms/components", + "static/forms/helpers/field_validators/types" + ] } diff --git a/src/plugins/expressions/kibana.json b/src/plugins/expressions/kibana.json index 5d2112103e94d..4774c69cc29ff 100644 --- a/src/plugins/expressions/kibana.json +++ b/src/plugins/expressions/kibana.json @@ -5,5 +5,6 @@ "ui": true, "requiredPlugins": [ "bfetch" - ] + ], + "extraPublicDirs": ["common", "common/fonts"] } diff --git a/src/plugins/home/server/services/sample_data/data_sets/flights/saved_objects.ts b/src/plugins/home/server/services/sample_data/data_sets/flights/saved_objects.ts index 1cce11aea37fb..c1f7fcb75b149 100644 --- a/src/plugins/home/server/services/sample_data/data_sets/flights/saved_objects.ts +++ b/src/plugins/home/server/services/sample_data/data_sets/flights/saved_objects.ts @@ -420,7 +420,7 @@ export const getSavedObjects = (): SavedObject[] => [ defaultMessage: '[Flights] Airport Connections (Hover Over Airport)', }), visState: - '{"aggs":[],"params":{"spec":"{\\n $schema: https://vega.github.io/schema/vega/v3.0.json\\n config: {\\n kibana: {type: \\"map\\", latitude: 25, longitude: -70, zoom: 3}\\n }\\n data: [\\n {\\n name: table\\n url: {\\n index: kibana_sample_data_flights\\n %context%: true\\n // Uncomment to enable time filtering\\n // %timefield%: timestamp\\n body: {\\n size: 0\\n aggs: {\\n origins: {\\n terms: {field: \\"OriginAirportID\\", size: 10000}\\n aggs: {\\n originLocation: {\\n top_hits: {\\n size: 1\\n _source: {\\n includes: [\\"OriginLocation\\", \\"Origin\\"]\\n }\\n }\\n }\\n distinations: {\\n terms: {field: \\"DestAirportID\\", size: 10000}\\n aggs: {\\n destLocation: {\\n top_hits: {\\n size: 1\\n _source: {\\n includes: [\\"DestLocation\\"]\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n format: {property: \\"aggregations.origins.buckets\\"}\\n transform: [\\n {\\n type: geopoint\\n projection: projection\\n fields: [\\n originLocation.hits.hits[0]._source.OriginLocation.lon\\n originLocation.hits.hits[0]._source.OriginLocation.lat\\n ]\\n }\\n ]\\n }\\n {\\n name: selectedDatum\\n on: [\\n {trigger: \\"!selected\\", remove: true}\\n {trigger: \\"selected\\", insert: \\"selected\\"}\\n ]\\n }\\n ]\\n signals: [\\n {\\n name: selected\\n value: null\\n on: [\\n {events: \\"@airport:mouseover\\", update: \\"datum\\"}\\n {events: \\"@airport:mouseout\\", update: \\"null\\"}\\n ]\\n }\\n ]\\n scales: [\\n {\\n name: airportSize\\n type: linear\\n domain: {data: \\"table\\", field: \\"doc_count\\"}\\n range: [\\n {signal: \\"zoom*zoom*0.2+1\\"}\\n {signal: \\"zoom*zoom*10+1\\"}\\n ]\\n }\\n ]\\n marks: [\\n {\\n type: group\\n from: {\\n facet: {\\n name: facetedDatum\\n data: selectedDatum\\n field: distinations.buckets\\n }\\n }\\n data: [\\n {\\n name: facetDatumElems\\n source: facetedDatum\\n transform: [\\n {\\n type: geopoint\\n projection: projection\\n fields: [\\n destLocation.hits.hits[0]._source.DestLocation.lon\\n destLocation.hits.hits[0]._source.DestLocation.lat\\n ]\\n }\\n {type: \\"formula\\", expr: \\"{x:parent.x, y:parent.y}\\", as: \\"source\\"}\\n {type: \\"formula\\", expr: \\"{x:datum.x, y:datum.y}\\", as: \\"target\\"}\\n {type: \\"linkpath\\", shape: \\"diagonal\\"}\\n ]\\n }\\n ]\\n scales: [\\n {\\n name: lineThickness\\n type: linear\\n domain: {data: \\"facetDatumElems\\", field: \\"doc_count\\"}\\n range: [1, 8]\\n }\\n {\\n name: lineOpacity\\n type: linear\\n domain: {data: \\"facetDatumElems\\", field: \\"doc_count\\"}\\n range: [0.2, 0.8]\\n }\\n ]\\n marks: [\\n {\\n from: {data: \\"facetDatumElems\\"}\\n type: path\\n interactive: false\\n encode: {\\n update: {\\n path: {field: \\"path\\"}\\n stroke: {value: \\"black\\"}\\n strokeWidth: {scale: \\"lineThickness\\", field: \\"doc_count\\"}\\n strokeOpacity: {scale: \\"lineOpacity\\", field: \\"doc_count\\"}\\n }\\n }\\n }\\n ]\\n }\\n {\\n name: airport\\n type: symbol\\n from: {data: \\"table\\"}\\n encode: {\\n update: {\\n size: {scale: \\"airportSize\\", field: \\"doc_count\\"}\\n xc: {signal: \\"datum.x\\"}\\n yc: {signal: \\"datum.y\\"}\\n tooltip: {\\n signal: \\"{title: datum.originLocation.hits.hits[0]._source.Origin + \' (\' + datum.key + \')\', connnections: length(datum.distinations.buckets), flights: datum.doc_count}\\"\\n }\\n }\\n }\\n }\\n ]\\n}"},"title":"[Flights] Airport Connections (Hover Over Airport)","type":"vega"}', + '{"aggs":[],"params":{"spec":"{\\n $schema: https://vega.github.io/schema/vega/v5.json\\n config: {\\n kibana: {type: \\"map\\", latitude: 25, longitude: -70, zoom: 3}\\n }\\n data: [\\n {\\n name: table\\n url: {\\n index: kibana_sample_data_flights\\n %context%: true\\n // Uncomment to enable time filtering\\n // %timefield%: timestamp\\n body: {\\n size: 0\\n aggs: {\\n origins: {\\n terms: {field: \\"OriginAirportID\\", size: 10000}\\n aggs: {\\n originLocation: {\\n top_hits: {\\n size: 1\\n _source: {\\n includes: [\\"OriginLocation\\", \\"Origin\\"]\\n }\\n }\\n }\\n distinations: {\\n terms: {field: \\"DestAirportID\\", size: 10000}\\n aggs: {\\n destLocation: {\\n top_hits: {\\n size: 1\\n _source: {\\n includes: [\\"DestLocation\\"]\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n format: {property: \\"aggregations.origins.buckets\\"}\\n transform: [\\n {\\n type: geopoint\\n projection: projection\\n fields: [\\n originLocation.hits.hits[0]._source.OriginLocation.lon\\n originLocation.hits.hits[0]._source.OriginLocation.lat\\n ]\\n }\\n ]\\n }\\n {\\n name: selectedDatum\\n on: [\\n {trigger: \\"!selected\\", remove: true}\\n {trigger: \\"selected\\", insert: \\"selected\\"}\\n ]\\n }\\n ]\\n signals: [\\n {\\n name: selected\\n value: null\\n on: [\\n {events: \\"@airport:mouseover\\", update: \\"datum\\"}\\n {events: \\"@airport:mouseout\\", update: \\"null\\"}\\n ]\\n }\\n ]\\n scales: [\\n {\\n name: airportSize\\n type: linear\\n domain: {data: \\"table\\", field: \\"doc_count\\"}\\n range: [\\n {signal: \\"zoom*zoom*0.2+1\\"}\\n {signal: \\"zoom*zoom*10+1\\"}\\n ]\\n }\\n ]\\n marks: [\\n {\\n type: group\\n from: {\\n facet: {\\n name: facetedDatum\\n data: selectedDatum\\n field: distinations.buckets\\n }\\n }\\n data: [\\n {\\n name: facetDatumElems\\n source: facetedDatum\\n transform: [\\n {\\n type: geopoint\\n projection: projection\\n fields: [\\n destLocation.hits.hits[0]._source.DestLocation.lon\\n destLocation.hits.hits[0]._source.DestLocation.lat\\n ]\\n }\\n {type: \\"formula\\", expr: \\"{x:parent.x, y:parent.y}\\", as: \\"source\\"}\\n {type: \\"formula\\", expr: \\"{x:datum.x, y:datum.y}\\", as: \\"target\\"}\\n {type: \\"linkpath\\", shape: \\"diagonal\\"}\\n ]\\n }\\n ]\\n scales: [\\n {\\n name: lineThickness\\n type: log\\n clamp: true\\n range: [1, 8]\\n }\\n {\\n name: lineOpacity\\n type: log\\n clamp: true\\n range: [0.2, 0.8]\\n }\\n ]\\n marks: [\\n {\\n from: {data: \\"facetDatumElems\\"}\\n type: path\\n interactive: false\\n encode: {\\n update: {\\n path: {field: \\"path\\"}\\n stroke: {value: \\"black\\"}\\n strokeWidth: {scale: \\"lineThickness\\", field: \\"doc_count\\"}\\n strokeOpacity: {scale: \\"lineOpacity\\", field: \\"doc_count\\"}\\n }\\n }\\n }\\n ]\\n }\\n {\\n name: airport\\n type: symbol\\n from: {data: \\"table\\"}\\n encode: {\\n update: {\\n size: {scale: \\"airportSize\\", field: \\"doc_count\\"}\\n xc: {signal: \\"datum.x\\"}\\n yc: {signal: \\"datum.y\\"}\\n tooltip: {\\n signal: \\"{title: datum.originLocation.hits.hits[0]._source.Origin + \' (\' + datum.key + \')\', connnections: length(datum.distinations.buckets), flights: datum.doc_count}\\"\\n }\\n }\\n }\\n }\\n ]\\n}"},"title":"[Flights] Airport Connections (Hover Over Airport)","type":"vega"}', uiStateJSON: '{}', description: '', version: 1, diff --git a/src/plugins/home/server/services/sample_data/data_sets/logs/saved_objects.ts b/src/plugins/home/server/services/sample_data/data_sets/logs/saved_objects.ts index 883108651bfc5..0620e93118b8d 100644 --- a/src/plugins/home/server/services/sample_data/data_sets/logs/saved_objects.ts +++ b/src/plugins/home/server/services/sample_data/data_sets/logs/saved_objects.ts @@ -145,7 +145,7 @@ export const getSavedObjects = (): SavedObject[] => [ defaultMessage: '[Logs] File Type Scatter Plot', }), visState: - '{"title":"[Logs] File Type Scatter Plot","type":"vega","params":{"spec":"{\\n $schema: \\"https://vega.github.io/schema/vega-lite/v2.json\\"\\n // Use points for drawing to actually create a scatterplot\\n mark: point\\n // Specify where to load data from\\n data: {\\n // By using an object to the url parameter we will\\n // construct an Elasticsearch query\\n url: {\\n // Context == true means filters of the dashboard will be taken into account\\n %context%: true\\n // Specify on which field the time picker should operate\\n %timefield%: timestamp\\n // Specify the index pattern to load data from\\n index: kibana_sample_data_logs\\n // This body will be send to Elasticsearch\'s _search endpoint\\n // You can use everything the ES Query DSL supports here\\n body: {\\n // Set the size to load 10000 documents\\n size: 10000,\\n // Just ask for the fields we actually need for visualization\\n _source: [\\"timestamp\\", \\"bytes\\", \\"extension\\"]\\n }\\n }\\n // Tell Vega, that the array of data will be inside hits.hits of the response\\n // since the result returned from Elasticsearch fill have a format like:\\n // {\\n // hits: {\\n // total: 42000,\\n // max_score: 2,\\n // hits: [\\n // < our individual documents >\\n // ]\\n // }\\n // }\\n format: { property: \\"hits.hits\\" }\\n }\\n // You can do transformation and calculation of the data before drawing it\\n transform: [\\n // Since timestamp is a string value, we need to convert it to a unix timestamp\\n // so that Vega can work on it properly.\\n {\\n // Convert _source.timestamp field to a date\\n calculate: \\"toDate(datum._source[\'timestamp\'])\\"\\n // Store the result in a field named \\"time\\" in the object\\n as: \\"time\\"\\n }\\n ]\\n // Specify what data will be drawn on which axis\\n encoding: {\\n x: {\\n // Draw the time field on the x-axis in temporal mode (i.e. as a time axis)\\n field: time\\n type: temporal\\n // Hide the axis label for the x-axis\\n axis: { title: false }\\n }\\n y: {\\n // Draw the bytes of each document on the y-axis\\n field: _source.bytes\\n // Mark the y-axis as quantitative\\n type: quantitative\\n // Specify the label for this axis\\n axis: { title: \\"Transferred bytes\\" }\\n }\\n color: {\\n // Make the color of each point depend on the _source.extension field\\n field: _source.extension\\n // Treat different values as completely unrelated values to each other.\\n // You could switch this to quantitative if you have a numeric field and\\n // want to create a color scale from one color to another depending on that\\n // field\'s value.\\n type: nominal\\n // Rename the legend title so it won\'t just state: \\"_source.extension\\"\\n legend: { title: \'File type\' }\\n }\\n shape: {\\n // Also make the shape of each point dependent on the extension.\\n field: _source.extension\\n type: nominal\\n }\\n }\\n}"},"aggs":[]}', + '{"title":"[Logs] File Type Scatter Plot","type":"vega","params":{"spec":"{\\n $schema: \\"https://vega.github.io/schema/vega-lite/v4.json\\"\\n // Use points for drawing to actually create a scatterplot\\n mark: point\\n // Specify where to load data from\\n data: {\\n // By using an object to the url parameter we will\\n // construct an Elasticsearch query\\n url: {\\n // Context == true means filters of the dashboard will be taken into account\\n %context%: true\\n // Specify on which field the time picker should operate\\n %timefield%: timestamp\\n // Specify the index pattern to load data from\\n index: kibana_sample_data_logs\\n // This body will be send to Elasticsearch\'s _search endpoint\\n // You can use everything the ES Query DSL supports here\\n body: {\\n // Set the size to load 10000 documents\\n size: 10000,\\n // Just ask for the fields we actually need for visualization\\n _source: [\\"timestamp\\", \\"bytes\\", \\"extension\\"]\\n }\\n }\\n // Tell Vega, that the array of data will be inside hits.hits of the response\\n // since the result returned from Elasticsearch fill have a format like:\\n // {\\n // hits: {\\n // total: 42000,\\n // max_score: 2,\\n // hits: [\\n // < our individual documents >\\n // ]\\n // }\\n // }\\n format: { property: \\"hits.hits\\" }\\n }\\n // You can do transformation and calculation of the data before drawing it\\n transform: [\\n // Since timestamp is a string value, we need to convert it to a unix timestamp\\n // so that Vega can work on it properly.\\n {\\n // Convert _source.timestamp field to a date\\n calculate: \\"toDate(datum._source[\'timestamp\'])\\"\\n // Store the result in a field named \\"time\\" in the object\\n as: \\"time\\"\\n }\\n ]\\n // Specify what data will be drawn on which axis\\n encoding: {\\n x: {\\n // Draw the time field on the x-axis in temporal mode (i.e. as a time axis)\\n field: time\\n type: temporal\\n // Hide the axis label for the x-axis\\n axis: { title: false }\\n }\\n y: {\\n // Draw the bytes of each document on the y-axis\\n field: _source.bytes\\n // Mark the y-axis as quantitative\\n type: quantitative\\n // Specify the label for this axis\\n axis: { title: \\"Transferred bytes\\" }\\n }\\n color: {\\n // Make the color of each point depend on the _source.extension field\\n field: _source.extension\\n // Treat different values as completely unrelated values to each other.\\n // You could switch this to quantitative if you have a numeric field and\\n // want to create a color scale from one color to another depending on that\\n // field\'s value.\\n type: nominal\\n // Rename the legend title so it won\'t just state: \\"_source.extension\\"\\n legend: { title: \'File type\' }\\n }\\n shape: {\\n // Also make the shape of each point dependent on the extension.\\n field: _source.extension\\n type: nominal\\n }\\n }\\n}"},"aggs":[]}', uiStateJSON: '{}', description: '', version: 1, @@ -166,7 +166,7 @@ export const getSavedObjects = (): SavedObject[] => [ defaultMessage: '[Logs] Source and Destination Sankey Chart', }), visState: - '{"title":"[Logs] Source and Destination Sankey Chart","type":"vega","params":{"spec":"{ \\n $schema: https://vega.github.io/schema/vega/v3.0.json\\n data: [\\n\\t{\\n \\t// query ES based on the currently selected time range and filter string\\n \\tname: rawData\\n \\turl: {\\n \\t%context%: true\\n \\t%timefield%: timestamp\\n \\tindex: kibana_sample_data_logs\\n \\tbody: {\\n \\tsize: 0\\n \\taggs: {\\n \\ttable: {\\n \\tcomposite: {\\n \\tsize: 10000\\n \\tsources: [\\n \\t{\\n \\tstk1: {\\n \\tterms: {field: \\"geo.src\\"}\\n \\t}\\n \\t}\\n \\t{\\n \\tstk2: {\\n \\tterms: {field: \\"geo.dest\\"}\\n \\t}\\n \\t}\\n \\t]\\n \\t}\\n \\t}\\n \\t}\\n \\t}\\n \\t}\\n \\t// From the result, take just the data we are interested in\\n \\tformat: {property: \\"aggregations.table.buckets\\"}\\n \\t// Convert key.stk1 -> stk1 for simpler access below\\n \\ttransform: [\\n \\t{type: \\"formula\\", expr: \\"datum.key.stk1\\", as: \\"stk1\\"}\\n \\t{type: \\"formula\\", expr: \\"datum.key.stk2\\", as: \\"stk2\\"}\\n \\t{type: \\"formula\\", expr: \\"datum.doc_count\\", as: \\"size\\"}\\n \\t]\\n\\t}\\n\\t{\\n \\tname: nodes\\n \\tsource: rawData\\n \\ttransform: [\\n \\t// when a country is selected, filter out unrelated data\\n \\t{\\n \\ttype: filter\\n \\texpr: !groupSelector || groupSelector.stk1 == datum.stk1 || groupSelector.stk2 == datum.stk2\\n \\t}\\n \\t// Set new key for later lookups - identifies each node\\n \\t{type: \\"formula\\", expr: \\"datum.stk1+datum.stk2\\", as: \\"key\\"}\\n \\t// instead of each table row, create two new rows,\\n \\t// one for the source (stack=stk1) and one for destination node (stack=stk2).\\n \\t// The country code stored in stk1 and stk2 fields is placed into grpId field.\\n \\t{\\n \\ttype: fold\\n \\tfields: [\\"stk1\\", \\"stk2\\"]\\n \\tas: [\\"stack\\", \\"grpId\\"]\\n \\t}\\n \\t// Create a sortkey, different for stk1 and stk2 stacks.\\n \\t{\\n \\ttype: formula\\n \\texpr: datum.stack == \'stk1\' ? datum.stk1+datum.stk2 : datum.stk2+datum.stk1\\n \\tas: sortField\\n \\t}\\n \\t// Calculate y0 and y1 positions for stacking nodes one on top of the other,\\n \\t// independently for each stack, and ensuring they are in the proper order,\\n \\t// alphabetical from the top (reversed on the y axis)\\n \\t{\\n \\ttype: stack\\n \\tgroupby: [\\"stack\\"]\\n \\tsort: {field: \\"sortField\\", order: \\"descending\\"}\\n \\tfield: size\\n \\t}\\n \\t// calculate vertical center point for each node, used to draw edges\\n \\t{type: \\"formula\\", expr: \\"(datum.y0+datum.y1)/2\\", as: \\"yc\\"}\\n \\t]\\n\\t}\\n\\t{\\n \\tname: groups\\n \\tsource: nodes\\n \\ttransform: [\\n \\t// combine all nodes into country groups, summing up the doc counts\\n \\t{\\n \\ttype: aggregate\\n \\tgroupby: [\\"stack\\", \\"grpId\\"]\\n \\tfields: [\\"size\\"]\\n \\tops: [\\"sum\\"]\\n \\tas: [\\"total\\"]\\n \\t}\\n \\t// re-calculate the stacking y0,y1 values\\n \\t{\\n \\ttype: stack\\n \\tgroupby: [\\"stack\\"]\\n \\tsort: {field: \\"grpId\\", order: \\"descending\\"}\\n \\tfield: total\\n \\t}\\n \\t// project y0 and y1 values to screen coordinates\\n \\t// doing it once here instead of doing it several times in marks\\n \\t{type: \\"formula\\", expr: \\"scale(\'y\', datum.y0)\\", as: \\"scaledY0\\"}\\n \\t{type: \\"formula\\", expr: \\"scale(\'y\', datum.y1)\\", as: \\"scaledY1\\"}\\n \\t// boolean flag if the label should be on the right of the stack\\n \\t{type: \\"formula\\", expr: \\"datum.stack == \'stk1\'\\", as: \\"rightLabel\\"}\\n \\t// Calculate traffic percentage for this country using \\"y\\" scale\\n \\t// domain upper bound, which represents the total traffic\\n \\t{\\n \\ttype: formula\\n \\texpr: datum.total/domain(\'y\')[1]\\n \\tas: percentage\\n \\t}\\n \\t]\\n\\t}\\n\\t{\\n \\t// This is a temp lookup table with all the \'stk2\' stack nodes\\n \\tname: destinationNodes\\n \\tsource: nodes\\n \\ttransform: [\\n \\t{type: \\"filter\\", expr: \\"datum.stack == \'stk2\'\\"}\\n \\t]\\n\\t}\\n\\t{\\n \\tname: edges\\n \\tsource: nodes\\n \\ttransform: [\\n \\t// we only want nodes from the left stack\\n \\t{type: \\"filter\\", expr: \\"datum.stack == \'stk1\'\\"}\\n \\t// find corresponding node from the right stack, keep it as \\"target\\"\\n \\t{\\n \\ttype: lookup\\n \\tfrom: destinationNodes\\n \\tkey: key\\n \\tfields: [\\"key\\"]\\n \\tas: [\\"target\\"]\\n \\t}\\n \\t// calculate SVG link path between stk1 and stk2 stacks for the node pair\\n \\t{\\n \\ttype: linkpath\\n \\torient: horizontal\\n \\tshape: diagonal\\n \\tsourceY: {expr: \\"scale(\'y\', datum.yc)\\"}\\n \\tsourceX: {expr: \\"scale(\'x\', \'stk1\') + bandwidth(\'x\')\\"}\\n \\ttargetY: {expr: \\"scale(\'y\', datum.target.yc)\\"}\\n \\ttargetX: {expr: \\"scale(\'x\', \'stk2\')\\"}\\n \\t}\\n \\t// A little trick to calculate the thickness of the line.\\n \\t// The value needs to be the same as the hight of the node, but scaling\\n \\t// size to screen\'s height gives inversed value because screen\'s Y\\n \\t// coordinate goes from the top to the bottom, whereas the graph\'s Y=0\\n \\t// is at the bottom. So subtracting scaled doc count from screen height\\n \\t// (which is the \\"lower\\" bound of the \\"y\\" scale) gives us the right value\\n \\t{\\n \\ttype: formula\\n \\texpr: range(\'y\')[0]-scale(\'y\', datum.size)\\n \\tas: strokeWidth\\n \\t}\\n \\t// Tooltip needs individual link\'s percentage of all traffic\\n \\t{\\n \\ttype: formula\\n \\texpr: datum.size/domain(\'y\')[1]\\n \\tas: percentage\\n \\t}\\n \\t]\\n\\t}\\n ]\\n scales: [\\n\\t{\\n \\t// calculates horizontal stack positioning\\n \\tname: x\\n \\ttype: band\\n \\trange: width\\n \\tdomain: [\\"stk1\\", \\"stk2\\"]\\n \\tpaddingOuter: 0.05\\n \\tpaddingInner: 0.95\\n\\t}\\n\\t{\\n \\t// this scale goes up as high as the highest y1 value of all nodes\\n \\tname: y\\n \\ttype: linear\\n \\trange: height\\n \\tdomain: {data: \\"nodes\\", field: \\"y1\\"}\\n\\t}\\n\\t{\\n \\t// use rawData to ensure the colors stay the same when clicking.\\n \\tname: color\\n \\ttype: ordinal\\n \\trange: category\\n \\tdomain: {data: \\"rawData\\", field: \\"stk1\\"}\\n\\t}\\n\\t{\\n \\t// this scale is used to map internal ids (stk1, stk2) to stack names\\n \\tname: stackNames\\n \\ttype: ordinal\\n \\trange: [\\"Source\\", \\"Destination\\"]\\n \\tdomain: [\\"stk1\\", \\"stk2\\"]\\n\\t}\\n ]\\n axes: [\\n\\t{\\n \\t// x axis should use custom label formatting to print proper stack names\\n \\torient: bottom\\n \\tscale: x\\n \\tencode: {\\n \\tlabels: {\\n \\tupdate: {\\n \\ttext: {scale: \\"stackNames\\", field: \\"value\\"}\\n \\t}\\n \\t}\\n \\t}\\n\\t}\\n\\t{orient: \\"left\\", scale: \\"y\\"}\\n ]\\n marks: [\\n\\t{\\n \\t// draw the connecting line between stacks\\n \\ttype: path\\n \\tname: edgeMark\\n \\tfrom: {data: \\"edges\\"}\\n \\t// this prevents some autosizing issues with large strokeWidth for paths\\n \\tclip: true\\n \\tencode: {\\n \\tupdate: {\\n \\t// By default use color of the left node, except when showing traffic\\n \\t// from just one country, in which case use destination color.\\n \\tstroke: [\\n \\t{\\n \\ttest: groupSelector && groupSelector.stack==\'stk1\'\\n \\tscale: color\\n \\tfield: stk2\\n \\t}\\n \\t{scale: \\"color\\", field: \\"stk1\\"}\\n \\t]\\n \\tstrokeWidth: {field: \\"strokeWidth\\"}\\n \\tpath: {field: \\"path\\"}\\n \\t// when showing all traffic, and hovering over a country,\\n \\t// highlight the traffic from that country.\\n \\tstrokeOpacity: {\\n \\tsignal: !groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 0.9 : 0.3\\n \\t}\\n \\t// Ensure that the hover-selected edges show on top\\n \\tzindex: {\\n \\tsignal: !groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 1 : 0\\n \\t}\\n \\t// format tooltip string\\n \\ttooltip: {\\n \\tsignal: datum.stk1 + \' → \' + datum.stk2 + \'\\t\' + format(datum.size, \',.0f\') + \' (\' + format(datum.percentage, \'.1%\') + \')\'\\n \\t}\\n \\t}\\n \\t// Simple mouseover highlighting of a single line\\n \\thover: {\\n \\tstrokeOpacity: {value: 1}\\n \\t}\\n \\t}\\n\\t}\\n\\t{\\n \\t// draw stack groups (countries)\\n \\ttype: rect\\n \\tname: groupMark\\n \\tfrom: {data: \\"groups\\"}\\n \\tencode: {\\n \\tenter: {\\n \\tfill: {scale: \\"color\\", field: \\"grpId\\"}\\n \\twidth: {scale: \\"x\\", band: 1}\\n \\t}\\n \\tupdate: {\\n \\tx: {scale: \\"x\\", field: \\"stack\\"}\\n \\ty: {field: \\"scaledY0\\"}\\n \\ty2: {field: \\"scaledY1\\"}\\n \\tfillOpacity: {value: 0.6}\\n \\ttooltip: {\\n \\tsignal: datum.grpId + \' \' + format(datum.total, \',.0f\') + \' (\' + format(datum.percentage, \'.1%\') + \')\'\\n \\t}\\n \\t}\\n \\thover: {\\n \\tfillOpacity: {value: 1}\\n \\t}\\n \\t}\\n\\t}\\n\\t{\\n \\t// draw country code labels on the inner side of the stack\\n \\ttype: text\\n \\tfrom: {data: \\"groups\\"}\\n \\t// don\'t process events for the labels - otherwise line mouseover is unclean\\n \\tinteractive: false\\n \\tencode: {\\n \\tupdate: {\\n \\t// depending on which stack it is, position x with some padding\\n \\tx: {\\n \\tsignal: scale(\'x\', datum.stack) + (datum.rightLabel ? bandwidth(\'x\') + 8 : -8)\\n \\t}\\n \\t// middle of the group\\n \\tyc: {signal: \\"(datum.scaledY0 + datum.scaledY1)/2\\"}\\n \\talign: {signal: \\"datum.rightLabel ? \'left\' : \'right\'\\"}\\n \\tbaseline: {value: \\"middle\\"}\\n \\tfontWeight: {value: \\"bold\\"}\\n \\t// only show text label if the group\'s height is large enough\\n \\ttext: {signal: \\"abs(datum.scaledY0-datum.scaledY1) > 13 ? datum.grpId : \'\'\\"}\\n \\t}\\n \\t}\\n\\t}\\n\\t{\\n \\t// Create a \\"show all\\" button. Shown only when a country is selected.\\n \\ttype: group\\n \\tdata: [\\n \\t// We need to make the button show only when groupSelector signal is true.\\n \\t// Each mark is drawn as many times as there are elements in the backing data.\\n \\t// Which means that if values list is empty, it will not be drawn.\\n \\t// Here I create a data source with one empty object, and filter that list\\n \\t// based on the signal value. This can only be done in a group.\\n \\t{\\n \\tname: dataForShowAll\\n \\tvalues: [{}]\\n \\ttransform: [{type: \\"filter\\", expr: \\"groupSelector\\"}]\\n \\t}\\n \\t]\\n \\t// Set button size and positioning\\n \\tencode: {\\n \\tenter: {\\n \\txc: {signal: \\"width/2\\"}\\n \\ty: {value: 30}\\n \\twidth: {value: 80}\\n \\theight: {value: 30}\\n \\t}\\n \\t}\\n \\tmarks: [\\n \\t{\\n \\t// This group is shown as a button with rounded corners.\\n \\ttype: group\\n \\t// mark name allows signal capturing\\n \\tname: groupReset\\n \\t// Only shows button if dataForShowAll has values.\\n \\tfrom: {data: \\"dataForShowAll\\"}\\n \\tencode: {\\n \\tenter: {\\n \\tcornerRadius: {value: 6}\\n \\tfill: {value: \\"#F5F7FA\\"}\\n \\tstroke: {value: \\"#c1c1c1\\"}\\n \\tstrokeWidth: {value: 2}\\n \\t// use parent group\'s size\\n \\theight: {\\n \\tfield: {group: \\"height\\"}\\n \\t}\\n \\twidth: {\\n \\tfield: {group: \\"width\\"}\\n \\t}\\n \\t}\\n \\tupdate: {\\n \\t// groups are transparent by default\\n \\topacity: {value: 1}\\n \\t}\\n \\thover: {\\n \\topacity: {value: 0.7}\\n \\t}\\n \\t}\\n \\tmarks: [\\n \\t{\\n \\ttype: text\\n \\t// if true, it will prevent clicking on the button when over text.\\n \\tinteractive: false\\n \\tencode: {\\n \\tenter: {\\n \\t// center text in the paren group\\n \\txc: {\\n \\tfield: {group: \\"width\\"}\\n \\tmult: 0.5\\n \\t}\\n \\tyc: {\\n \\tfield: {group: \\"height\\"}\\n \\tmult: 0.5\\n \\toffset: 2\\n \\t}\\n \\talign: {value: \\"center\\"}\\n \\tbaseline: {value: \\"middle\\"}\\n \\tfontWeight: {value: \\"bold\\"}\\n \\ttext: {value: \\"Show All\\"}\\n \\t}\\n \\t}\\n \\t}\\n \\t]\\n \\t}\\n \\t]\\n\\t}\\n ]\\n signals: [\\n\\t{\\n \\t// used to highlight traffic to/from the same country\\n \\tname: groupHover\\n \\tvalue: {}\\n \\ton: [\\n \\t{\\n \\tevents: @groupMark:mouseover\\n \\tupdate: \\"{stk1:datum.stack==\'stk1\' && datum.grpId, stk2:datum.stack==\'stk2\' && datum.grpId}\\"\\n \\t}\\n \\t{events: \\"mouseout\\", update: \\"{}\\"}\\n \\t]\\n\\t}\\n\\t// used to filter only the data related to the selected country\\n\\t{\\n \\tname: groupSelector\\n \\tvalue: false\\n \\ton: [\\n \\t{\\n \\t// Clicking groupMark sets this signal to the filter values\\n \\tevents: @groupMark:click!\\n \\tupdate: \\"{stack:datum.stack, stk1:datum.stack==\'stk1\' && datum.grpId, stk2:datum.stack==\'stk2\' && datum.grpId}\\"\\n \\t}\\n \\t{\\n \\t// Clicking \\"show all\\" button, or double-clicking anywhere resets it\\n \\tevents: [\\n \\t{type: \\"click\\", markname: \\"groupReset\\"}\\n \\t{type: \\"dblclick\\"}\\n \\t]\\n \\tupdate: \\"false\\"\\n \\t}\\n \\t]\\n\\t}\\n ]\\n}\\n"},"aggs":[]}', + '{"title":"[Logs] Source and Destination Sankey Chart","type":"vega","params":{"spec":"{ \\n $schema: https://vega.github.io/schema/vega/v5.json\\n data: [\\n\\t{\\n \\t// query ES based on the currently selected time range and filter string\\n \\tname: rawData\\n \\turl: {\\n \\t%context%: true\\n \\t%timefield%: timestamp\\n \\tindex: kibana_sample_data_logs\\n \\tbody: {\\n \\tsize: 0\\n \\taggs: {\\n \\ttable: {\\n \\tcomposite: {\\n \\tsize: 10000\\n \\tsources: [\\n \\t{\\n \\tstk1: {\\n \\tterms: {field: \\"geo.src\\"}\\n \\t}\\n \\t}\\n \\t{\\n \\tstk2: {\\n \\tterms: {field: \\"geo.dest\\"}\\n \\t}\\n \\t}\\n \\t]\\n \\t}\\n \\t}\\n \\t}\\n \\t}\\n \\t}\\n \\t// From the result, take just the data we are interested in\\n \\tformat: {property: \\"aggregations.table.buckets\\"}\\n \\t// Convert key.stk1 -> stk1 for simpler access below\\n \\ttransform: [\\n \\t{type: \\"formula\\", expr: \\"datum.key.stk1\\", as: \\"stk1\\"}\\n \\t{type: \\"formula\\", expr: \\"datum.key.stk2\\", as: \\"stk2\\"}\\n \\t{type: \\"formula\\", expr: \\"datum.doc_count\\", as: \\"size\\"}\\n \\t]\\n\\t}\\n\\t{\\n \\tname: nodes\\n \\tsource: rawData\\n \\ttransform: [\\n \\t// when a country is selected, filter out unrelated data\\n \\t{\\n \\ttype: filter\\n \\texpr: !groupSelector || groupSelector.stk1 == datum.stk1 || groupSelector.stk2 == datum.stk2\\n \\t}\\n \\t// Set new key for later lookups - identifies each node\\n \\t{type: \\"formula\\", expr: \\"datum.stk1+datum.stk2\\", as: \\"key\\"}\\n \\t// instead of each table row, create two new rows,\\n \\t// one for the source (stack=stk1) and one for destination node (stack=stk2).\\n \\t// The country code stored in stk1 and stk2 fields is placed into grpId field.\\n \\t{\\n \\ttype: fold\\n \\tfields: [\\"stk1\\", \\"stk2\\"]\\n \\tas: [\\"stack\\", \\"grpId\\"]\\n \\t}\\n \\t// Create a sortkey, different for stk1 and stk2 stacks.\\n \\t{\\n \\ttype: formula\\n \\texpr: datum.stack == \'stk1\' ? datum.stk1+datum.stk2 : datum.stk2+datum.stk1\\n \\tas: sortField\\n \\t}\\n \\t// Calculate y0 and y1 positions for stacking nodes one on top of the other,\\n \\t// independently for each stack, and ensuring they are in the proper order,\\n \\t// alphabetical from the top (reversed on the y axis)\\n \\t{\\n \\ttype: stack\\n \\tgroupby: [\\"stack\\"]\\n \\tsort: {field: \\"sortField\\", order: \\"descending\\"}\\n \\tfield: size\\n \\t}\\n \\t// calculate vertical center point for each node, used to draw edges\\n \\t{type: \\"formula\\", expr: \\"(datum.y0+datum.y1)/2\\", as: \\"yc\\"}\\n \\t]\\n\\t}\\n\\t{\\n \\tname: groups\\n \\tsource: nodes\\n \\ttransform: [\\n \\t// combine all nodes into country groups, summing up the doc counts\\n \\t{\\n \\ttype: aggregate\\n \\tgroupby: [\\"stack\\", \\"grpId\\"]\\n \\tfields: [\\"size\\"]\\n \\tops: [\\"sum\\"]\\n \\tas: [\\"total\\"]\\n \\t}\\n \\t// re-calculate the stacking y0,y1 values\\n \\t{\\n \\ttype: stack\\n \\tgroupby: [\\"stack\\"]\\n \\tsort: {field: \\"grpId\\", order: \\"descending\\"}\\n \\tfield: total\\n \\t}\\n \\t// project y0 and y1 values to screen coordinates\\n \\t// doing it once here instead of doing it several times in marks\\n \\t{type: \\"formula\\", expr: \\"scale(\'y\', datum.y0)\\", as: \\"scaledY0\\"}\\n \\t{type: \\"formula\\", expr: \\"scale(\'y\', datum.y1)\\", as: \\"scaledY1\\"}\\n \\t// boolean flag if the label should be on the right of the stack\\n \\t{type: \\"formula\\", expr: \\"datum.stack == \'stk1\'\\", as: \\"rightLabel\\"}\\n \\t// Calculate traffic percentage for this country using \\"y\\" scale\\n \\t// domain upper bound, which represents the total traffic\\n \\t{\\n \\ttype: formula\\n \\texpr: datum.total/domain(\'y\')[1]\\n \\tas: percentage\\n \\t}\\n \\t]\\n\\t}\\n\\t{\\n \\t// This is a temp lookup table with all the \'stk2\' stack nodes\\n \\tname: destinationNodes\\n \\tsource: nodes\\n \\ttransform: [\\n \\t{type: \\"filter\\", expr: \\"datum.stack == \'stk2\'\\"}\\n \\t]\\n\\t}\\n\\t{\\n \\tname: edges\\n \\tsource: nodes\\n \\ttransform: [\\n \\t// we only want nodes from the left stack\\n \\t{type: \\"filter\\", expr: \\"datum.stack == \'stk1\'\\"}\\n \\t// find corresponding node from the right stack, keep it as \\"target\\"\\n \\t{\\n \\ttype: lookup\\n \\tfrom: destinationNodes\\n \\tkey: key\\n \\tfields: [\\"key\\"]\\n \\tas: [\\"target\\"]\\n \\t}\\n \\t// calculate SVG link path between stk1 and stk2 stacks for the node pair\\n \\t{\\n \\ttype: linkpath\\n \\torient: horizontal\\n \\tshape: diagonal\\n \\tsourceY: {expr: \\"scale(\'y\', datum.yc)\\"}\\n \\tsourceX: {expr: \\"scale(\'x\', \'stk1\') + bandwidth(\'x\')\\"}\\n \\ttargetY: {expr: \\"scale(\'y\', datum.target.yc)\\"}\\n \\ttargetX: {expr: \\"scale(\'x\', \'stk2\')\\"}\\n \\t}\\n \\t// A little trick to calculate the thickness of the line.\\n \\t// The value needs to be the same as the hight of the node, but scaling\\n \\t// size to screen\'s height gives inversed value because screen\'s Y\\n \\t// coordinate goes from the top to the bottom, whereas the graph\'s Y=0\\n \\t// is at the bottom. So subtracting scaled doc count from screen height\\n \\t// (which is the \\"lower\\" bound of the \\"y\\" scale) gives us the right value\\n \\t{\\n \\ttype: formula\\n \\texpr: range(\'y\')[0]-scale(\'y\', datum.size)\\n \\tas: strokeWidth\\n \\t}\\n \\t// Tooltip needs individual link\'s percentage of all traffic\\n \\t{\\n \\ttype: formula\\n \\texpr: datum.size/domain(\'y\')[1]\\n \\tas: percentage\\n \\t}\\n \\t]\\n\\t}\\n ]\\n scales: [\\n\\t{\\n \\t// calculates horizontal stack positioning\\n \\tname: x\\n \\ttype: band\\n \\trange: width\\n \\tdomain: [\\"stk1\\", \\"stk2\\"]\\n \\tpaddingOuter: 0.05\\n \\tpaddingInner: 0.95\\n\\t}\\n\\t{\\n \\t// this scale goes up as high as the highest y1 value of all nodes\\n \\tname: y\\n \\ttype: linear\\n \\trange: height\\n \\tdomain: {data: \\"nodes\\", field: \\"y1\\"}\\n\\t}\\n\\t{\\n \\t// use rawData to ensure the colors stay the same when clicking.\\n \\tname: color\\n \\ttype: ordinal\\n \\trange: category\\n \\tdomain: {data: \\"rawData\\", field: \\"stk1\\"}\\n\\t}\\n\\t{\\n \\t// this scale is used to map internal ids (stk1, stk2) to stack names\\n \\tname: stackNames\\n \\ttype: ordinal\\n \\trange: [\\"Source\\", \\"Destination\\"]\\n \\tdomain: [\\"stk1\\", \\"stk2\\"]\\n\\t}\\n ]\\n axes: [\\n\\t{\\n \\t// x axis should use custom label formatting to print proper stack names\\n \\torient: bottom\\n \\tscale: x\\n \\tencode: {\\n \\tlabels: {\\n \\tupdate: {\\n \\ttext: {scale: \\"stackNames\\", field: \\"value\\"}\\n \\t}\\n \\t}\\n \\t}\\n\\t}\\n\\t{orient: \\"left\\", scale: \\"y\\"}\\n ]\\n marks: [\\n\\t{\\n \\t// draw the connecting line between stacks\\n \\ttype: path\\n \\tname: edgeMark\\n \\tfrom: {data: \\"edges\\"}\\n \\t// this prevents some autosizing issues with large strokeWidth for paths\\n \\tclip: true\\n \\tencode: {\\n \\tupdate: {\\n \\t// By default use color of the left node, except when showing traffic\\n \\t// from just one country, in which case use destination color.\\n \\tstroke: [\\n \\t{\\n \\ttest: groupSelector && groupSelector.stack==\'stk1\'\\n \\tscale: color\\n \\tfield: stk2\\n \\t}\\n \\t{scale: \\"color\\", field: \\"stk1\\"}\\n \\t]\\n \\tstrokeWidth: {field: \\"strokeWidth\\"}\\n \\tpath: {field: \\"path\\"}\\n \\t// when showing all traffic, and hovering over a country,\\n \\t// highlight the traffic from that country.\\n \\tstrokeOpacity: {\\n \\tsignal: !groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 0.9 : 0.3\\n \\t}\\n \\t// Ensure that the hover-selected edges show on top\\n \\tzindex: {\\n \\tsignal: !groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 1 : 0\\n \\t}\\n \\t// format tooltip string\\n \\ttooltip: {\\n \\tsignal: datum.stk1 + \' → \' + datum.stk2 + \'\\t\' + format(datum.size, \',.0f\') + \' (\' + format(datum.percentage, \'.1%\') + \')\'\\n \\t}\\n \\t}\\n \\t// Simple mouseover highlighting of a single line\\n \\thover: {\\n \\tstrokeOpacity: {value: 1}\\n \\t}\\n \\t}\\n\\t}\\n\\t{\\n \\t// draw stack groups (countries)\\n \\ttype: rect\\n \\tname: groupMark\\n \\tfrom: {data: \\"groups\\"}\\n \\tencode: {\\n \\tenter: {\\n \\tfill: {scale: \\"color\\", field: \\"grpId\\"}\\n \\twidth: {scale: \\"x\\", band: 1}\\n \\t}\\n \\tupdate: {\\n \\tx: {scale: \\"x\\", field: \\"stack\\"}\\n \\ty: {field: \\"scaledY0\\"}\\n \\ty2: {field: \\"scaledY1\\"}\\n \\tfillOpacity: {value: 0.6}\\n \\ttooltip: {\\n \\tsignal: datum.grpId + \' \' + format(datum.total, \',.0f\') + \' (\' + format(datum.percentage, \'.1%\') + \')\'\\n \\t}\\n \\t}\\n \\thover: {\\n \\tfillOpacity: {value: 1}\\n \\t}\\n \\t}\\n\\t}\\n\\t{\\n \\t// draw country code labels on the inner side of the stack\\n \\ttype: text\\n \\tfrom: {data: \\"groups\\"}\\n \\t// don\'t process events for the labels - otherwise line mouseover is unclean\\n \\tinteractive: false\\n \\tencode: {\\n \\tupdate: {\\n \\t// depending on which stack it is, position x with some padding\\n \\tx: {\\n \\tsignal: scale(\'x\', datum.stack) + (datum.rightLabel ? bandwidth(\'x\') + 8 : -8)\\n \\t}\\n \\t// middle of the group\\n \\tyc: {signal: \\"(datum.scaledY0 + datum.scaledY1)/2\\"}\\n \\talign: {signal: \\"datum.rightLabel ? \'left\' : \'right\'\\"}\\n \\tbaseline: {value: \\"middle\\"}\\n \\tfontWeight: {value: \\"bold\\"}\\n \\t// only show text label if the group\'s height is large enough\\n \\ttext: {signal: \\"abs(datum.scaledY0-datum.scaledY1) > 13 ? datum.grpId : \'\'\\"}\\n \\t}\\n \\t}\\n\\t}\\n\\t{\\n \\t// Create a \\"show all\\" button. Shown only when a country is selected.\\n \\ttype: group\\n \\tdata: [\\n \\t// We need to make the button show only when groupSelector signal is true.\\n \\t// Each mark is drawn as many times as there are elements in the backing data.\\n \\t// Which means that if values list is empty, it will not be drawn.\\n \\t// Here I create a data source with one empty object, and filter that list\\n \\t// based on the signal value. This can only be done in a group.\\n \\t{\\n \\tname: dataForShowAll\\n \\tvalues: [{}]\\n \\ttransform: [{type: \\"filter\\", expr: \\"groupSelector\\"}]\\n \\t}\\n \\t]\\n \\t// Set button size and positioning\\n \\tencode: {\\n \\tenter: {\\n \\txc: {signal: \\"width/2\\"}\\n \\ty: {value: 30}\\n \\twidth: {value: 80}\\n \\theight: {value: 30}\\n \\t}\\n \\t}\\n \\tmarks: [\\n \\t{\\n \\t// This group is shown as a button with rounded corners.\\n \\ttype: group\\n \\t// mark name allows signal capturing\\n \\tname: groupReset\\n \\t// Only shows button if dataForShowAll has values.\\n \\tfrom: {data: \\"dataForShowAll\\"}\\n \\tencode: {\\n \\tenter: {\\n \\tcornerRadius: {value: 6}\\n \\tfill: {value: \\"#F5F7FA\\"}\\n \\tstroke: {value: \\"#c1c1c1\\"}\\n \\tstrokeWidth: {value: 2}\\n \\t// use parent group\'s size\\n \\theight: {\\n \\tfield: {group: \\"height\\"}\\n \\t}\\n \\twidth: {\\n \\tfield: {group: \\"width\\"}\\n \\t}\\n \\t}\\n \\tupdate: {\\n \\t// groups are transparent by default\\n \\topacity: {value: 1}\\n \\t}\\n \\thover: {\\n \\topacity: {value: 0.7}\\n \\t}\\n \\t}\\n \\tmarks: [\\n \\t{\\n \\ttype: text\\n \\t// if true, it will prevent clicking on the button when over text.\\n \\tinteractive: false\\n \\tencode: {\\n \\tenter: {\\n \\t// center text in the paren group\\n \\txc: {\\n \\tfield: {group: \\"width\\"}\\n \\tmult: 0.5\\n \\t}\\n \\tyc: {\\n \\tfield: {group: \\"height\\"}\\n \\tmult: 0.5\\n \\toffset: 2\\n \\t}\\n \\talign: {value: \\"center\\"}\\n \\tbaseline: {value: \\"middle\\"}\\n \\tfontWeight: {value: \\"bold\\"}\\n \\ttext: {value: \\"Show All\\"}\\n \\t}\\n \\t}\\n \\t}\\n \\t]\\n \\t}\\n \\t]\\n\\t}\\n ]\\n signals: [\\n\\t{\\n \\t// used to highlight traffic to/from the same country\\n \\tname: groupHover\\n \\tvalue: {}\\n \\ton: [\\n \\t{\\n \\tevents: @groupMark:mouseover\\n \\tupdate: \\"{stk1:datum.stack==\'stk1\' && datum.grpId, stk2:datum.stack==\'stk2\' && datum.grpId}\\"\\n \\t}\\n \\t{events: \\"mouseout\\", update: \\"{}\\"}\\n \\t]\\n\\t}\\n\\t// used to filter only the data related to the selected country\\n\\t{\\n \\tname: groupSelector\\n \\tvalue: false\\n \\ton: [\\n \\t{\\n \\t// Clicking groupMark sets this signal to the filter values\\n \\tevents: @groupMark:click!\\n \\tupdate: \\"{stack:datum.stack, stk1:datum.stack==\'stk1\' && datum.grpId, stk2:datum.stack==\'stk2\' && datum.grpId}\\"\\n \\t}\\n \\t{\\n \\t// Clicking \\"show all\\" button, or double-clicking anywhere resets it\\n \\tevents: [\\n \\t{type: \\"click\\", markname: \\"groupReset\\"}\\n \\t{type: \\"dblclick\\"}\\n \\t]\\n \\tupdate: \\"false\\"\\n \\t}\\n \\t]\\n\\t}\\n ]\\n}\\n"},"aggs":[]}', uiStateJSON: '{}', description: '', version: 1, diff --git a/src/plugins/inspector/kibana.json b/src/plugins/inspector/kibana.json index 39d3ff65eed53..99a38d2928df6 100644 --- a/src/plugins/inspector/kibana.json +++ b/src/plugins/inspector/kibana.json @@ -2,5 +2,6 @@ "id": "inspector", "version": "kibana", "server": false, - "ui": true + "ui": true, + "extraPublicDirs": ["common", "common/adapters/request"] } diff --git a/src/plugins/kibana_legacy/kibana.json b/src/plugins/kibana_legacy/kibana.json index e96b4859a36d0..606acd8b88b05 100644 --- a/src/plugins/kibana_legacy/kibana.json +++ b/src/plugins/kibana_legacy/kibana.json @@ -2,5 +2,6 @@ "id": "kibanaLegacy", "version": "kibana", "server": true, - "ui": true + "ui": true, + "extraPublicDirs": ["common/kbn_base_url"] } diff --git a/src/plugins/kibana_utils/kibana.json b/src/plugins/kibana_utils/kibana.json index 6fa39d82d1021..7e2127c27548e 100644 --- a/src/plugins/kibana_utils/kibana.json +++ b/src/plugins/kibana_utils/kibana.json @@ -1,5 +1,10 @@ { "id": "kibanaUtils", "version": "kibana", - "ui": true + "ui": true, + "extraPublicDirs": [ + "common", + "demos/state_containers/todomvc", + "common/state_containers" + ] } diff --git a/src/plugins/maps_legacy/public/leaflet.js b/src/plugins/maps_legacy/public/leaflet.js index e36da2c52b8c5..bee75021c76ad 100644 --- a/src/plugins/maps_legacy/public/leaflet.js +++ b/src/plugins/maps_legacy/public/leaflet.js @@ -17,8 +17,6 @@ * under the License. */ -export let L; - if (!window.hasOwnProperty('L')) { require('leaflet/dist/leaflet.css'); window.L = require('leaflet/dist/leaflet.js'); @@ -31,6 +29,6 @@ if (!window.hasOwnProperty('L')) { require('leaflet-draw/dist/leaflet.draw.js'); require('leaflet-responsive-popup/leaflet.responsive.popup.css'); require('leaflet-responsive-popup/leaflet.responsive.popup.js'); -} else { - L = window.L; } + +export const L = window.L; diff --git a/src/plugins/saved_objects_management/kibana.json b/src/plugins/saved_objects_management/kibana.json index 22135ce4558ae..6184d890c415c 100644 --- a/src/plugins/saved_objects_management/kibana.json +++ b/src/plugins/saved_objects_management/kibana.json @@ -4,5 +4,6 @@ "server": true, "ui": true, "requiredPlugins": ["home", "management", "data"], - "optionalPlugins": ["dashboard", "visualizations", "discover"] + "optionalPlugins": ["dashboard", "visualizations", "discover"], + "extraPublicDirs": ["public/lib"] } diff --git a/src/plugins/telemetry/kibana.json b/src/plugins/telemetry/kibana.json index f623f4f2a565d..a497597762520 100644 --- a/src/plugins/telemetry/kibana.json +++ b/src/plugins/telemetry/kibana.json @@ -6,5 +6,8 @@ "requiredPlugins": [ "telemetryCollectionManager", "usageCollection" + ], + "extraPublicDirs": [ + "common/constants" ] } diff --git a/src/plugins/ui_actions/kibana.json b/src/plugins/ui_actions/kibana.json index 44ecbbfa68408..907cbabbdf9c9 100644 --- a/src/plugins/ui_actions/kibana.json +++ b/src/plugins/ui_actions/kibana.json @@ -2,5 +2,8 @@ "id": "uiActions", "version": "kibana", "server": false, - "ui": true + "ui": true, + "extraPublicDirs": [ + "public/tests/test_samples" + ] } diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.js b/src/plugins/vis_type_vega/public/data_model/vega_parser.js index cbfe2a6ede4f2..377567e47ced8 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.js +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.js @@ -18,8 +18,7 @@ */ import _ from 'lodash'; -import * as vega from 'vega-lib'; -import * as vegaLite from 'vega-lite'; +import { vega, vegaLite } from '../lib/vega'; import schemaParser from 'vega-schema-url-parser'; import versionCompare from 'compare-versions'; import { EsQueryParser } from './es_query_parser'; @@ -32,15 +31,13 @@ import { i18n } from '@kbn/i18n'; // Set default single color to match other Kibana visualizations const defaultColor = VISUALIZATION_COLORS[0]; - -const DEFAULT_SCHEMA = 'https://vega.github.io/schema/vega/v3.0.json'; - const locToDirMap = { left: 'row-reverse', right: 'row', top: 'column-reverse', bottom: 'column', }; +const DEFAULT_SCHEMA = 'https://vega.github.io/schema/vega/v5.json'; // If there is no "%type%" parameter, use this parser const DEFAULT_PARSER = 'elasticsearch'; diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js b/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js index a40ef31260b6f..51aa4313a97b5 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js @@ -20,8 +20,14 @@ import { cloneDeep } from 'lodash'; import { VegaParser } from './vega_parser'; import { bypassExternalUrlCheck } from '../vega_view/vega_base_view'; + jest.mock('../services'); +jest.mock('../lib/vega', () => ({ + vega: jest.requireActual('vega'), + vegaLite: jest.requireActual('vega-lite'), +})); + describe(`VegaParser._setDefaultValue`, () => { function check(spec, expected, ...params) { return () => { @@ -146,30 +152,30 @@ describe('VegaParser._parseSchema', () => { test('should warn on no vega version specified', () => { const vp = new VegaParser({}); expect(vp._parseSchema()).toBe(false); - expect(vp.spec).toEqual({ $schema: 'https://vega.github.io/schema/vega/v3.0.json' }); + expect(vp.spec).toEqual({ $schema: 'https://vega.github.io/schema/vega/v5.json' }); expect(vp.warnings).toHaveLength(1); }); test( 'should not warn on current vega version', - check('https://vega.github.io/schema/vega/v4.0.json', false, 0) + check('https://vega.github.io/schema/vega/v5.json', false, 0) ); test( 'should not warn on older vega version', - check('https://vega.github.io/schema/vega/v3.0.json', false, 0) + check('https://vega.github.io/schema/vega/v4.json', false, 0) ); test( 'should warn on vega version too new to be supported', - check('https://vega.github.io/schema/vega/v5.0.json', false, 1) + check('https://vega.github.io/schema/vega/v5.99.json', false, 1) ); test( 'should not warn on current vega-lite version', - check('https://vega.github.io/schema/vega-lite/v2.0.json', true, 0) + check('https://vega.github.io/schema/vega-lite/v4.json', true, 0) ); test( 'should warn on vega-lite version too new to be supported', - check('https://vega.github.io/schema/vega-lite/v3.0.json', true, 1) + check('https://vega.github.io/schema/vega-lite/v5.json', true, 1) ); }); diff --git a/src/plugins/vis_type_vega/public/default.spec.hjson b/src/plugins/vis_type_vega/public/default.spec.hjson index cc60832619290..ace1950f4e909 100644 --- a/src/plugins/vis_type_vega/public/default.spec.hjson +++ b/src/plugins/vis_type_vega/public/default.spec.hjson @@ -6,7 +6,7 @@ Welcome to Vega visualizations. Here you can design your own dataviz from scrat This example graph shows the document count in all indexes in the current time range. You might need to adjust the time filter in the upper right corner. */ - $schema: https://vega.github.io/schema/vega-lite/v2.json + $schema: https://vega.github.io/schema/vega-lite/v4.json title: Event counts from all indexes // Define the data source diff --git a/src/plugins/vis_type_vega/public/lib/vega.js b/src/plugins/vis_type_vega/public/lib/vega.js new file mode 100644 index 0000000000000..eed4ecb60e151 --- /dev/null +++ b/src/plugins/vis_type_vega/public/lib/vega.js @@ -0,0 +1,23 @@ +/* + * 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 * as vegaLite from 'vega-lite/build-es5/vega-lite'; +import * as vega from 'vega/build-es5/vega'; + +export { vega, vegaLite }; diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js index 4f6697de1f29e..069664a9c91c1 100644 --- a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js +++ b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js @@ -20,8 +20,7 @@ import $ from 'jquery'; import moment from 'moment'; import dateMath from '@elastic/datemath'; -import * as vega from 'vega-lib'; -import * as vegaLite from 'vega-lite'; +import { vega, vegaLite } from '../lib/vega'; import { Utils } from '../data_model/utils'; import { VISUALIZATION_COLORS } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_map_view.js b/src/plugins/vis_type_vega/public/vega_view/vega_map_view.js index 1cc163848fcf0..6908fd13a9ca1 100644 --- a/src/plugins/vis_type_vega/public/vega_view/vega_map_view.js +++ b/src/plugins/vis_type_vega/public/vega_view/vega_map_view.js @@ -17,8 +17,8 @@ * under the License. */ -import * as vega from 'vega-lib'; import { i18n } from '@kbn/i18n'; +import { vega } from '../lib/vega'; import { VegaBaseView } from './vega_base_view'; import { VegaMapLayer } from './vega_map_layer'; import { getEmsTileLayerId, getUISettings, getKibanaMapFactory } from '../services'; diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_view.js b/src/plugins/vis_type_vega/public/vega_view/vega_view.js index a0b0deb4dac19..e3455b97b7fe2 100644 --- a/src/plugins/vis_type_vega/public/vega_view/vega_view.js +++ b/src/plugins/vis_type_vega/public/vega_view/vega_view.js @@ -17,7 +17,7 @@ * under the License. */ -import * as vega from 'vega-lib'; +import { vega } from '../lib/vega'; import { VegaBaseView } from './vega_base_view'; export class VegaView extends VegaBaseView { diff --git a/test/accessibility/services/a11y/a11y.ts b/test/accessibility/services/a11y/a11y.ts index 24f16518bc429..525445c60818a 100644 --- a/test/accessibility/services/a11y/a11y.ts +++ b/test/accessibility/services/a11y/a11y.ts @@ -70,7 +70,12 @@ export function A11yProvider({ getService }: FtrProviderContext) { exclude: ([] as string[]) .concat(excludeTestSubj || []) .map((ts) => [testSubjectToCss(ts)]) - .concat([['.ace_scrollbar']]), + .concat([ + ['.ace_scrollbar'], + [ + '.leaflet-vega-container[role="graphics-document"][aria-roledescription="visualization"]', + ], + ]), }; } diff --git a/test/examples/embeddables/adding_children.ts b/test/examples/embeddables/adding_children.ts index 4e3d9d30b01aa..9c088f0a5d313 100644 --- a/test/examples/embeddables/adding_children.ts +++ b/test/examples/embeddables/adding_children.ts @@ -24,6 +24,7 @@ import { PluginFunctionalProviderContext } from 'test/plugin_functional/services export default function ({ getService }: PluginFunctionalProviderContext) { const testSubjects = getService('testSubjects'); const flyout = getService('flyout'); + const retry = getService('retry'); describe('creating and adding children', () => { before(async () => { @@ -33,8 +34,15 @@ export default function ({ getService }: PluginFunctionalProviderContext) { it('Can create a new child', async () => { await testSubjects.click('embeddablePanelToggleMenuIcon'); await testSubjects.click('embeddablePanelAction-ACTION_ADD_PANEL'); - await testSubjects.click('createNew'); + + // this seem like an overkill, but clicking this button which opens context menu was flaky + await testSubjects.waitForEnabled('createNew'); + await retry.waitFor('createNew popover opened', async () => { + await testSubjects.click('createNew'); + return await testSubjects.exists('createNew-TODO_EMBEDDABLE'); + }); await testSubjects.click('createNew-TODO_EMBEDDABLE'); + await testSubjects.setValue('taskInputField', 'new task'); await testSubjects.click('createTodoEmbeddable'); const tasks = await testSubjects.getVisibleTextAll('todoEmbeddableTask'); @@ -44,6 +52,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) { it('Can add a child backed off a saved object', async () => { await testSubjects.click('embeddablePanelToggleMenuIcon'); await testSubjects.click('embeddablePanelAction-ACTION_ADD_PANEL'); + await testSubjects.waitForDeleted('savedObjectFinderLoadingIndicator'); await testSubjects.click('savedObjectTitleGarbage'); await testSubjects.moveMouseTo('euiFlyoutCloseButton'); await flyout.ensureClosed('dashboardAddPanel'); diff --git a/test/functional/apps/dashboard/dashboard_filter_bar.js b/test/functional/apps/dashboard/dashboard_filter_bar.js index c931e6763f483..f9b0d0a370c06 100644 --- a/test/functional/apps/dashboard/dashboard_filter_bar.js +++ b/test/functional/apps/dashboard/dashboard_filter_bar.js @@ -170,6 +170,8 @@ export default function ({ getService, getPageObjects }) { }); describe('saved search filtering', function () { + // https://github.com/elastic/kibana/issues/47286#issuecomment-644687577 + this.tags('skipCoverage'); before(async () => { await filterBar.ensureFieldEditorModalIsClosed(); await PageObjects.dashboard.gotoDashboardLandingPage(); diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index fe5694efc35da..d08e88ecf47ea 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -409,7 +409,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo async closeToastIfExists() { const toastShown = await find.existsByCssSelector('.euiToast'); if (toastShown) { - await this.closeToast(); + await find.clickByCssSelector('.euiToast__closeButton'); } } diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/app/components/main.tsx b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/app/components/main.tsx index ace2af2b4f0cf..b4f9634b23d29 100644 --- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/app/components/main.tsx +++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/app/components/main.tsx @@ -21,7 +21,7 @@ import React from 'react'; import { EuiPage, EuiPageBody, EuiPageContent, EuiPageContentHeader } from '@elastic/eui'; import { first } from 'rxjs/operators'; import { IInterpreterRenderHandlers, ExpressionValue } from 'src/plugins/expressions'; -import { RequestAdapter, DataAdapter } from '../../../../../../../src/plugins/inspector'; +import { RequestAdapter, DataAdapter } from '../../../../../../../src/plugins/inspector/public'; import { Adapters, ExpressionRenderHandler } from '../../types'; import { getExpressions } from '../../services'; diff --git a/test/scripts/jenkins_build_kibana.sh b/test/scripts/jenkins_build_kibana.sh index e3f46e7a6ada4..3e49edc8e6ae5 100755 --- a/test/scripts/jenkins_build_kibana.sh +++ b/test/scripts/jenkins_build_kibana.sh @@ -2,9 +2,16 @@ source src/dev/ci_setup/setup_env.sh -echo " -> building kibana platform plugins" +echo " -> building examples separate from test plugins" node scripts/build_kibana_platform_plugins \ --oss \ + --examples \ + --verbose; + +echo " -> building test plugins" +node scripts/build_kibana_platform_plugins \ + --oss \ + --no-examples \ --scan-dir "$KIBANA_DIR/test/plugin_functional/plugins" \ --scan-dir "$KIBANA_DIR/test/interpreter_functional/plugins" \ --verbose; diff --git a/test/scripts/jenkins_xpack_build_kibana.sh b/test/scripts/jenkins_xpack_build_kibana.sh index c962b962b1e5e..58ef6a42d3fe4 100755 --- a/test/scripts/jenkins_xpack_build_kibana.sh +++ b/test/scripts/jenkins_xpack_build_kibana.sh @@ -3,8 +3,14 @@ cd "$KIBANA_DIR" source src/dev/ci_setup/setup_env.sh -echo " -> building kibana platform plugins" +echo " -> building examples separate from test plugins" node scripts/build_kibana_platform_plugins \ + --examples \ + --verbose; + +echo " -> building test plugins" +node scripts/build_kibana_platform_plugins \ + --no-examples \ --scan-dir "$KIBANA_DIR/test/plugin_functional/plugins" \ --scan-dir "$XPACK_DIR/test/plugin_functional/plugins" \ --scan-dir "$XPACK_DIR/test/functional_with_es_ssl/fixtures/plugins" \ diff --git a/x-pack/package.json b/x-pack/package.json index 2513a36f50014..e24d75cc0d968 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -98,7 +98,7 @@ "@types/react": "^16.9.36", "@types/react-beautiful-dnd": "^12.1.1", "@types/react-dom": "^16.9.8", - "@types/react-redux": "^7.1.7", + "@types/react-redux": "^7.1.9", "@types/react-router-dom": "^5.1.3", "@types/react-sticky": "^6.0.3", "@types/react-test-renderer": "^16.9.1", @@ -188,7 +188,7 @@ "@babel/core": "^7.10.2", "@babel/register": "^7.10.1", "@babel/runtime": "^7.10.2", - "@elastic/apm-rum-react": "^1.1.1", + "@elastic/apm-rum-react": "^1.1.2", "@elastic/datemath": "5.0.3", "@elastic/ems-client": "7.9.3", "@elastic/eui": "24.1.0", @@ -237,6 +237,7 @@ "cytoscape": "^3.10.0", "d3": "3.5.17", "d3-scale": "1.0.7", + "d3-array": "1.2.4", "dedent": "^0.7.0", "del": "^5.1.0", "dragselect": "1.13.1", @@ -321,7 +322,7 @@ "react-markdown": "^4.3.1", "react-moment-proptypes": "^1.7.0", "react-portal": "^3.2.0", - "react-redux": "^7.1.3", + "react-redux": "^7.2.0", "react-reverse-portal": "^1.0.4", "react-router": "^5.1.2", "react-router-dom": "^5.1.2", @@ -330,6 +331,7 @@ "react-syntax-highlighter": "^5.7.0", "react-tiny-virtual-list": "^2.2.0", "react-use": "^13.27.0", + "react-virtualized": "^9.21.2", "react-vis": "^1.8.1", "react-visibility-sensor": "^5.1.1", "recompose": "^0.26.0", diff --git a/x-pack/plugins/actions/README.md b/x-pack/plugins/actions/README.md index 96d5f04ac088f..605676cee363d 100644 --- a/x-pack/plugins/actions/README.md +++ b/x-pack/plugins/actions/README.md @@ -234,9 +234,24 @@ Payload: ## Firing actions -The plugin exposes an execute function that you can use to run actions. +Running actions is possible by using the ActionsClient which is provided by the `getActionsClientWithRequest` function part of the plugin's Start Contract. +By providing the user's Request you'll receive an instance of the ActionsClient which is tailered to the current user and is scoped to the resources the user is authorized to access. -**server.plugins.actions.execute(options)** +## Accessing a scoped ActionsClient + +``` +const actionsClient = server.plugins.actions.getActionsClientWithRequest(request); +``` + +Once you have a scoped ActionsClient you can execute an action by caling either the `enqueueExecution` which will schedule the action to run later or the `execute` apis which will run it immediately and return the result respectively. + +### actionsClient.enqueueExecution(options) + +This api schedules a task which will run the action using the current user scope at the soonest opportunity. + +Running the action by scheduling a task means that we will no longer have a user request by which to ascertain the action's privileges and so you might need to provide these yourself: +- The **SpaceId** in which the user's action is expected to run +- When security is enabled you'll also need to provide an **apiKey** which allows us to mimic the user and their privileges. The following table describes the properties of the `options` object. @@ -251,8 +266,9 @@ The following table describes the properties of the `options` object. This example makes action `3c5b2bd4-5424-4e4b-8cf5-c0a58c762cc5` send an email. The action plugin will load the saved object and find what action type to call with `params`. -``` -server.plugins.actions.execute({ +```typescript +const actionsClient = await server.plugins.actions.getActionsClientWithRequest(request); +await actionsClient.enqueueExecution({ id: '3c5b2bd4-5424-4e4b-8cf5-c0a58c762cc5', spaceId: 'default', // The spaceId of the action params: { @@ -264,6 +280,34 @@ server.plugins.actions.execute({ }); ``` +### actionsClient.execute(options) + +This api runs the action and asynchronously returns the result of running the action. + +The following table describes the properties of the `options` object. + +| Property | Description | Type | +| -------- | ------------------------------------------------------------------------------------------------------ | ------ | +| id | The id of the action you want to execute. | string | +| params | The `params` value to give the action type executor. | object | + +## Example + +As with the previous example, we'll use the action `3c5b2bd4-5424-4e4b-8cf5-c0a58c762cc5` to send an email. + +```typescript +const actionsClient = await server.plugins.actions.getActionsClientWithRequest(request); +const result = await actionsClient.execute({ + id: '3c5b2bd4-5424-4e4b-8cf5-c0a58c762cc5', + params: { + from: 'example@elastic.co', + to: ['destination@elastic.co'], + subject: 'My email subject', + body: 'My email body', + }, +}); +``` + # Built-in Action Types Kibana ships with a set of built-in action types: diff --git a/x-pack/plugins/actions/server/actions_client.mock.ts b/x-pack/plugins/actions/server/actions_client.mock.ts index a2b64e49f76e3..efd044c7e2493 100644 --- a/x-pack/plugins/actions/server/actions_client.mock.ts +++ b/x-pack/plugins/actions/server/actions_client.mock.ts @@ -17,6 +17,8 @@ const createActionsClientMock = () => { update: jest.fn(), getAll: jest.fn(), getBulk: jest.fn(), + execute: jest.fn(), + enqueueExecution: jest.fn(), }; return mocked; }; diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index bf55a1c18d169..2865bbbe1d944 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -19,10 +19,16 @@ import { elasticsearchServiceMock, savedObjectsClientMock, } from '../../../../src/core/server/mocks'; +import { actionExecutorMock } from './lib/action_executor.mock'; +import uuid from 'uuid'; +import { KibanaRequest } from 'kibana/server'; const defaultKibanaIndex = '.kibana'; const savedObjectsClient = savedObjectsClientMock.create(); const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); +const actionExecutor = actionExecutorMock.create(); +const executionEnqueuer = jest.fn(); +const request = {} as KibanaRequest; const mockTaskManager = taskManagerMock.setup(); @@ -53,6 +59,9 @@ beforeEach(() => { scopedClusterClient, defaultKibanaIndex, preconfiguredActions: [], + actionExecutor, + executionEnqueuer, + request, }); }); @@ -232,6 +241,9 @@ describe('create()', () => { scopedClusterClient, defaultKibanaIndex, preconfiguredActions: [], + actionExecutor, + executionEnqueuer, + request, }); const savedObjectCreateResult = { @@ -328,6 +340,9 @@ describe('get()', () => { savedObjectsClient, scopedClusterClient, defaultKibanaIndex, + actionExecutor, + executionEnqueuer, + request, preconfiguredActions: [ { id: 'testPreconfigured', @@ -388,6 +403,9 @@ describe('getAll()', () => { savedObjectsClient, scopedClusterClient, defaultKibanaIndex, + actionExecutor, + executionEnqueuer, + request, preconfiguredActions: [ { id: 'testPreconfigured', @@ -453,6 +471,9 @@ describe('getBulk()', () => { savedObjectsClient, scopedClusterClient, defaultKibanaIndex, + actionExecutor, + executionEnqueuer, + request, preconfiguredActions: [ { id: 'testPreconfigured', @@ -718,3 +739,40 @@ describe('update()', () => { ).rejects.toThrowErrorMatchingInlineSnapshot(`"Fail"`); }); }); + +describe('execute()', () => { + test('calls the actionExecutor with the appropriate parameters', async () => { + const actionId = uuid.v4(); + actionExecutor.execute.mockResolvedValue({ status: 'ok', actionId }); + await expect( + actionsClient.execute({ + actionId, + params: { + name: 'my name', + }, + }) + ).resolves.toMatchObject({ status: 'ok', actionId }); + + expect(actionExecutor.execute).toHaveBeenCalledWith({ + actionId, + request, + params: { + name: 'my name', + }, + }); + }); +}); + +describe('enqueueExecution()', () => { + test('calls the executionEnqueuer with the appropriate parameters', async () => { + const opts = { + id: uuid.v4(), + params: { baz: false }, + spaceId: 'default', + apiKey: Buffer.from('123:abc').toString('base64'), + }; + await expect(actionsClient.enqueueExecution(opts)).resolves.toMatchInlineSnapshot(`undefined`); + + expect(executionEnqueuer).toHaveBeenCalledWith(savedObjectsClient, opts); + }); +}); diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index 48703f01f5509..a2feac83cba9f 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -9,13 +9,25 @@ import { SavedObjectsClientContract, SavedObjectAttributes, SavedObject, + KibanaRequest, } from 'src/core/server'; import { i18n } from '@kbn/i18n'; import { ActionTypeRegistry } from './action_type_registry'; -import { validateConfig, validateSecrets } from './lib'; -import { ActionResult, FindActionResult, RawAction, PreConfiguredAction } from './types'; +import { validateConfig, validateSecrets, ActionExecutorContract } from './lib'; +import { + ActionResult, + FindActionResult, + RawAction, + PreConfiguredAction, + ActionTypeExecutorResult, +} from './types'; import { PreconfiguredActionDisabledModificationError } from './lib/errors/preconfigured_action_disabled_modification'; +import { ExecuteOptions } from './lib/action_executor'; +import { + ExecutionEnqueuer, + ExecuteOptions as EnqueueExecutionOptions, +} from './create_execute_function'; // We are assuming there won't be many actions. This is why we will load // all the actions in advance and assume the total count to not go over 10000. @@ -42,6 +54,9 @@ interface ConstructorOptions { actionTypeRegistry: ActionTypeRegistry; savedObjectsClient: SavedObjectsClientContract; preconfiguredActions: PreConfiguredAction[]; + actionExecutor: ActionExecutorContract; + executionEnqueuer: ExecutionEnqueuer; + request: KibanaRequest; } interface UpdateOptions { @@ -55,6 +70,9 @@ export class ActionsClient { private readonly savedObjectsClient: SavedObjectsClientContract; private readonly actionTypeRegistry: ActionTypeRegistry; private readonly preconfiguredActions: PreConfiguredAction[]; + private readonly actionExecutor: ActionExecutorContract; + private readonly request: KibanaRequest; + private readonly executionEnqueuer: ExecutionEnqueuer; constructor({ actionTypeRegistry, @@ -62,12 +80,18 @@ export class ActionsClient { scopedClusterClient, savedObjectsClient, preconfiguredActions, + actionExecutor, + executionEnqueuer, + request, }: ConstructorOptions) { this.actionTypeRegistry = actionTypeRegistry; this.savedObjectsClient = savedObjectsClient; this.scopedClusterClient = scopedClusterClient; this.defaultKibanaIndex = defaultKibanaIndex; this.preconfiguredActions = preconfiguredActions; + this.actionExecutor = actionExecutor; + this.executionEnqueuer = executionEnqueuer; + this.request = request; } /** @@ -251,6 +275,17 @@ export class ActionsClient { } return await this.savedObjectsClient.delete('action', id); } + + public async execute({ + actionId, + params, + }: Omit): Promise { + return this.actionExecutor.execute({ actionId, params, request: this.request }); + } + + public async enqueueExecution(options: EnqueueExecutionOptions): Promise { + return this.executionEnqueuer(this.savedObjectsClient, options); + } } function actionFromSavedObject(savedObject: SavedObject): ActionResult { diff --git a/x-pack/plugins/actions/server/create_execute_function.test.ts b/x-pack/plugins/actions/server/create_execute_function.test.ts index 1b7752588e3d3..04d4d92945cdb 100644 --- a/x-pack/plugins/actions/server/create_execute_function.test.ts +++ b/x-pack/plugins/actions/server/create_execute_function.test.ts @@ -5,23 +5,20 @@ */ import { taskManagerMock } from '../../task_manager/server/task_manager.mock'; -import { createExecuteFunction } from './create_execute_function'; +import { createExecutionEnqueuerFunction } from './create_execute_function'; import { savedObjectsClientMock } from '../../../../src/core/server/mocks'; import { actionTypeRegistryMock } from './action_type_registry.mock'; const mockTaskManager = taskManagerMock.start(); const savedObjectsClient = savedObjectsClientMock.create(); -const getBasePath = jest.fn(); beforeEach(() => jest.resetAllMocks()); describe('execute()', () => { test('schedules the action with all given parameters', async () => { - const executeFn = createExecuteFunction({ - getBasePath, + const executeFn = createExecutionEnqueuerFunction({ taskManager: mockTaskManager, actionTypeRegistry: actionTypeRegistryMock.create(), - getScopedSavedObjectsClient: jest.fn().mockReturnValueOnce(savedObjectsClient), isESOUsingEphemeralEncryptionKey: false, preconfiguredActions: [], }); @@ -39,7 +36,7 @@ describe('execute()', () => { attributes: {}, references: [], }); - await executeFn({ + await executeFn(savedObjectsClient, { id: '123', params: { baz: false }, spaceId: 'default', @@ -70,11 +67,9 @@ describe('execute()', () => { }); test('schedules the action with all given parameters with a preconfigured action', async () => { - const executeFn = createExecuteFunction({ - getBasePath, + const executeFn = createExecutionEnqueuerFunction({ taskManager: mockTaskManager, actionTypeRegistry: actionTypeRegistryMock.create(), - getScopedSavedObjectsClient: jest.fn().mockReturnValueOnce(savedObjectsClient), isESOUsingEphemeralEncryptionKey: false, preconfiguredActions: [ { @@ -101,7 +96,7 @@ describe('execute()', () => { attributes: {}, references: [], }); - await executeFn({ + await executeFn(savedObjectsClient, { id: '123', params: { baz: false }, spaceId: 'default', @@ -131,115 +126,15 @@ describe('execute()', () => { }); }); - test('uses API key when provided', async () => { - const getScopedSavedObjectsClient = jest.fn().mockReturnValueOnce(savedObjectsClient); - const executeFn = createExecuteFunction({ - getBasePath, - taskManager: mockTaskManager, - getScopedSavedObjectsClient, - isESOUsingEphemeralEncryptionKey: false, - actionTypeRegistry: actionTypeRegistryMock.create(), - preconfiguredActions: [], - }); - savedObjectsClient.get.mockResolvedValueOnce({ - id: '123', - type: 'action', - attributes: { - actionTypeId: 'mock-action', - }, - references: [], - }); - savedObjectsClient.create.mockResolvedValueOnce({ - id: '234', - type: 'action_task_params', - attributes: {}, - references: [], - }); - - await executeFn({ - id: '123', - params: { baz: false }, - spaceId: 'default', - apiKey: Buffer.from('123:abc').toString('base64'), - }); - expect(getScopedSavedObjectsClient).toHaveBeenCalledWith({ - getBasePath: expect.anything(), - headers: { - // base64 encoded "123:abc" - authorization: 'ApiKey MTIzOmFiYw==', - }, - path: '/', - route: { settings: {} }, - url: { - href: '/', - }, - raw: { - req: { - url: '/', - }, - }, - }); - }); - - test(`doesn't use API keys when not provided`, async () => { - const getScopedSavedObjectsClient = jest.fn().mockReturnValueOnce(savedObjectsClient); - const executeFn = createExecuteFunction({ - getBasePath, - taskManager: mockTaskManager, - getScopedSavedObjectsClient, - isESOUsingEphemeralEncryptionKey: false, - actionTypeRegistry: actionTypeRegistryMock.create(), - preconfiguredActions: [], - }); - savedObjectsClient.get.mockResolvedValueOnce({ - id: '123', - type: 'action', - attributes: { - actionTypeId: 'mock-action', - }, - references: [], - }); - savedObjectsClient.create.mockResolvedValueOnce({ - id: '234', - type: 'action_task_params', - attributes: {}, - references: [], - }); - - await executeFn({ - id: '123', - params: { baz: false }, - spaceId: 'default', - apiKey: null, - }); - expect(getScopedSavedObjectsClient).toHaveBeenCalledWith({ - getBasePath: expect.anything(), - headers: {}, - path: '/', - route: { settings: {} }, - url: { - href: '/', - }, - raw: { - req: { - url: '/', - }, - }, - }); - }); - test('throws when passing isESOUsingEphemeralEncryptionKey with true as a value', async () => { - const getScopedSavedObjectsClient = jest.fn().mockReturnValueOnce(savedObjectsClient); - const executeFn = createExecuteFunction({ - getBasePath, + const executeFn = createExecutionEnqueuerFunction({ taskManager: mockTaskManager, - getScopedSavedObjectsClient, isESOUsingEphemeralEncryptionKey: true, actionTypeRegistry: actionTypeRegistryMock.create(), preconfiguredActions: [], }); await expect( - executeFn({ + executeFn(savedObjectsClient, { id: '123', params: { baz: false }, spaceId: 'default', @@ -252,11 +147,8 @@ describe('execute()', () => { test('should ensure action type is enabled', async () => { const mockedActionTypeRegistry = actionTypeRegistryMock.create(); - const getScopedSavedObjectsClient = jest.fn().mockReturnValueOnce(savedObjectsClient); - const executeFn = createExecuteFunction({ - getBasePath, + const executeFn = createExecutionEnqueuerFunction({ taskManager: mockTaskManager, - getScopedSavedObjectsClient, isESOUsingEphemeralEncryptionKey: false, actionTypeRegistry: mockedActionTypeRegistry, preconfiguredActions: [], @@ -274,7 +166,7 @@ describe('execute()', () => { }); await expect( - executeFn({ + executeFn(savedObjectsClient, { id: '123', params: { baz: false }, spaceId: 'default', @@ -285,11 +177,8 @@ describe('execute()', () => { test('should skip ensure action type if action type is preconfigured and license is valid', async () => { const mockedActionTypeRegistry = actionTypeRegistryMock.create(); - const getScopedSavedObjectsClient = jest.fn().mockReturnValueOnce(savedObjectsClient); - const executeFn = createExecuteFunction({ - getBasePath, + const executeFn = createExecutionEnqueuerFunction({ taskManager: mockTaskManager, - getScopedSavedObjectsClient, isESOUsingEphemeralEncryptionKey: false, actionTypeRegistry: mockedActionTypeRegistry, preconfiguredActions: [ @@ -303,9 +192,6 @@ describe('execute()', () => { }, ], }); - mockedActionTypeRegistry.ensureActionTypeEnabled.mockImplementation(() => { - throw new Error('Fail'); - }); mockedActionTypeRegistry.isActionExecutable.mockImplementation(() => true); savedObjectsClient.get.mockResolvedValueOnce({ id: '123', @@ -322,25 +208,13 @@ describe('execute()', () => { references: [], }); - await executeFn({ + await executeFn(savedObjectsClient, { id: '123', params: { baz: false }, spaceId: 'default', apiKey: null, }); - expect(getScopedSavedObjectsClient).toHaveBeenCalledWith({ - getBasePath: expect.anything(), - headers: {}, - path: '/', - route: { settings: {} }, - url: { - href: '/', - }, - raw: { - req: { - url: '/', - }, - }, - }); + + expect(mockedActionTypeRegistry.ensureActionTypeEnabled).not.toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/actions/server/create_execute_function.ts b/x-pack/plugins/actions/server/create_execute_function.ts index a16b2f4295072..2bad33d56f228 100644 --- a/x-pack/plugins/actions/server/create_execute_function.ts +++ b/x-pack/plugins/actions/server/create_execute_function.ts @@ -4,19 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract, KibanaRequest } from '../../../../src/core/server'; +import { SavedObjectsClientContract } from '../../../../src/core/server'; import { TaskManagerStartContract } from '../../task_manager/server'; -import { - GetBasePathFunction, - RawAction, - ActionTypeRegistryContract, - PreConfiguredAction, -} from './types'; +import { RawAction, ActionTypeRegistryContract, PreConfiguredAction } from './types'; interface CreateExecuteFunctionOptions { taskManager: TaskManagerStartContract; - getScopedSavedObjectsClient: (request: KibanaRequest) => SavedObjectsClientContract; - getBasePath: GetBasePathFunction; isESOUsingEphemeralEncryptionKey: boolean; actionTypeRegistry: ActionTypeRegistryContract; preconfiguredActions: PreConfiguredAction[]; @@ -29,45 +22,27 @@ export interface ExecuteOptions { apiKey: string | null; } -export function createExecuteFunction({ - getBasePath, +export type ExecutionEnqueuer = ( + savedObjectsClient: SavedObjectsClientContract, + options: ExecuteOptions +) => Promise; + +export function createExecutionEnqueuerFunction({ taskManager, actionTypeRegistry, - getScopedSavedObjectsClient, isESOUsingEphemeralEncryptionKey, preconfiguredActions, }: CreateExecuteFunctionOptions) { - return async function execute({ id, params, spaceId, apiKey }: ExecuteOptions) { + return async function execute( + savedObjectsClient: SavedObjectsClientContract, + { id, params, spaceId, apiKey }: ExecuteOptions + ) { if (isESOUsingEphemeralEncryptionKey === true) { throw new Error( `Unable to execute action due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml` ); } - const requestHeaders: Record = {}; - - if (apiKey) { - requestHeaders.authorization = `ApiKey ${apiKey}`; - } - - // Since we're using API keys and accessing elasticsearch can only be done - // via a request, we're faking one with the proper authorization headers. - const fakeRequest: unknown = { - headers: requestHeaders, - getBasePath: () => getBasePath(spaceId), - path: '/', - route: { settings: {} }, - url: { - href: '/', - }, - raw: { - req: { - url: '/', - }, - }, - }; - - const savedObjectsClient = getScopedSavedObjectsClient(fakeRequest as KibanaRequest); const actionTypeId = await getActionTypeId(id); if (!actionTypeRegistry.isActionExecutable(id, actionTypeId)) { diff --git a/x-pack/plugins/actions/server/mocks.ts b/x-pack/plugins/actions/server/mocks.ts index 09bc137da736f..1763d275c6fb0 100644 --- a/x-pack/plugins/actions/server/mocks.ts +++ b/x-pack/plugins/actions/server/mocks.ts @@ -23,7 +23,6 @@ const createSetupMock = () => { const createStartMock = () => { const mock: jest.Mocked = { - execute: jest.fn(), isActionTypeEnabled: jest.fn(), isActionExecutable: jest.fn(), getActionsClientWithRequest: jest.fn().mockResolvedValue(actionsClientMock.create()), diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 8c1b21e600ae1..ad3fa97ee0c36 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -35,8 +35,7 @@ import { Services, ActionType, PreConfiguredAction } from './types'; import { ActionExecutor, TaskRunnerFactory, LicenseState, ILicenseState } from './lib'; import { ActionsClient } from './actions_client'; import { ActionTypeRegistry } from './action_type_registry'; -import { ExecuteOptions } from './create_execute_function'; -import { createExecuteFunction } from './create_execute_function'; +import { createExecutionEnqueuerFunction } from './create_execute_function'; import { registerBuiltInActionTypes } from './builtin_action_types'; import { registerActionsUsageCollector } from './usage'; @@ -68,7 +67,6 @@ export interface PluginSetupContract { export interface PluginStartContract { isActionTypeEnabled(id: string): boolean; isActionExecutable(actionId: string, actionTypeId: string): boolean; - execute(options: ExecuteOptions): Promise; getActionsClientWithRequest(request: KibanaRequest): Promise>; preconfiguredActions: PreConfiguredAction[]; } @@ -203,7 +201,7 @@ export class ActionsPlugin implements Plugin, Plugi getAllActionRoute(router, this.licenseState); updateActionRoute(router, this.licenseState); listActionTypesRoute(router, this.licenseState); - executeActionRoute(router, this.licenseState, actionExecutor); + executeActionRoute(router, this.licenseState); return { registerType: (actionType: ActionType) => { @@ -269,14 +267,6 @@ export class ActionsPlugin implements Plugin, Plugi scheduleActionsTelemetry(this.telemetryLogger, plugins.taskManager); return { - execute: createExecuteFunction({ - taskManager: plugins.taskManager, - actionTypeRegistry: actionTypeRegistry!, - getScopedSavedObjectsClient, - getBasePath: this.getBasePath, - isESOUsingEphemeralEncryptionKey: isESOUsingEphemeralEncryptionKey!, - preconfiguredActions, - }), isActionTypeEnabled: (id) => { return this.actionTypeRegistry!.isActionTypeEnabled(id); }, @@ -296,6 +286,14 @@ export class ActionsPlugin implements Plugin, Plugi defaultKibanaIndex: await kibanaIndex, scopedClusterClient: core.elasticsearch.legacy.client.asScoped(request), preconfiguredActions, + request, + actionExecutor: actionExecutor!, + executionEnqueuer: createExecutionEnqueuerFunction({ + taskManager: plugins.taskManager, + actionTypeRegistry: actionTypeRegistry!, + isESOUsingEphemeralEncryptionKey: isESOUsingEphemeralEncryptionKey!, + preconfiguredActions, + }), }); }, preconfiguredActions, @@ -319,10 +317,15 @@ export class ActionsPlugin implements Plugin, Plugi core: CoreSetup, defaultKibanaIndex: string ): IContextProvider, 'actions'> => { - const { actionTypeRegistry, isESOUsingEphemeralEncryptionKey, preconfiguredActions } = this; + const { + actionTypeRegistry, + isESOUsingEphemeralEncryptionKey, + preconfiguredActions, + actionExecutor, + } = this; return async function actionsRouteHandlerContext(context, request) { - const [{ savedObjects }] = await core.getStartServices(); + const [{ savedObjects }, { taskManager }] = await core.getStartServices(); return { getActionsClient: () => { if (isESOUsingEphemeralEncryptionKey === true) { @@ -336,6 +339,14 @@ export class ActionsPlugin implements Plugin, Plugi defaultKibanaIndex, scopedClusterClient: context.core.elasticsearch.legacy.client, preconfiguredActions, + request, + actionExecutor: actionExecutor!, + executionEnqueuer: createExecutionEnqueuerFunction({ + taskManager, + actionTypeRegistry: actionTypeRegistry!, + isESOUsingEphemeralEncryptionKey: isESOUsingEphemeralEncryptionKey!, + preconfiguredActions, + }), }); }, listTypes: actionTypeRegistry!.list.bind(actionTypeRegistry!), diff --git a/x-pack/plugins/actions/server/routes/execute.test.ts b/x-pack/plugins/actions/server/routes/execute.test.ts index 4467ddf372b21..6e8ebbf6f91cd 100644 --- a/x-pack/plugins/actions/server/routes/execute.test.ts +++ b/x-pack/plugins/actions/server/routes/execute.test.ts @@ -8,7 +8,9 @@ import { executeActionRoute } from './execute'; import { httpServiceMock } from 'src/core/server/mocks'; import { licenseStateMock } from '../lib/license_state.mock'; import { mockHandlerArguments } from './_mock_handler_arguments'; -import { ActionExecutorContract, verifyApiAccess, ActionTypeDisabledError } from '../lib'; +import { verifyApiAccess, ActionTypeDisabledError } from '../lib'; +import { actionsClientMock } from '../actions_client.mock'; +import { ActionTypeExecutorResult } from '../types'; jest.mock('../lib/verify_api_access.ts', () => ({ verifyApiAccess: jest.fn(), @@ -23,8 +25,11 @@ describe('executeActionRoute', () => { const licenseState = licenseStateMock.create(); const router = httpServiceMock.createRouter(); + const actionsClient = actionsClientMock.create(); + actionsClient.execute.mockResolvedValueOnce({ status: 'ok', actionId: '1' }); + const [context, req, res] = mockHandlerArguments( - {}, + { actionsClient }, { body: { params: { @@ -42,14 +47,8 @@ describe('executeActionRoute', () => { actionId: '1', status: 'ok', }; - const actionExecutor = { - initialize: jest.fn(), - execute: jest.fn(async ({ params, request, actionId }) => { - return executeResult; - }), - } as jest.Mocked; - executeActionRoute(router, licenseState, actionExecutor); + executeActionRoute(router, licenseState); const [config, handler] = router.post.mock.calls[0]; @@ -64,12 +63,11 @@ describe('executeActionRoute', () => { expect(await handler(context, req, res)).toEqual({ body: executeResult }); - expect(actionExecutor.execute).toHaveBeenCalledWith({ + expect(actionsClient.execute).toHaveBeenCalledWith({ actionId: '1', params: { someData: 'data', }, - request: req, }); expect(res.ok).toHaveBeenCalled(); @@ -79,8 +77,11 @@ describe('executeActionRoute', () => { const licenseState = licenseStateMock.create(); const router = httpServiceMock.createRouter(); + const actionsClient = actionsClientMock.create(); + actionsClient.execute.mockResolvedValueOnce((null as unknown) as ActionTypeExecutorResult); + const [context, req, res] = mockHandlerArguments( - {}, + { actionsClient }, { body: { params: {}, @@ -92,21 +93,15 @@ describe('executeActionRoute', () => { ['noContent'] ); - const actionExecutor = { - initialize: jest.fn(), - execute: jest.fn(), - } as jest.Mocked; - - executeActionRoute(router, licenseState, actionExecutor); + executeActionRoute(router, licenseState); const [, handler] = router.post.mock.calls[0]; expect(await handler(context, req, res)).toEqual(undefined); - expect(actionExecutor.execute).toHaveBeenCalledWith({ + expect(actionsClient.execute).toHaveBeenCalledWith({ actionId: '1', params: {}, - request: req, }); expect(res.ok).not.toHaveBeenCalled(); @@ -117,8 +112,14 @@ describe('executeActionRoute', () => { const licenseState = licenseStateMock.create(); const router = httpServiceMock.createRouter(); + const actionsClient = actionsClientMock.create(); + actionsClient.execute.mockResolvedValue({ + actionId: '1', + status: 'ok', + }); + const [context, req, res] = mockHandlerArguments( - {}, + { actionsClient }, { body: {}, params: {}, @@ -126,17 +127,7 @@ describe('executeActionRoute', () => { ['ok'] ); - const actionExecutor = { - initialize: jest.fn(), - execute: jest.fn(async ({ params, request, actionId }) => { - return { - actionId: '1', - status: 'ok', - }; - }), - } as jest.Mocked; - - executeActionRoute(router, licenseState, actionExecutor); + executeActionRoute(router, licenseState); const [, handler] = router.post.mock.calls[0]; @@ -149,12 +140,18 @@ describe('executeActionRoute', () => { const licenseState = licenseStateMock.create(); const router = httpServiceMock.createRouter(); + const actionsClient = actionsClientMock.create(); + actionsClient.execute.mockResolvedValue({ + actionId: '1', + status: 'ok', + }); + (verifyApiAccess as jest.Mock).mockImplementation(() => { throw new Error('OMG'); }); const [context, req, res] = mockHandlerArguments( - {}, + { actionsClient }, { body: {}, params: {}, @@ -162,17 +159,7 @@ describe('executeActionRoute', () => { ['ok'] ); - const actionExecutor = { - initialize: jest.fn(), - execute: jest.fn(async ({ params, request, actionId }) => { - return { - actionId: '1', - status: 'ok', - }; - }), - } as jest.Mocked; - - executeActionRoute(router, licenseState, actionExecutor); + executeActionRoute(router, licenseState); const [, handler] = router.post.mock.calls[0]; @@ -185,8 +172,11 @@ describe('executeActionRoute', () => { const licenseState = licenseStateMock.create(); const router = httpServiceMock.createRouter(); + const actionsClient = actionsClientMock.create(); + actionsClient.execute.mockRejectedValue(new ActionTypeDisabledError('Fail', 'license_invalid')); + const [context, req, res] = mockHandlerArguments( - {}, + { actionsClient }, { body: {}, params: {}, @@ -194,14 +184,7 @@ describe('executeActionRoute', () => { ['ok', 'forbidden'] ); - const actionExecutor = { - initialize: jest.fn(), - execute: jest.fn().mockImplementation(() => { - throw new ActionTypeDisabledError('Fail', 'license_invalid'); - }), - } as jest.Mocked; - - executeActionRoute(router, licenseState, actionExecutor); + executeActionRoute(router, licenseState); const [, handler] = router.post.mock.calls[0]; diff --git a/x-pack/plugins/actions/server/routes/execute.ts b/x-pack/plugins/actions/server/routes/execute.ts index 820bc191993d1..28e6a54f5e92d 100644 --- a/x-pack/plugins/actions/server/routes/execute.ts +++ b/x-pack/plugins/actions/server/routes/execute.ts @@ -13,7 +13,6 @@ import { } from 'kibana/server'; import { ILicenseState, verifyApiAccess, isErrorThatHandlesItsOwnResponse } from '../lib'; -import { ActionExecutorContract } from '../lib'; import { ActionTypeExecutorResult } from '../types'; import { BASE_ACTION_API_PATH } from '../../common'; @@ -25,11 +24,7 @@ const bodySchema = schema.object({ params: schema.recordOf(schema.string(), schema.any()), }); -export const executeActionRoute = ( - router: IRouter, - licenseState: ILicenseState, - actionExecutor: ActionExecutorContract -) => { +export const executeActionRoute = (router: IRouter, licenseState: ILicenseState) => { router.post( { path: `${BASE_ACTION_API_PATH}/action/{id}/_execute`, @@ -47,12 +42,17 @@ export const executeActionRoute = ( res: KibanaResponseFactory ): Promise { verifyApiAccess(licenseState); + + if (!context.actions) { + return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); + } + + const actionsClient = context.actions.getActionsClient(); const { params } = req.body; const { id } = req.params; try { - const body: ActionTypeExecutorResult = await actionExecutor.execute({ + const body: ActionTypeExecutorResult = await actionsClient.execute({ params, - request: req, actionId: id, }); return body diff --git a/x-pack/plugins/alerts/kibana.json b/x-pack/plugins/alerts/kibana.json index 3509f79dbbe4d..eef61ff4b3d53 100644 --- a/x-pack/plugins/alerts/kibana.json +++ b/x-pack/plugins/alerts/kibana.json @@ -6,5 +6,6 @@ "kibanaVersion": "kibana", "configPath": ["xpack", "alerts"], "requiredPlugins": ["licensing", "taskManager", "encryptedSavedObjects", "actions", "eventLog"], - "optionalPlugins": ["usageCollection", "spaces", "security"] + "optionalPlugins": ["usageCollection", "spaces", "security"], + "extraPublicDirs": ["common", "common/parse_duration"] } diff --git a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts index ceae9f04fe5ae..dd5a9f531bd58 100644 --- a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts @@ -7,8 +7,9 @@ import { AlertType } from '../types'; import { createExecutionHandler } from './create_execution_handler'; import { loggingServiceMock } from '../../../../../src/core/server/mocks'; -import { actionsMock } from '../../../actions/server/mocks'; +import { actionsMock, actionsClientMock } from '../../../actions/server/mocks'; import { eventLoggerMock } from '../../../event_log/server/event_logger.mock'; +import { KibanaRequest } from 'kibana/server'; const alertType: AlertType = { id: 'test', @@ -22,6 +23,7 @@ const alertType: AlertType = { producer: 'alerting', }; +const actionsClient = actionsClientMock.create(); const createExecutionHandlerParams = { actionsPlugin: actionsMock.createStart(), spaceId: 'default', @@ -47,15 +49,19 @@ const createExecutionHandlerParams = { }, }, ], + request: {} as KibanaRequest, }; beforeEach(() => { jest.resetAllMocks(); createExecutionHandlerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); createExecutionHandlerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); + createExecutionHandlerParams.actionsPlugin.getActionsClientWithRequest.mockResolvedValue( + actionsClient + ); }); -test('calls actionsPlugin.execute per selected action', async () => { +test('enqueues execution per selected action', async () => { const executionHandler = createExecutionHandler(createExecutionHandlerParams); await executionHandler({ actionGroup: 'default', @@ -63,8 +69,11 @@ test('calls actionsPlugin.execute per selected action', async () => { context: {}, alertInstanceId: '2', }); - expect(createExecutionHandlerParams.actionsPlugin.execute).toHaveBeenCalledTimes(1); - expect(createExecutionHandlerParams.actionsPlugin.execute.mock.calls[0]).toMatchInlineSnapshot(` + expect( + createExecutionHandlerParams.actionsPlugin.getActionsClientWithRequest + ).toHaveBeenCalledWith(createExecutionHandlerParams.request); + expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); + expect(actionsClient.enqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { "apiKey": "MTIzOmFiYw==", @@ -139,8 +148,8 @@ test(`doesn't call actionsPlugin.execute for disabled actionTypes`, async () => context: {}, alertInstanceId: '2', }); - expect(createExecutionHandlerParams.actionsPlugin.execute).toHaveBeenCalledTimes(1); - expect(createExecutionHandlerParams.actionsPlugin.execute).toHaveBeenCalledWith({ + expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); + expect(actionsClient.enqueueExecution).toHaveBeenCalledWith({ id: '2', params: { foo: true, @@ -180,7 +189,7 @@ test('trow error error message when action type is disabled', async () => { alertInstanceId: '2', }); - expect(createExecutionHandlerParams.actionsPlugin.execute).toHaveBeenCalledTimes(0); + expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(0); createExecutionHandlerParams.actionsPlugin.isActionExecutable.mockImplementation(() => true); const executionHandlerForPreconfiguredAction = createExecutionHandler({ @@ -193,7 +202,7 @@ test('trow error error message when action type is disabled', async () => { context: {}, alertInstanceId: '2', }); - expect(createExecutionHandlerParams.actionsPlugin.execute).toHaveBeenCalledTimes(1); + expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); }); test('limits actionsPlugin.execute per action group', async () => { @@ -204,7 +213,7 @@ test('limits actionsPlugin.execute per action group', async () => { context: {}, alertInstanceId: '2', }); - expect(createExecutionHandlerParams.actionsPlugin.execute).not.toHaveBeenCalled(); + expect(actionsClient.enqueueExecution).not.toHaveBeenCalled(); }); test('context attribute gets parameterized', async () => { @@ -215,8 +224,8 @@ test('context attribute gets parameterized', async () => { state: {}, alertInstanceId: '2', }); - expect(createExecutionHandlerParams.actionsPlugin.execute).toHaveBeenCalledTimes(1); - expect(createExecutionHandlerParams.actionsPlugin.execute.mock.calls[0]).toMatchInlineSnapshot(` + expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); + expect(actionsClient.enqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { "apiKey": "MTIzOmFiYw==", @@ -241,8 +250,8 @@ test('state attribute gets parameterized', async () => { state: { value: 'state-val' }, alertInstanceId: '2', }); - expect(createExecutionHandlerParams.actionsPlugin.execute).toHaveBeenCalledTimes(1); - expect(createExecutionHandlerParams.actionsPlugin.execute.mock.calls[0]).toMatchInlineSnapshot(` + expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); + expect(actionsClient.enqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { "apiKey": "MTIzOmFiYw==", diff --git a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts index 3c58c6d9ba288..8d859a570ba91 100644 --- a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts @@ -6,7 +6,7 @@ import { pluck } from 'lodash'; import { AlertAction, State, Context, AlertType } from '../types'; -import { Logger } from '../../../../../src/core/server'; +import { Logger, KibanaRequest } from '../../../../../src/core/server'; import { transformActionParams } from './transform_action_params'; import { PluginStartContract as ActionsPluginStartContract } from '../../../actions/server'; import { IEventLogger, IEvent, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; @@ -23,6 +23,7 @@ interface CreateExecutionHandlerOptions { alertType: AlertType; logger: Logger; eventLogger: IEventLogger; + request: KibanaRequest; } interface ExecutionHandlerOptions { @@ -43,6 +44,7 @@ export function createExecutionHandler({ apiKey, alertType, eventLogger, + request, }: CreateExecutionHandlerOptions) { const alertTypeActionGroups = new Set(pluck(alertType.actionGroups, 'id')); return async ({ actionGroup, context, state, alertInstanceId }: ExecutionHandlerOptions) => { @@ -80,7 +82,8 @@ export function createExecutionHandler({ // TODO would be nice to add the action name here, but it's not available const actionLabel = `${action.actionTypeId}:${action.id}`; - await actionsPlugin.execute({ + const actionsClient = await actionsPlugin.getActionsClientWithRequest(request); + await actionsClient.enqueueExecution({ id: action.id, params: action.params, spaceId, diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts index 983dff86d5602..690971bc87006 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts @@ -13,7 +13,7 @@ import { TaskRunner } from './task_runner'; import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks'; import { loggingServiceMock } from '../../../../../src/core/server/mocks'; import { PluginStartContract as ActionsPluginStart } from '../../../actions/server'; -import { actionsMock } from '../../../actions/server/mocks'; +import { actionsMock, actionsClientMock } from '../../../actions/server/mocks'; import { alertsMock } from '../mocks'; import { eventLoggerMock } from '../../../event_log/server/event_logger.mock'; import { IEventLogger } from '../../../event_log/server'; @@ -57,6 +57,7 @@ describe('Task Runner', () => { const encryptedSavedObjectsClient = encryptedSavedObjectsMock.createClient(); const services = alertsMock.createAlertServices(); const savedObjectsClient = services.savedObjectsClient; + const actionsClient = actionsClientMock.create(); const taskRunnerFactoryInitializerParams: jest.Mocked & { actionsPlugin: jest.Mocked; @@ -108,6 +109,9 @@ describe('Task Runner', () => { beforeEach(() => { jest.resetAllMocks(); taskRunnerFactoryInitializerParams.getServices.mockReturnValue(services); + taskRunnerFactoryInitializerParams.actionsPlugin.getActionsClientWithRequest.mockResolvedValue( + actionsClient + ); }); test('successfully executes the task', async () => { @@ -206,9 +210,129 @@ describe('Task Runner', () => { references: [], }); await taskRunner.run(); - expect(taskRunnerFactoryInitializerParams.actionsPlugin.execute).toHaveBeenCalledTimes(1); - expect(taskRunnerFactoryInitializerParams.actionsPlugin.execute.mock.calls[0]) - .toMatchInlineSnapshot(` + expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); + expect(actionsClient.enqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "apiKey": "MTIzOmFiYw==", + "id": "1", + "params": Object { + "foo": true, + }, + "spaceId": undefined, + }, + ] + `); + + const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; + expect(eventLogger.logEvent).toHaveBeenCalledTimes(3); + expect(eventLogger.logEvent).toHaveBeenCalledWith({ + event: { + action: 'execute', + outcome: 'success', + }, + kibana: { + saved_objects: [ + { + id: '1', + namespace: undefined, + rel: 'primary', + type: 'alert', + }, + ], + }, + message: "alert executed: test:1: 'alert-name'", + }); + expect(eventLogger.logEvent).toHaveBeenCalledWith({ + event: { + action: 'new-instance', + }, + kibana: { + alerting: { + instance_id: '1', + }, + saved_objects: [ + { + id: '1', + namespace: undefined, + rel: 'primary', + type: 'alert', + }, + ], + }, + message: "test:1: 'alert-name' created new instance: '1'", + }); + expect(eventLogger.logEvent).toHaveBeenCalledWith({ + event: { + action: 'execute-action', + }, + kibana: { + alerting: { + instance_id: '1', + }, + saved_objects: [ + { + id: '1', + namespace: undefined, + rel: 'primary', + type: 'alert', + }, + { + id: '1', + namespace: undefined, + type: 'action', + }, + ], + }, + message: + "alert: test:1: 'alert-name' instanceId: '1' scheduled actionGroup: 'default' action: undefined:1", + }); + }); + + test('includes the apiKey in the request used to initialize the actionsClient', async () => { + taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); + taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); + alertType.executor.mockImplementation( + ({ services: executorServices }: AlertExecutorOptions) => { + executorServices.alertInstanceFactory('1').scheduleActions('default'); + } + ); + const taskRunner = new TaskRunner( + alertType, + mockedTaskInstance, + taskRunnerFactoryInitializerParams + ); + savedObjectsClient.get.mockResolvedValueOnce(mockedAlertTypeSavedObject); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + apiKey: Buffer.from('123:abc').toString('base64'), + }, + references: [], + }); + await taskRunner.run(); + expect( + taskRunnerFactoryInitializerParams.actionsPlugin.getActionsClientWithRequest + ).toHaveBeenCalledWith({ + getBasePath: expect.anything(), + headers: { + // base64 encoded "123:abc" + authorization: 'ApiKey MTIzOmFiYw==', + }, + path: '/', + route: { settings: {} }, + url: { + href: '/', + }, + raw: { + req: { + url: '/', + }, + }, + }); + expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); + expect(actionsClient.enqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { "apiKey": "MTIzOmFiYw==", diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner.ts b/x-pack/plugins/alerts/server/task_runner/task_runner.ts index be399893088e3..3512ab16a3712 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner.ts @@ -71,14 +71,14 @@ export class TaskRunner { return apiKey; } - async getServicesWithSpaceLevelPermissions(spaceId: string, apiKey: string | null) { + private getFakeKibanaRequest(spaceId: string, apiKey: string | null) { const requestHeaders: Record = {}; if (apiKey) { requestHeaders.authorization = `ApiKey ${apiKey}`; } - const fakeRequest = { + return ({ headers: requestHeaders, getBasePath: () => this.context.getBasePath(spaceId), path: '/', @@ -91,9 +91,11 @@ export class TaskRunner { url: '/', }, }, - }; + } as unknown) as KibanaRequest; + } - return this.context.getServices((fakeRequest as unknown) as KibanaRequest); + async getServicesWithSpaceLevelPermissions(spaceId: string, apiKey: string | null) { + return this.context.getServices(this.getFakeKibanaRequest(spaceId, apiKey)); } private getExecutionHandler( @@ -128,6 +130,7 @@ export class TaskRunner { spaceId, alertType: this.alertType, eventLogger: this.context.eventLogger, + request: this.getFakeKibanaRequest(spaceId, apiKey), }); } diff --git a/x-pack/plugins/apm/e2e/run-e2e.sh b/x-pack/plugins/apm/e2e/run-e2e.sh index 4bebab8e0c6a8..aa7c0e21425ad 100755 --- a/x-pack/plugins/apm/e2e/run-e2e.sh +++ b/x-pack/plugins/apm/e2e/run-e2e.sh @@ -164,6 +164,7 @@ echo "✅ Setup completed successfully. Running tests..." # run cypress tests ################################################## yarn cypress run --config pageLoadTimeout=100000,watchForFileChanges=true +e2e_status=$? # # Run interactively @@ -171,3 +172,9 @@ yarn cypress run --config pageLoadTimeout=100000,watchForFileChanges=true echo "${bold}If you want to run the test interactively, run:${normal}" echo "" # newline echo "cd ${E2E_DIR} && yarn cypress open --config pageLoadTimeout=100000,watchForFileChanges=true" + +# Report the e2e status at the very end +if [ $e2e_status -ne 0 ]; then + echo "⚠️ Running tests failed." + exit 1 +fi diff --git a/x-pack/plugins/apm/kibana.json b/x-pack/plugins/apm/kibana.json index 1b8e7c4dc5431..56a9e226b6528 100644 --- a/x-pack/plugins/apm/kibana.json +++ b/x-pack/plugins/apm/kibana.json @@ -25,5 +25,8 @@ "configPath": [ "xpack", "apm" + ], + "extraPublicDirs": [ + "public/style/variables" ] } diff --git a/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap index b7ea026f80fde..ba3174156d5d4 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap +++ b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap @@ -434,7 +434,7 @@ Array [ > @@ -3370,7 +3370,7 @@ Array [ > { public getTPMFormatter = (t: number) => { const { urlParams } = this.props; const unit = tpmUnit(urlParams.transactionType); - return `${asInteger(t)} ${unit}`; + return `${asDecimal(t)} ${unit}`; }; public getTPMTooltipFormatter = (p: Coordinate) => { diff --git a/x-pack/plugins/apm/public/selectors/__tests__/chartSelectors.test.ts b/x-pack/plugins/apm/public/selectors/__tests__/chartSelectors.test.ts index 2f0a30a5019a9..901e6052bbf06 100644 --- a/x-pack/plugins/apm/public/selectors/__tests__/chartSelectors.test.ts +++ b/x-pack/plugins/apm/public/selectors/__tests__/chartSelectors.test.ts @@ -15,6 +15,8 @@ import { warningColor, errorColor, } from '../../utils/httpStatusCodeToColor'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { ApmTimeSeriesResponse } from '../../../server/lib/transactions/charts/get_timeseries_data/transform'; describe('chartSelectors', () => { describe('getAnomalyScoreSeries', () => { @@ -98,7 +100,7 @@ describe('chartSelectors', () => { }); describe('getTpmSeries', () => { - const apmTimeseries = { + const apmTimeseries: ApmTimeSeriesResponse = { responseTimes: { avg: [], p95: [], @@ -107,13 +109,14 @@ describe('chartSelectors', () => { tpmBuckets: [ { key: 'HTTP 2xx', + avg: 3.5, dataPoints: [ { x: 0, y: 5 }, - { x: 0, y: 2 }, + { x: 1, y: 2 }, ], }, - { key: 'HTTP 4xx', dataPoints: [{ x: 0, y: 1 }] }, - { key: 'HTTP 5xx', dataPoints: [{ x: 0, y: 0 }] }, + { key: 'HTTP 4xx', avg: 1, dataPoints: [{ x: 0, y: 1 }] }, + { key: 'HTTP 5xx', avg: 0, dataPoints: [{ x: 0, y: 0 }] }, ], overallAvgDuration: 200, }; @@ -125,7 +128,7 @@ describe('chartSelectors', () => { color: successColor, data: [ { x: 0, y: 5 }, - { x: 0, y: 2 }, + { x: 1, y: 2 }, ], legendValue: '3.5 tpm', title: 'HTTP 2xx', @@ -154,7 +157,7 @@ describe('chartSelectors', () => { expect( getTpmSeries({ ...apmTimeseries, - tpmBuckets: [{ key, dataPoints: [{ x: 0, y: 0 }] }], + tpmBuckets: [{ key, avg: 0, dataPoints: [{ x: 0, y: 0 }] }], })[0].color ).toEqual(theme.euiColorSecondary); }); @@ -166,7 +169,7 @@ describe('chartSelectors', () => { expect( getTpmSeries({ ...apmTimeseries, - tpmBuckets: [{ key, dataPoints: [{ x: 0, y: 0 }] }], + tpmBuckets: [{ key, avg: 0, dataPoints: [{ x: 0, y: 0 }] }], })[0].color ).toEqual(theme.euiColorSecondary); }); @@ -178,7 +181,7 @@ describe('chartSelectors', () => { expect( getTpmSeries({ ...apmTimeseries, - tpmBuckets: [{ key, dataPoints: [{ x: 0, y: 0 }] }], + tpmBuckets: [{ key, avg: 0, dataPoints: [{ x: 0, y: 0 }] }], })[0].color ).toEqual(theme.euiColorSecondary); }); @@ -190,7 +193,7 @@ describe('chartSelectors', () => { expect( getTpmSeries({ ...apmTimeseries, - tpmBuckets: [{ key, dataPoints: [{ x: 0, y: 0 }] }], + tpmBuckets: [{ key, avg: 0, dataPoints: [{ x: 0, y: 0 }] }], })[0].color ).toEqual(theme.euiColorSecondary); }); @@ -202,7 +205,7 @@ describe('chartSelectors', () => { expect( getTpmSeries({ ...apmTimeseries, - tpmBuckets: [{ key, dataPoints: [{ x: 0, y: 0 }] }], + tpmBuckets: [{ key, avg: 0, dataPoints: [{ x: 0, y: 0 }] }], })[0].color ).toEqual(theme.euiColorDanger); }); @@ -214,7 +217,7 @@ describe('chartSelectors', () => { expect( getTpmSeries({ ...apmTimeseries, - tpmBuckets: [{ key, dataPoints: [{ x: 0, y: 0 }] }], + tpmBuckets: [{ key, avg: 0, dataPoints: [{ x: 0, y: 0 }] }], })[0].color ).toEqual(theme.euiColorDanger); }); @@ -226,7 +229,7 @@ describe('chartSelectors', () => { expect( getTpmSeries({ ...apmTimeseries, - tpmBuckets: [{ key, dataPoints: [{ x: 0, y: 0 }] }], + tpmBuckets: [{ key, avg: 0, dataPoints: [{ x: 0, y: 0 }] }], })[0].color ).toEqual(theme.euiColorDanger); }); @@ -238,7 +241,7 @@ describe('chartSelectors', () => { expect( getTpmSeries({ ...apmTimeseries, - tpmBuckets: [{ key, dataPoints: [{ x: 0, y: 0 }] }], + tpmBuckets: [{ key, avg: 0, dataPoints: [{ x: 0, y: 0 }] }], })[0].color ).toEqual(theme.euiColorDanger); }); diff --git a/x-pack/plugins/apm/public/selectors/chartSelectors.ts b/x-pack/plugins/apm/public/selectors/chartSelectors.ts index f8aed9dcf6d9f..714d62a703f51 100644 --- a/x-pack/plugins/apm/public/selectors/chartSelectors.ts +++ b/x-pack/plugins/apm/public/selectors/chartSelectors.ts @@ -7,7 +7,6 @@ import theme from '@elastic/eui/dist/eui_theme_light.json'; import { i18n } from '@kbn/i18n'; import { difference, zipObject } from 'lodash'; -import mean from 'lodash.mean'; import { rgba } from 'polished'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { TimeSeriesAPIResponse } from '../../server/lib/transactions/charts'; @@ -72,7 +71,6 @@ export function getResponseTimeSeries({ }: TimeSeriesAPIResponse) { const { overallAvgDuration } = apmTimeseries; const { avg, p95, p99 } = apmTimeseries.responseTimes; - const formattedDuration = asDuration(overallAvgDuration); const series: TimeSeries[] = [ { @@ -80,7 +78,7 @@ export function getResponseTimeSeries({ defaultMessage: 'Avg.', }), data: avg, - legendValue: formattedDuration, + legendValue: asDuration(overallAvgDuration), type: 'linemark', color: theme.euiColorVis1, }, @@ -171,11 +169,10 @@ export function getTpmSeries( } return tpmBuckets.map((bucket) => { - const average = mean(bucket.dataPoints.map((p) => p.y)); return { title: bucket.key, data: bucket.dataPoints, - legendValue: `${asDecimal(average)} ${tpmUnit(transactionType || '')}`, + legendValue: `${asDecimal(bucket.avg)} ${tpmUnit(transactionType || '')}`, type: 'linemark', color: getColor(bucket.key), }; diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/__snapshots__/transform.test.ts.snap b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/__snapshots__/transform.test.ts.snap index e99c32d933b0b..fc9edb496784f 100644 --- a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/__snapshots__/transform.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/__snapshots__/transform.test.ts.snap @@ -985,326 +985,327 @@ Object { }, "tpmBuckets": Array [ Object { + "avg": 112708, "dataPoints": Array [ Object { "x": 1528113600000, - "y": 82230, + "y": 8223, }, Object { "x": 1528124400000, - "y": 81460, + "y": 8146, }, Object { "x": 1528135200000, - "y": 82320, + "y": 8232, }, Object { "x": 1528146000000, - "y": 82485, + "y": 8248.5, }, Object { "x": 1528156800000, - "y": 83995, + "y": 8399.5, }, Object { "x": 1528167600000, - "y": 82805, + "y": 8280.5, }, Object { "x": 1528178400000, - "y": 82155, + "y": 8215.5, }, Object { "x": 1528189200000, - "y": 81915, + "y": 8191.5, }, Object { "x": 1528200000000, - "y": 81475, + "y": 8147.5, }, Object { "x": 1528210800000, - "y": 83510, + "y": 8351, }, Object { "x": 1528221600000, - "y": 82345, + "y": 8234.5, }, Object { "x": 1528232400000, - "y": 82330, + "y": 8233, }, Object { "x": 1528243200000, - "y": 82755, + "y": 8275.5, }, Object { "x": 1528254000000, - "y": 83375, + "y": 8337.5, }, Object { "x": 1528264800000, - "y": 82050, + "y": 8205, }, Object { "x": 1528275600000, - "y": 81235, + "y": 8123.5, }, Object { "x": 1528286400000, - "y": 75725, + "y": 7572.5, }, Object { "x": 1528297200000, - "y": 80890, + "y": 8089, }, Object { "x": 1528308000000, - "y": 82650, + "y": 8265, }, Object { "x": 1528318800000, - "y": 81055, + "y": 8105.5, }, Object { "x": 1528329600000, - "y": 82265, + "y": 8226.5, }, Object { "x": 1528340400000, - "y": 82515, + "y": 8251.5, }, Object { "x": 1528351200000, - "y": 83020, + "y": 8302, }, Object { "x": 1528362000000, - "y": 82610, + "y": 8261, }, Object { "x": 1528372800000, - "y": 80820, + "y": 8082, }, Object { "x": 1528383600000, - "y": 82600, + "y": 8260, }, Object { "x": 1528394400000, - "y": 82670, + "y": 8267, }, Object { "x": 1528405200000, - "y": 81555, + "y": 8155.5, }, Object { "x": 1528416000000, - "y": 83350, + "y": 8335, }, Object { "x": 1528426800000, - "y": 80960, + "y": 8096, }, Object { "x": 1528437600000, - "y": 82895, + "y": 8289.5, }, Object { "x": 1528448400000, - "y": 81650, + "y": 8165, }, Object { "x": 1528459200000, - "y": 82825, + "y": 8282.5, }, Object { "x": 1528470000000, - "y": 82715, + "y": 8271.5, }, Object { "x": 1528480800000, - "y": 82460, + "y": 8246, }, Object { "x": 1528491600000, - "y": 82020, + "y": 8202, }, Object { "x": 1528502400000, - "y": 22640, + "y": 2264, }, Object { "x": 1528513200000, - "y": 22785, + "y": 2278.5, }, Object { "x": 1528524000000, - "y": 22830, + "y": 2283, }, Object { "x": 1528534800000, - "y": 22930, + "y": 2293, }, Object { "x": 1528545600000, - "y": 23360, + "y": 2336, }, Object { "x": 1528556400000, - "y": 23425, + "y": 2342.5, }, Object { "x": 1528567200000, - "y": 22605, + "y": 2260.5, }, Object { "x": 1528578000000, - "y": 23060, + "y": 2306, }, Object { "x": 1528588800000, - "y": 22675, + "y": 2267.5, }, Object { "x": 1528599600000, - "y": 23030, + "y": 2303, }, Object { "x": 1528610400000, - "y": 23070, + "y": 2307, }, Object { "x": 1528621200000, - "y": 22535, + "y": 2253.5, }, Object { "x": 1528632000000, - "y": 23055, + "y": 2305.5, }, Object { "x": 1528642800000, - "y": 22935, + "y": 2293.5, }, Object { "x": 1528653600000, - "y": 22910, + "y": 2291, }, Object { "x": 1528664400000, - "y": 23075, + "y": 2307.5, }, Object { "x": 1528675200000, - "y": 81255, + "y": 8125.5, }, Object { "x": 1528686000000, - "y": 84125, + "y": 8412.5, }, Object { "x": 1528696800000, - "y": 81440, + "y": 8144, }, Object { "x": 1528707600000, - "y": 82460, + "y": 8246, }, Object { "x": 1528718400000, - "y": 82170, + "y": 8217, }, Object { "x": 1528729200000, - "y": 85015, + "y": 8501.5, }, Object { "x": 1528740000000, - "y": 81820, + "y": 8182, }, Object { "x": 1528750800000, - "y": 83225, + "y": 8322.5, }, Object { "x": 1528761600000, - "y": 83475, + "y": 8347.5, }, Object { "x": 1528772400000, - "y": 82490, + "y": 8249, }, Object { "x": 1528783200000, - "y": 82940, + "y": 8294, }, Object { "x": 1528794000000, - "y": 83425, + "y": 8342.5, }, Object { "x": 1528804800000, - "y": 81805, + "y": 8180.5, }, Object { "x": 1528815600000, - "y": 83290, + "y": 8329, }, Object { "x": 1528826400000, - "y": 82535, + "y": 8253.5, }, Object { "x": 1528837200000, - "y": 82090, + "y": 8209, }, Object { "x": 1528848000000, - "y": 82385, + "y": 8238.5, }, Object { "x": 1528858800000, - "y": 83775, + "y": 8377.5, }, Object { "x": 1528869600000, - "y": 82970, + "y": 8297, }, Object { "x": 1528880400000, - "y": 84060, + "y": 8406, }, Object { "x": 1528891200000, - "y": 84315, + "y": 8431.5, }, Object { "x": 1528902000000, - "y": 83275, + "y": 8327.5, }, Object { "x": 1528912800000, - "y": 83615, + "y": 8361.5, }, Object { "x": 1528923600000, - "y": 82885, + "y": 8288.5, }, Object { "x": 1528934400000, - "y": 75625, + "y": 7562.5, }, Object { "x": 1528945200000, - "y": 82160, + "y": 8216, }, Object { "x": 1528956000000, - "y": 82320, + "y": 8232, }, Object { "x": 1528966800000, - "y": 81845, + "y": 8184.5, }, Object { "x": 1528977600000, @@ -1314,6 +1315,7 @@ Object { "key": "HTTP 2xx", }, Object { + "avg": 665, "dataPoints": Array [ Object { "x": 1528113600000, @@ -1381,11 +1383,11 @@ Object { }, Object { "x": 1528286400000, - "y": 20205, + "y": 2020.5, }, Object { "x": 1528297200000, - "y": 2270, + "y": 227, }, Object { "x": 1528308000000, @@ -1621,7 +1623,7 @@ Object { }, Object { "x": 1528934400000, - "y": 10775, + "y": 1077.5, }, Object { "x": 1528945200000, @@ -1643,326 +1645,327 @@ Object { "key": "HTTP 3xx", }, Object { + "avg": 8190.7, "dataPoints": Array [ Object { "x": 1528113600000, - "y": 5930, + "y": 593, }, Object { "x": 1528124400000, - "y": 6065, + "y": 606.5, }, Object { "x": 1528135200000, - "y": 6025, + "y": 602.5, }, Object { "x": 1528146000000, - "y": 5810, + "y": 581, }, Object { "x": 1528156800000, - "y": 6190, + "y": 619, }, Object { "x": 1528167600000, - "y": 5955, + "y": 595.5, }, Object { "x": 1528178400000, - "y": 6370, + "y": 637, }, Object { "x": 1528189200000, - "y": 6170, + "y": 617, }, Object { "x": 1528200000000, - "y": 5820, + "y": 582, }, Object { "x": 1528210800000, - "y": 6165, + "y": 616.5, }, Object { "x": 1528221600000, - "y": 6115, + "y": 611.5, }, Object { "x": 1528232400000, - "y": 6080, + "y": 608, }, Object { "x": 1528243200000, - "y": 6000, + "y": 600, }, Object { "x": 1528254000000, - "y": 6185, + "y": 618.5, }, Object { "x": 1528264800000, - "y": 6155, + "y": 615.5, }, Object { "x": 1528275600000, - "y": 5910, + "y": 591, }, Object { "x": 1528286400000, - "y": 5625, + "y": 562.5, }, Object { "x": 1528297200000, - "y": 6215, + "y": 621.5, }, Object { "x": 1528308000000, - "y": 6235, + "y": 623.5, }, Object { "x": 1528318800000, - "y": 5815, + "y": 581.5, }, Object { "x": 1528329600000, - "y": 6100, + "y": 610, }, Object { "x": 1528340400000, - "y": 6010, + "y": 601, }, Object { "x": 1528351200000, - "y": 5960, + "y": 596, }, Object { "x": 1528362000000, - "y": 6240, + "y": 624, }, Object { "x": 1528372800000, - "y": 5945, + "y": 594.5, }, Object { "x": 1528383600000, - "y": 6150, + "y": 615, }, Object { "x": 1528394400000, - "y": 6030, + "y": 603, }, Object { "x": 1528405200000, - "y": 5950, + "y": 595, }, Object { "x": 1528416000000, - "y": 6160, + "y": 616, }, Object { "x": 1528426800000, - "y": 5855, + "y": 585.5, }, Object { "x": 1528437600000, - "y": 6160, + "y": 616, }, Object { "x": 1528448400000, - "y": 6265, + "y": 626.5, }, Object { "x": 1528459200000, - "y": 6250, + "y": 625, }, Object { "x": 1528470000000, - "y": 5835, + "y": 583.5, }, Object { "x": 1528480800000, - "y": 6290, + "y": 629, }, Object { "x": 1528491600000, - "y": 5740, + "y": 574, }, Object { "x": 1528502400000, - "y": 1420, + "y": 142, }, Object { "x": 1528513200000, - "y": 1200, + "y": 120, }, Object { "x": 1528524000000, - "y": 1365, + "y": 136.5, }, Object { "x": 1528534800000, - "y": 1475, + "y": 147.5, }, Object { "x": 1528545600000, - "y": 1405, + "y": 140.5, }, Object { "x": 1528556400000, - "y": 1500, + "y": 150, }, Object { "x": 1528567200000, - "y": 1320, + "y": 132, }, Object { "x": 1528578000000, - "y": 1300, + "y": 130, }, Object { "x": 1528588800000, - "y": 1395, + "y": 139.5, }, Object { "x": 1528599600000, - "y": 1295, + "y": 129.5, }, Object { "x": 1528610400000, - "y": 1455, + "y": 145.5, }, Object { "x": 1528621200000, - "y": 1240, + "y": 124, }, Object { "x": 1528632000000, - "y": 1555, + "y": 155.5, }, Object { "x": 1528642800000, - "y": 1385, + "y": 138.5, }, Object { "x": 1528653600000, - "y": 1395, + "y": 139.5, }, Object { "x": 1528664400000, - "y": 1375, + "y": 137.5, }, Object { "x": 1528675200000, - "y": 5835, + "y": 583.5, }, Object { "x": 1528686000000, - "y": 6350, + "y": 635, }, Object { "x": 1528696800000, - "y": 5815, + "y": 581.5, }, Object { "x": 1528707600000, - "y": 5775, + "y": 577.5, }, Object { "x": 1528718400000, - "y": 6085, + "y": 608.5, }, Object { "x": 1528729200000, - "y": 6135, + "y": 613.5, }, Object { "x": 1528740000000, - "y": 5970, + "y": 597, }, Object { "x": 1528750800000, - "y": 5765, + "y": 576.5, }, Object { "x": 1528761600000, - "y": 6055, + "y": 605.5, }, Object { "x": 1528772400000, - "y": 6015, + "y": 601.5, }, Object { "x": 1528783200000, - "y": 6345, + "y": 634.5, }, Object { "x": 1528794000000, - "y": 5985, + "y": 598.5, }, Object { "x": 1528804800000, - "y": 5920, + "y": 592, }, Object { "x": 1528815600000, - "y": 5880, + "y": 588, }, Object { "x": 1528826400000, - "y": 5810, + "y": 581, }, Object { "x": 1528837200000, - "y": 6350, + "y": 635, }, Object { "x": 1528848000000, - "y": 6120, + "y": 612, }, Object { "x": 1528858800000, - "y": 6275, + "y": 627.5, }, Object { "x": 1528869600000, - "y": 6035, + "y": 603.5, }, Object { "x": 1528880400000, - "y": 6030, + "y": 603, }, Object { "x": 1528891200000, - "y": 6270, + "y": 627, }, Object { "x": 1528902000000, - "y": 6080, + "y": 608, }, Object { "x": 1528912800000, - "y": 6315, + "y": 631.5, }, Object { "x": 1528923600000, - "y": 6385, + "y": 638.5, }, Object { "x": 1528934400000, - "y": 5915, + "y": 591.5, }, Object { "x": 1528945200000, - "y": 6105, + "y": 610.5, }, Object { "x": 1528956000000, - "y": 5990, + "y": 599, }, Object { "x": 1528966800000, - "y": 6070, + "y": 607, }, Object { "x": 1528977600000, @@ -1972,326 +1975,327 @@ Object { "key": "HTTP 4xx", }, Object { + "avg": 8203.6, "dataPoints": Array [ Object { "x": 1528113600000, - "y": 6045, + "y": 604.5, }, Object { "x": 1528124400000, - "y": 6015, + "y": 601.5, }, Object { "x": 1528135200000, - "y": 5980, + "y": 598, }, Object { "x": 1528146000000, - "y": 6150, + "y": 615, }, Object { "x": 1528156800000, - "y": 6165, + "y": 616.5, }, Object { "x": 1528167600000, - "y": 6360, + "y": 636, }, Object { "x": 1528178400000, - "y": 6090, + "y": 609, }, Object { "x": 1528189200000, - "y": 6085, + "y": 608.5, }, Object { "x": 1528200000000, - "y": 6175, + "y": 617.5, }, Object { "x": 1528210800000, - "y": 6245, + "y": 624.5, }, Object { "x": 1528221600000, - "y": 5790, + "y": 579, }, Object { "x": 1528232400000, - "y": 6075, + "y": 607.5, }, Object { "x": 1528243200000, - "y": 5955, + "y": 595.5, }, Object { "x": 1528254000000, - "y": 6175, + "y": 617.5, }, Object { "x": 1528264800000, - "y": 6060, + "y": 606, }, Object { "x": 1528275600000, - "y": 5900, + "y": 590, }, Object { "x": 1528286400000, - "y": 5455, + "y": 545.5, }, Object { "x": 1528297200000, - "y": 5880, + "y": 588, }, Object { "x": 1528308000000, - "y": 6215, + "y": 621.5, }, Object { "x": 1528318800000, - "y": 6040, + "y": 604, }, Object { "x": 1528329600000, - "y": 6010, + "y": 601, }, Object { "x": 1528340400000, - "y": 6440, + "y": 644, }, Object { "x": 1528351200000, - "y": 6205, + "y": 620.5, }, Object { "x": 1528362000000, - "y": 6075, + "y": 607.5, }, Object { "x": 1528372800000, - "y": 5760, + "y": 576, }, Object { "x": 1528383600000, - "y": 6205, + "y": 620.5, }, Object { "x": 1528394400000, - "y": 5885, + "y": 588.5, }, Object { "x": 1528405200000, - "y": 6215, + "y": 621.5, }, Object { "x": 1528416000000, - "y": 6275, + "y": 627.5, }, Object { "x": 1528426800000, - "y": 5945, + "y": 594.5, }, Object { "x": 1528437600000, - "y": 5915, + "y": 591.5, }, Object { "x": 1528448400000, - "y": 6075, + "y": 607.5, }, Object { "x": 1528459200000, - "y": 6410, + "y": 641, }, Object { "x": 1528470000000, - "y": 5885, + "y": 588.5, }, Object { "x": 1528480800000, - "y": 5995, + "y": 599.5, }, Object { "x": 1528491600000, - "y": 6170, + "y": 617, }, Object { "x": 1528502400000, - "y": 1420, + "y": 142, }, Object { "x": 1528513200000, - "y": 1535, + "y": 153.5, }, Object { "x": 1528524000000, - "y": 1415, + "y": 141.5, }, Object { "x": 1528534800000, - "y": 1515, + "y": 151.5, }, Object { "x": 1528545600000, - "y": 1630, + "y": 163, }, Object { "x": 1528556400000, - "y": 1345, + "y": 134.5, }, Object { "x": 1528567200000, - "y": 1485, + "y": 148.5, }, Object { "x": 1528578000000, - "y": 1390, + "y": 139, }, Object { "x": 1528588800000, - "y": 1445, + "y": 144.5, }, Object { "x": 1528599600000, - "y": 1360, + "y": 136, }, Object { "x": 1528610400000, - "y": 1395, + "y": 139.5, }, Object { "x": 1528621200000, - "y": 1190, + "y": 119, }, Object { "x": 1528632000000, - "y": 1440, + "y": 144, }, Object { "x": 1528642800000, - "y": 1290, + "y": 129, }, Object { "x": 1528653600000, - "y": 1320, + "y": 132, }, Object { "x": 1528664400000, - "y": 1480, + "y": 148, }, Object { "x": 1528675200000, - "y": 6065, + "y": 606.5, }, Object { "x": 1528686000000, - "y": 6270, + "y": 627, }, Object { "x": 1528696800000, - "y": 5675, + "y": 567.5, }, Object { "x": 1528707600000, - "y": 6200, + "y": 620, }, Object { "x": 1528718400000, - "y": 6075, + "y": 607.5, }, Object { "x": 1528729200000, - "y": 6195, + "y": 619.5, }, Object { "x": 1528740000000, - "y": 6045, + "y": 604.5, }, Object { "x": 1528750800000, - "y": 6040, + "y": 604, }, Object { "x": 1528761600000, - "y": 5880, + "y": 588, }, Object { "x": 1528772400000, - "y": 6035, + "y": 603.5, }, Object { "x": 1528783200000, - "y": 5990, + "y": 599, }, Object { "x": 1528794000000, - "y": 5825, + "y": 582.5, }, Object { "x": 1528804800000, - "y": 5940, + "y": 594, }, Object { "x": 1528815600000, - "y": 6225, + "y": 622.5, }, Object { "x": 1528826400000, - "y": 6190, + "y": 619, }, Object { "x": 1528837200000, - "y": 6415, + "y": 641.5, }, Object { "x": 1528848000000, - "y": 5990, + "y": 599, }, Object { "x": 1528858800000, - "y": 5860, + "y": 586, }, Object { "x": 1528869600000, - "y": 6145, + "y": 614.5, }, Object { "x": 1528880400000, - "y": 6195, + "y": 619.5, }, Object { "x": 1528891200000, - "y": 6155, + "y": 615.5, }, Object { "x": 1528902000000, - "y": 6240, + "y": 624, }, Object { "x": 1528912800000, - "y": 6100, + "y": 610, }, Object { "x": 1528923600000, - "y": 6120, + "y": 612, }, Object { "x": 1528934400000, - "y": 5440, + "y": 544, }, Object { "x": 1528945200000, - "y": 6175, + "y": 617.5, }, Object { "x": 1528956000000, - "y": 5805, + "y": 580.5, }, Object { "x": 1528966800000, - "y": 5915, + "y": 591.5, }, Object { "x": 1528977600000, @@ -2301,6 +2305,7 @@ Object { "key": "HTTP 5xx", }, Object { + "avg": 0, "dataPoints": Array [], "key": "A Custom Bucket (that should be last)", }, diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts index 0f1eca5853bb9..8a0fe1a57736f 100644 --- a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts @@ -21,10 +21,12 @@ export async function getApmTimeseriesData(options: { }) { const { start, end } = options.setup; const { bucketSize } = getBucketSize(start, end, 'auto'); + const durationAsMinutes = (end - start) / 1000 / 60; const timeseriesResponse = await timeseriesFetcher(options); return timeseriesTransformer({ timeseriesResponse, bucketSize, + durationAsMinutes, }); } diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.test.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.test.ts index abce5dbe18059..d0d0875be388f 100644 --- a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.test.ts +++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.test.ts @@ -16,7 +16,8 @@ describe('timeseriesTransformer', () => { beforeEach(async () => { res = await timeseriesTransformer({ timeseriesResponse, - bucketSize: 12, + bucketSize: 120, + durationAsMinutes: 10, }); }); @@ -79,7 +80,7 @@ describe('getTpmBuckets', () => { { key_as_string: '', key: 1, - doc_count: 500, + doc_count: 100, }, { key_as_string: '', @@ -95,23 +96,31 @@ describe('getTpmBuckets', () => { }, }, ]; - const bucketSize = 10; - expect(getTpmBuckets(buckets as any, bucketSize)).toEqual([ + + expect( + getTpmBuckets({ + transactionResultBuckets: buckets, + bucketSize: 120, + durationAsMinutes: 10, + }) + ).toEqual([ { + avg: 90, dataPoints: [ { x: 0, y: 0 }, - { x: 1, y: 1200 }, - { x: 2, y: 1800 }, - { x: 3, y: 2400 }, + { x: 1, y: 100 }, + { x: 2, y: 150 }, + { x: 3, y: 200 }, ], key: 'HTTP 4xx', }, { + avg: 50, dataPoints: [ { x: 0, y: 0 }, - { x: 1, y: 3000 }, - { x: 2, y: 600 }, - { x: 3, y: 1800 }, + { x: 1, y: 50 }, + { x: 2, y: 50 }, + { x: 3, y: 150 }, ], key: 'HTTP 5xx', }, diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.ts index bbdd0bd3e054a..f68c069253b99 100644 --- a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.ts +++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { isNumber, round, sortBy } from 'lodash'; +import { isNumber, sortBy } from 'lodash'; import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; import { Coordinate } from '../../../../../typings/timeseries'; import { ESResponse } from './fetcher'; @@ -14,16 +14,22 @@ export type ApmTimeSeriesResponse = ReturnType; export function timeseriesTransformer({ timeseriesResponse, bucketSize, + durationAsMinutes, }: { timeseriesResponse: ESResponse; bucketSize: number; + durationAsMinutes: number; }) { const aggs = timeseriesResponse.aggregations; const overallAvgDuration = aggs?.overall_avg_duration.value || null; const responseTimeBuckets = aggs?.response_times.buckets || []; const { avg, p95, p99 } = getResponseTime(responseTimeBuckets); const transactionResultBuckets = aggs?.transaction_results.buckets || []; - const tpmBuckets = getTpmBuckets(transactionResultBuckets, bucketSize); + const tpmBuckets = getTpmBuckets({ + transactionResultBuckets, + bucketSize, + durationAsMinutes, + }); return { responseTimes: { @@ -36,18 +42,28 @@ export function timeseriesTransformer({ }; } -export function getTpmBuckets( - transactionResultBuckets: Required< - ESResponse - >['aggregations']['transaction_results']['buckets'] = [], - bucketSize: number -) { +type TransactionResultBuckets = Required< + ESResponse +>['aggregations']['transaction_results']['buckets']; + +export function getTpmBuckets({ + transactionResultBuckets = [], + bucketSize, + durationAsMinutes, +}: { + transactionResultBuckets: TransactionResultBuckets; + bucketSize: number; + durationAsMinutes: number; +}) { const buckets = transactionResultBuckets.map( ({ key: resultKey, timeseries }) => { const dataPoints = timeseries.buckets.map((bucket) => { + // calculate request/minute. Avoid up-scaling numbers if bucketSize is below 60s (1 minute). + // Eg. 1 request during a 10 second window should be displayed as "1 rpm" instead of "6 rpm". + const tmpValue = bucket.doc_count * (60 / Math.max(60, bucketSize)); return { x: bucket.key, - y: round(bucket.doc_count * (60 / bucketSize), 1), + y: tmpValue, }; }); @@ -55,7 +71,14 @@ export function getTpmBuckets( const key = resultKey === '' ? NOT_AVAILABLE_LABEL : (resultKey as string); - return { key, dataPoints }; + const docCountTotal = timeseries.buckets + .map((bucket) => bucket.doc_count) + .reduce((a, b) => a + b, 0); + + // calculate request/minute + const avg = docCountTotal / durationAsMinutes; + + return { key, dataPoints, avg }; } ); @@ -65,11 +88,11 @@ export function getTpmBuckets( ); } -function getResponseTime( - responseTimeBuckets: Required< - ESResponse - >['aggregations']['response_times']['buckets'] = [] -) { +type ResponseTimeBuckets = Required< + ESResponse +>['aggregations']['response_times']['buckets']; + +function getResponseTime(responseTimeBuckets: ResponseTimeBuckets = []) { return responseTimeBuckets.reduce( (acc, bucket) => { const { '95.0': p95, '99.0': p99 } = bucket.pct.values; diff --git a/x-pack/plugins/canvas/common/lib/autocomplete.ts b/x-pack/plugins/canvas/common/lib/autocomplete.ts index 0ab549bd14e83..c97879de2137e 100644 --- a/x-pack/plugins/canvas/common/lib/autocomplete.ts +++ b/x-pack/plugins/canvas/common/lib/autocomplete.ts @@ -14,7 +14,7 @@ import { ExpressionFunction, ExpressionFunctionParameter, getByAlias, -} from '../../../../../src/plugins/expressions'; +} from '../../../../../src/plugins/expressions/common'; const MARKER = 'CANVAS_SUGGESTION_MARKER'; diff --git a/x-pack/plugins/data_enhanced/kibana.json b/x-pack/plugins/data_enhanced/kibana.json index 443bb63a27799..1be55d2b7a635 100644 --- a/x-pack/plugins/data_enhanced/kibana.json +++ b/x-pack/plugins/data_enhanced/kibana.json @@ -8,6 +8,7 @@ "requiredPlugins": [ "data" ], + "optionalPlugins": ["kibanaReact", "kibanaUtils"], "server": true, "ui": true } diff --git a/x-pack/plugins/features/kibana.json b/x-pack/plugins/features/kibana.json index 1cab1821b1bf5..92fdd08e93478 100644 --- a/x-pack/plugins/features/kibana.json +++ b/x-pack/plugins/features/kibana.json @@ -6,5 +6,6 @@ "optionalPlugins": ["visTypeTimelion"], "configPath": ["xpack", "features"], "server": true, - "ui": true + "ui": true, + "extraPublicDirs": ["common"] } diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/min_age_input.js b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/min_age_input.js index 28bc8671f29e2..cd690c768a326 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/min_age_input.js +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/min_age_input.js @@ -188,7 +188,7 @@ export const MinAgeInput = (props) => { errors={errors} helpText={ ({ type: AGENT_CONFIG_SAVED_OBJECT_TYPE, - filter: `${AGENT_CONFIG_SAVED_OBJECT_TYPE}.attributes.is_default:true`, + searchFields: ['is_default'], + search: 'true', }); if (configs.total === 0) { diff --git a/x-pack/plugins/ingest_manager/server/services/agents/status.ts b/x-pack/plugins/ingest_manager/server/services/agents/status.ts index 63388db890ea5..0efb202eff532 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/status.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/status.ts @@ -56,9 +56,8 @@ export async function getAgentStatusForConfig( async function getEventsCount(soClient: SavedObjectsClientContract, configId?: string) { const { total } = await soClient.find({ type: AGENT_EVENT_SAVED_OBJECT_TYPE, - filter: configId - ? `${AGENT_EVENT_SAVED_OBJECT_TYPE}.attributes.config_id:"${configId}"` - : undefined, + searchFields: ['config_id'], + search: configId, perPage: 0, page: 1, sortField: 'timestamp', diff --git a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_create.test.tsx b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_create.test.tsx index 6acb6369e2e90..4318062491df8 100644 --- a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_create.test.tsx +++ b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_create.test.tsx @@ -68,21 +68,6 @@ describe('', () => { expect(exists('versionField')).toBe(true); }); - test('should toggle the on-failure processors editor', async () => { - const { actions, component, exists } = testBed; - - // On-failure editor should be hidden by default - expect(exists('onFailureEditor')).toBe(false); - - await act(async () => { - actions.toggleOnFailureSwitch(); - await nextTick(); - component.update(); - }); - - expect(exists('onFailureEditor')).toBe(true); - }); - test('should show the request flyout', async () => { const { actions, component, find, exists } = testBed; diff --git a/x-pack/plugins/ingest_pipelines/common/types.ts b/x-pack/plugins/ingest_pipelines/common/types.ts index 8d77359a7c3c5..ad6d1cc8aa928 100644 --- a/x-pack/plugins/ingest_pipelines/common/types.ts +++ b/x-pack/plugins/ingest_pipelines/common/types.ts @@ -4,10 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -interface Processor { - [key: string]: { - [key: string]: unknown; - }; +export interface ESProcessorConfig { + on_failure?: Processor[]; + ignore_failure?: boolean; + if?: string; + tag?: string; + [key: string]: any; +} + +export interface Processor { + [typeName: string]: ESProcessorConfig; } export interface Pipeline { diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form.tsx index 73bc1cfaa2cf5..ec065a74abca0 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState } from 'react'; +import React, { useState, useCallback, useRef } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; @@ -16,6 +16,11 @@ import { PipelineTestFlyout } from './pipeline_test_flyout'; import { PipelineFormFields } from './pipeline_form_fields'; import { PipelineFormError } from './pipeline_form_error'; import { pipelineFormSchema } from './schema'; +import { + OnUpdateHandlerArg, + OnUpdateHandler, + SerializeResult, +} from '../pipeline_processors_editor'; export interface PipelineFormProps { onSave: (pipeline: Pipeline) => void; @@ -30,8 +35,8 @@ export const PipelineForm: React.FunctionComponent = ({ defaultValue = { name: '', description: '', - processors: '', - on_failure: '', + processors: [], + on_failure: [], version: '', }, onSave, @@ -44,10 +49,25 @@ export const PipelineForm: React.FunctionComponent = ({ const [isTestingPipeline, setIsTestingPipeline] = useState(false); + const processorStateRef = useRef(); + const handleSave: FormConfig['onSubmit'] = async (formData, isValid) => { - if (isValid) { - onSave(formData as Pipeline); + let override: SerializeResult | undefined; + + if (!isValid) { + return; + } + + if (processorStateRef.current) { + const processorsState = processorStateRef.current; + if (await processorsState.validate()) { + override = processorsState.getData(); + } else { + return; + } } + + onSave({ ...formData, ...(override || {}) } as Pipeline); }; const handleTestPipelineClick = () => { @@ -60,6 +80,10 @@ export const PipelineForm: React.FunctionComponent = ({ onSubmit: handleSave, }); + const onEditorFlyoutOpen = useCallback(() => { + setIsRequestVisible(false); + }, [setIsRequestVisible]); + const saveButtonLabel = isSaving ? ( = ({ /> ); + const onProcessorsChangeHandler = useCallback( + (arg) => (processorStateRef.current = arg), + [] + ); + return ( <>
= ({ {/* All form fields */} @@ -147,6 +179,9 @@ export const PipelineForm: React.FunctionComponent = ({ {/* ES request flyout */} {isRequestVisible ? ( + processorStateRef.current?.getData() || { processors: [], on_failure: [] } + } closeFlyout={() => setIsRequestVisible((prevIsRequestVisible) => !prevIsRequestVisible)} /> ) : null} @@ -154,6 +189,9 @@ export const PipelineForm: React.FunctionComponent = ({ {/* Test pipeline flyout */} {isTestingPipeline ? ( + processorStateRef.current?.getData() || { processors: [], on_failure: [] } + } closeFlyout={() => { setIsTestingPipeline((prevIsTestingPipeline) => !prevIsTestingPipeline); }} diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx index 9fb5ab55a34ce..a0e7c8fd8bcd7 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx @@ -6,22 +6,22 @@ import React, { useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; -import { EuiButton, EuiSpacer, EuiSwitch, EuiLink } from '@elastic/eui'; +import { EuiSpacer, EuiSwitch } from '@elastic/eui'; -import { - getUseField, - getFormRow, - Field, - JsonEditorField, - useKibana, -} from '../../../shared_imports'; +import { Processor } from '../../../../common/types'; +import { FormDataProvider } from '../../../shared_imports'; +import { PipelineProcessorsEditor, OnUpdateHandler } from '../pipeline_processors_editor'; + +import { getUseField, getFormRow, Field, useKibana } from '../../../shared_imports'; interface Props { + initialProcessors: Processor[]; + initialOnFailureProcessors?: Processor[]; + onProcessorsUpdate: OnUpdateHandler; hasVersion: boolean; - hasOnFailure: boolean; isTestButtonDisabled: boolean; onTestPipelineClick: () => void; + onEditorFlyoutOpen: () => void; isEditing?: boolean; } @@ -29,16 +29,18 @@ const UseField = getUseField({ component: Field }); const FormRow = getFormRow({ titleTag: 'h3' }); export const PipelineFormFields: React.FunctionComponent = ({ + initialProcessors, + initialOnFailureProcessors, + onProcessorsUpdate, isEditing, hasVersion, - hasOnFailure, isTestButtonDisabled, onTestPipelineClick, + onEditorFlyoutOpen, }) => { const { services } = useKibana(); const [isVersionVisible, setIsVersionVisible] = useState(hasVersion); - const [isOnFailureEditorVisible, setIsOnFailureEditorVisible] = useState(hasOnFailure); return ( <> @@ -110,127 +112,32 @@ export const PipelineFormFields: React.FunctionComponent = ({ /> - {/* Processors field */} - - } - description={ - <> - - {i18n.translate('xpack.ingestPipelines.form.processorsDocumentionLink', { - defaultMessage: 'Learn more', - })} - - ), - }} - /> - - + {/* Pipeline Processors Editor */} + + {({ processors, on_failure: onFailure }) => { + const processorProp = + typeof processors === 'string' && processors + ? JSON.parse(processors) + : initialProcessors ?? []; - - - - - } - > - - + const onFailureProp = + typeof onFailure === 'string' && onFailure + ? JSON.parse(onFailure) + : initialOnFailureProcessors ?? []; - {/* On-failure field */} - - } - description={ - <> - - {i18n.translate('xpack.ingestPipelines.form.onFailureDocumentionLink', { - defaultMessage: 'Learn more', - })} - - ), - }} - /> - - - } - checked={isOnFailureEditorVisible} - onChange={(e) => setIsOnFailureEditorVisible(e.target.checked)} - data-test-subj="onFailureToggle" + return ( + - - } - > - {isOnFailureEditorVisible ? ( - - ) : ( - // requires children or a field - // For now, we return an empty
if the editor is not visible -
- )} - + ); + }} + ); }; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_request_flyout/pipeline_request_flyout_provider.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_request_flyout/pipeline_request_flyout_provider.tsx index 6dcedca6085af..dd2439433fc41 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_request_flyout/pipeline_request_flyout_provider.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_request_flyout/pipeline_request_flyout_provider.tsx @@ -4,13 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, FunctionComponent } from 'react'; import { Pipeline } from '../../../../../common/types'; import { useFormContext } from '../../../../shared_imports'; + +import { ReadProcessorsFunction } from '../types'; + import { PipelineRequestFlyout } from './pipeline_request_flyout'; -export const PipelineRequestFlyoutProvider = ({ closeFlyout }: { closeFlyout: () => void }) => { +interface Props { + closeFlyout: () => void; + readProcessors: ReadProcessorsFunction; +} + +export const PipelineRequestFlyoutProvider: FunctionComponent = ({ + closeFlyout, + readProcessors, +}) => { const form = useFormContext(); const [formData, setFormData] = useState({} as Pipeline); @@ -25,5 +36,10 @@ export const PipelineRequestFlyoutProvider = ({ closeFlyout }: { closeFlyout: () return subscription.unsubscribe; }, [form]); - return ; + return ( + + ); }; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/pipeline_test_flyout_provider.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/pipeline_test_flyout_provider.tsx index 351478394595a..7f91672d64df4 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/pipeline_test_flyout_provider.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/pipeline_test_flyout_provider.tsx @@ -8,11 +8,19 @@ import React, { useState, useEffect } from 'react'; import { Pipeline } from '../../../../../common/types'; import { useFormContext } from '../../../../shared_imports'; + +import { ReadProcessorsFunction } from '../types'; + import { PipelineTestFlyout, PipelineTestFlyoutProps } from './pipeline_test_flyout'; -type Props = Omit; +interface Props extends Omit { + readProcessors: ReadProcessorsFunction; +} -export const PipelineTestFlyoutProvider: React.FunctionComponent = ({ closeFlyout }) => { +export const PipelineTestFlyoutProvider: React.FunctionComponent = ({ + closeFlyout, + readProcessors, +}) => { const form = useFormContext(); const [formData, setFormData] = useState({} as Pipeline); const [isFormDataValid, setIsFormDataValid] = useState(false); @@ -31,7 +39,7 @@ export const PipelineTestFlyoutProvider: React.FunctionComponent = ({ clo return ( diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/types.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/types.ts new file mode 100644 index 0000000000000..bd74f09546ff4 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/types.ts @@ -0,0 +1,9 @@ +/* + * 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 { Pipeline } from '../../../../common/types'; + +export type ReadProcessorsFunction = () => Pick; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.helpers.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.helpers.ts new file mode 100644 index 0000000000000..acd61a9bbd01e --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.helpers.ts @@ -0,0 +1,36 @@ +/* + * 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 { registerTestBed, TestBed } from '../../../../../../../test_utils'; +import { PipelineProcessorsEditor, Props } from '../pipeline_processors_editor.container'; + +const testBedSetup = registerTestBed(PipelineProcessorsEditor, { + doMountAsync: false, +}); + +export interface SetupResult extends TestBed { + actions: { + toggleOnFailure: () => void; + }; +} + +export const setup = async (props: Props): Promise => { + const testBed = await testBedSetup(props); + const toggleOnFailure = () => { + const { find } = testBed; + find('pipelineEditorOnFailureToggle').simulate('click'); + }; + + return { + ...testBed, + actions: { toggleOnFailure }, + }; +}; + +type TestSubject = + | 'pipelineEditorDoneButton' + | 'pipelineEditorOnFailureToggle' + | 'pipelineEditorOnFailureTree'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx new file mode 100644 index 0000000000000..758d6f5e620ce --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx @@ -0,0 +1,67 @@ +/* + * 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 { setup } from './pipeline_processors_editor.helpers'; +import { Pipeline } from '../../../../../common/types'; + +const testProcessors: Pick = { + processors: [ + { + script: { + source: 'ctx._type = null', + }, + }, + { + gsub: { + field: '_index', + pattern: '(.monitoring-\\w+-)6(-.+)', + replacement: '$17$2', + }, + }, + ], +}; + +describe('Pipeline Editor', () => { + it('provides the same data out it got in if nothing changes', async () => { + const onUpdate = jest.fn(); + + await setup({ + value: { + ...testProcessors, + }, + onFlyoutOpen: jest.fn(), + onUpdate, + isTestButtonDisabled: false, + onTestPipelineClick: jest.fn(), + learnMoreAboutProcessorsUrl: 'test', + learnMoreAboutOnFailureProcessorsUrl: 'test', + }); + + const { + calls: [[arg]], + } = onUpdate.mock; + + expect(arg.getData()).toEqual(testProcessors); + }); + + it('toggles the on-failure processors', async () => { + const { actions, exists } = await setup({ + value: { + ...testProcessors, + }, + onFlyoutOpen: jest.fn(), + onUpdate: jest.fn(), + isTestButtonDisabled: false, + onTestPipelineClick: jest.fn(), + learnMoreAboutProcessorsUrl: 'test', + learnMoreAboutOnFailureProcessorsUrl: 'test', + }); + + expect(exists('pipelineEditorOnFailureTree')).toBe(false); + actions.toggleOnFailure(); + expect(exists('pipelineEditorOnFailureTree')).toBe(true); + }); +}); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/add_processor_button.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/add_processor_button.tsx new file mode 100644 index 0000000000000..5f9bf87ceca1e --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/add_processor_button.tsx @@ -0,0 +1,32 @@ +/* + * 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, { FunctionComponent } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiButtonEmpty } from '@elastic/eui'; +import { usePipelineProcessorsContext } from '../context'; + +export interface Props { + onClick: () => void; +} + +export const AddProcessorButton: FunctionComponent = ({ onClick }) => { + const { + state: { editor }, + } = usePipelineProcessorsContext(); + return ( + + {i18n.translate('xpack.ingestPipelines.pipelineEditor.addProcessorButtonLabel', { + defaultMessage: 'Add a processor', + })} + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/index.ts new file mode 100644 index 0000000000000..cb5d5a10e9f42 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/index.ts @@ -0,0 +1,19 @@ +/* + * 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 { SettingsFormFlyout, OnSubmitHandler } from './settings_form_flyout'; + +export { ProcessorSettingsForm, ProcessorSettingsFromOnSubmitArg } from './processor_settings_form'; + +export { ProcessorsTree, ProcessorInfo, OnActionHandler } from './processors_tree'; + +export { PipelineProcessorsEditorItem } from './pipeline_processors_editor_item/pipeline_processors_editor_item'; + +export { ProcessorRemoveModal } from './processor_remove_modal'; + +export { ProcessorsTitleAndTestButton } from './processors_title_and_test_button'; + +export { OnFailureProcessorsTitle } from './on_failure_processors_title'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/on_failure_processors_title.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/on_failure_processors_title.tsx new file mode 100644 index 0000000000000..1c8edac7cfd64 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/on_failure_processors_title.tsx @@ -0,0 +1,52 @@ +/* + * 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 { EuiFlexGroup, EuiFlexItem, EuiLink, EuiText, EuiTitle } from '@elastic/eui'; +import React, { FunctionComponent } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { usePipelineProcessorsContext } from '../context'; + +export const OnFailureProcessorsTitle: FunctionComponent = () => { + const { links } = usePipelineProcessorsContext(); + return ( + + + +

+ +

+
+ + + {i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.onFailureProcessorsDocumentationLink', + { + defaultMessage: 'Learn more.', + } + )} + + ), + }} + /> + +
+
+ ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/context_menu.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/context_menu.tsx new file mode 100644 index 0000000000000..bc7d6fdcff357 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/context_menu.tsx @@ -0,0 +1,84 @@ +/* + * 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, { FunctionComponent, useState } from 'react'; + +import { EuiContextMenuItem, EuiContextMenuPanel, EuiPopover, EuiButtonIcon } from '@elastic/eui'; + +import { editorItemMessages } from './messages'; + +interface Props { + disabled: boolean; + showAddOnFailure: boolean; + onDuplicate: () => void; + onDelete: () => void; + onAddOnFailure: () => void; +} + +export const ContextMenu: FunctionComponent = ({ + showAddOnFailure, + onDuplicate, + onAddOnFailure, + onDelete, + disabled, +}) => { + const [isOpen, setIsOpen] = useState(false); + + const contextMenuItems = [ + { + setIsOpen(false); + onDuplicate(); + }} + > + {editorItemMessages.duplicateButtonLabel} + , + showAddOnFailure ? ( + { + setIsOpen(false); + onAddOnFailure(); + }} + > + {editorItemMessages.addOnFailureButtonLabel} + + ) : undefined, + { + setIsOpen(false); + onDelete(); + }} + > + {editorItemMessages.deleteButtonLabel} + , + ].filter(Boolean) as JSX.Element[]; + + return ( + setIsOpen(false)} + button={ + setIsOpen((v) => !v)} + iconType="boxesHorizontal" + aria-label={editorItemMessages.moreButtonAriaLabel} + /> + } + > + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/index.ts new file mode 100644 index 0000000000000..02bafdb326024 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { PipelineProcessorsEditorItem, Handlers } from './pipeline_processors_editor_item'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/inline_text_input.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/inline_text_input.tsx new file mode 100644 index 0000000000000..e0b67bc907ca9 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/inline_text_input.tsx @@ -0,0 +1,75 @@ +/* + * 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, { FunctionComponent, useState, useEffect, useCallback } from 'react'; +import { EuiFieldText, EuiText, keyCodes } from '@elastic/eui'; + +export interface Props { + placeholder: string; + ariaLabel: string; + onChange: (value: string) => void; + text?: string; +} + +export const InlineTextInput: FunctionComponent = ({ + placeholder, + text, + ariaLabel, + onChange, +}) => { + const [isShowingTextInput, setIsShowingTextInput] = useState(false); + const [textValue, setTextValue] = useState(text ?? ''); + + const content = isShowingTextInput ? ( + el?.focus()} + onChange={(event) => setTextValue(event.target.value)} + /> + ) : ( + + {text || {placeholder}} + + ); + + const submitChange = useCallback(() => { + setIsShowingTextInput(false); + onChange(textValue); + }, [setIsShowingTextInput, onChange, textValue]); + + useEffect(() => { + const keyboardListener = (event: KeyboardEvent) => { + if (event.keyCode === keyCodes.ESCAPE || event.code === 'Escape') { + setIsShowingTextInput(false); + } + if (event.keyCode === keyCodes.ENTER || event.code === 'Enter') { + submitChange(); + } + }; + if (isShowingTextInput) { + window.addEventListener('keyup', keyboardListener); + } + return () => { + window.removeEventListener('keyup', keyboardListener); + }; + }, [isShowingTextInput, submitChange, setIsShowingTextInput]); + + return ( +
setIsShowingTextInput(true)} + onBlur={submitChange} + > + {content} +
+ ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/messages.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/messages.ts new file mode 100644 index 0000000000000..67dbf2708d665 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/messages.ts @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const editorItemMessages = { + moveButtonLabel: i18n.translate('xpack.ingestPipelines.pipelineEditor.item.moveButtonLabel', { + defaultMessage: 'Move this processor', + }), + editorButtonLabel: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.item.editButtonAriaLabel', + { + defaultMessage: 'Edit this processor', + } + ), + duplicateButtonLabel: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.item.moreMenu.duplicateButtonLabel', + { + defaultMessage: 'Duplicate this processor', + } + ), + addOnFailureButtonLabel: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.item.moreMenu.addOnFailureHandlerButtonLabel', + { + defaultMessage: 'Add on failure handler', + } + ), + cancelMoveButtonLabel: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.item.cancelMoveButtonAriaLabel', + { + defaultMessage: 'Cancel moving this processor', + } + ), + deleteButtonLabel: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.item.moreMenu.deleteButtonLabel', + { + defaultMessage: 'Delete', + } + ), + moreButtonAriaLabel: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.item.moreButtonAriaLabel', + { + defaultMessage: 'Show more actions for this processor', + } + ), + processorTypeLabel: ({ type }: { type: string }) => + i18n.translate('xpack.ingestPipelines.pipelineEditor.item.textInputAriaLabel', { + defaultMessage: 'Provide a description for this {type} processor', + values: { type }, + }), + descriptionPlaceholder: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.item.descriptionPlaceholder', + { defaultMessage: 'No description' } + ), +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/pipeline_processors_editor_item.scss b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/pipeline_processors_editor_item.scss new file mode 100644 index 0000000000000..a17e644853847 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/pipeline_processors_editor_item.scss @@ -0,0 +1,17 @@ +.pipelineProcessorsEditor__item { + &__textContainer { + padding: 4px; + border-radius: 2px; + + transition: border-color .3s; + border: 2px solid #FFF; + + &:hover { + border: 2px solid $euiColorLightShade; + } + } + &__textInput { + height: 21px; + min-width: 100px; + } +} diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/pipeline_processors_editor_item.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/pipeline_processors_editor_item.tsx new file mode 100644 index 0000000000000..0e47b3ef7cf88 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/pipeline_processors_editor_item.tsx @@ -0,0 +1,145 @@ +/* + * 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, { FunctionComponent, memo } from 'react'; +import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiText, EuiToolTip } from '@elastic/eui'; + +import { ProcessorInternal, ProcessorSelector } from '../../types'; + +import { usePipelineProcessorsContext } from '../../context'; + +import './pipeline_processors_editor_item.scss'; + +import { InlineTextInput } from './inline_text_input'; +import { ContextMenu } from './context_menu'; +import { editorItemMessages } from './messages'; + +export interface Handlers { + onMove: () => void; + onCancelMove: () => void; +} + +export interface Props { + processor: ProcessorInternal; + selected: boolean; + handlers: Handlers; + selector: ProcessorSelector; + description?: string; +} + +export const PipelineProcessorsEditorItem: FunctionComponent = memo( + ({ processor, description, handlers: { onCancelMove, onMove }, selector, selected }) => { + const { + state: { editor, processorsDispatch }, + } = usePipelineProcessorsContext(); + + const disabled = editor.mode.id !== 'idle'; + const isDarkBold = + editor.mode.id !== 'editingProcessor' || processor.id === editor.mode.arg.processor.id; + + return ( + + + + + + {processor.type} + + + + { + let nextOptions: Record; + if (!nextDescription) { + const { description: __, ...restOptions } = processor.options; + nextOptions = restOptions; + } else { + nextOptions = { + ...processor.options, + description: nextDescription, + }; + } + processorsDispatch({ + type: 'updateProcessor', + payload: { + processor: { + ...processor, + options: nextOptions, + }, + selector, + }, + }); + }} + ariaLabel={editorItemMessages.processorTypeLabel({ type: processor.type })} + text={description} + placeholder={editorItemMessages.descriptionPlaceholder} + /> + + + { + editor.setMode({ + id: 'editingProcessor', + arg: { processor, selector }, + }); + }} + /> + + + {selected ? ( + + ) : ( + + + + )} + + + + + { + editor.setMode({ id: 'creatingProcessor', arg: { selector } }); + }} + onDelete={() => { + editor.setMode({ id: 'removingProcessor', arg: { selector } }); + }} + onDuplicate={() => { + processorsDispatch({ + type: 'duplicateProcessor', + payload: { + source: selector, + }, + }); + }} + /> + + + ); + } +); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_remove_modal.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_remove_modal.tsx new file mode 100644 index 0000000000000..c38e470b36699 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_remove_modal.tsx @@ -0,0 +1,54 @@ +/* + * 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 { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; +import { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui'; +import { ProcessorInternal, ProcessorSelector } from '../types'; + +interface Props { + processor: ProcessorInternal; + selector: ProcessorSelector; + onResult: (arg: { confirmed: boolean; selector: ProcessorSelector }) => void; +} + +export const ProcessorRemoveModal = ({ processor, onResult, selector }: Props) => { + return ( + + + } + onCancel={() => onResult({ confirmed: false, selector })} + onConfirm={() => onResult({ confirmed: true, selector })} + cancelButtonText={ + + } + confirmButtonText={ + + } + > +

+ +

+
+
+ ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/index.ts new file mode 100644 index 0000000000000..60a1aa0a96fb1 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/index.ts @@ -0,0 +1,10 @@ +/* + * 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 { + ProcessorSettingsForm, + ProcessorSettingsFromOnSubmitArg, +} from './processor_settings_form.container'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/map_processor_type_to_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/map_processor_type_to_form.tsx new file mode 100644 index 0000000000000..e8164a0057d39 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/map_processor_type_to_form.tsx @@ -0,0 +1,56 @@ +/* + * 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 { FunctionComponent } from 'react'; + +// import { SetProcessor } from './processors/set'; +// import { Gsub } from './processors/gsub'; + +const mapProcessorTypeToForm = { + append: undefined, // TODO: Implement + bytes: undefined, // TODO: Implement + circle: undefined, // TODO: Implement + convert: undefined, // TODO: Implement + csv: undefined, // TODO: Implement + date: undefined, // TODO: Implement + date_index_name: undefined, // TODO: Implement + dissect: undefined, // TODO: Implement + dot_expander: undefined, // TODO: Implement + drop: undefined, // TODO: Implement + enrich: undefined, // TODO: Implement + fail: undefined, // TODO: Implement + foreach: undefined, // TODO: Implement + geoip: undefined, // TODO: Implement + grok: undefined, // TODO: Implement + html_strip: undefined, // TODO: Implement + inference: undefined, // TODO: Implement + join: undefined, // TODO: Implement + json: undefined, // TODO: Implement + kv: undefined, // TODO: Implement + lowercase: undefined, // TODO: Implement + pipeline: undefined, // TODO: Implement + remove: undefined, // TODO: Implement + rename: undefined, // TODO: Implement + script: undefined, // TODO: Implement + set_security_user: undefined, // TODO: Implement + split: undefined, // TODO: Implement + sort: undefined, // TODO: Implement + trim: undefined, // TODO: Implement + uppercase: undefined, // TODO: Implement + urldecode: undefined, // TODO: Implement + user_agent: undefined, // TODO: Implement + + gsub: undefined, + set: undefined, +}; + +export const types = Object.keys(mapProcessorTypeToForm); + +export type ProcessorType = keyof typeof mapProcessorTypeToForm; + +export const getProcessorForm = (type: ProcessorType | string): FunctionComponent | undefined => { + return mapProcessorTypeToForm[type as ProcessorType]; +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processor_settings_form.container.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processor_settings_form.container.tsx new file mode 100644 index 0000000000000..29b52ef84600a --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processor_settings_form.container.tsx @@ -0,0 +1,56 @@ +/* + * 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, { FunctionComponent, useCallback, useEffect } from 'react'; + +import { useForm, OnFormUpdateArg, FormData } from '../../../../../shared_imports'; +import { ProcessorInternal } from '../../types'; + +import { ProcessorSettingsForm as ViewComponent } from './processor_settings_form'; + +export type ProcessorSettingsFromOnSubmitArg = Omit; + +interface Props { + onFormUpdate: (form: OnFormUpdateArg) => void; + onSubmit: (processor: ProcessorSettingsFromOnSubmitArg) => void; + processor?: ProcessorInternal; +} + +export const ProcessorSettingsForm: FunctionComponent = ({ + processor, + onFormUpdate, + onSubmit, +}) => { + const handleSubmit = useCallback( + async (data: FormData, isValid: boolean) => { + if (isValid) { + const { type, customOptions, ...options } = data; + onSubmit({ + type, + options: customOptions ? customOptions : options, + }); + } + }, + [onSubmit] + ); + + const { form } = useForm({ + defaultValue: processor?.options, + onSubmit: handleSubmit, + }); + + useEffect(() => { + const subscription = form.subscribe(onFormUpdate); + return subscription.unsubscribe; + + // TODO: Address this issue + // For some reason adding `form` object to the dependencies array here is causing an + // infinite update loop. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [onFormUpdate]); + + return ; +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processor_settings_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processor_settings_form.tsx new file mode 100644 index 0000000000000..49bde2129aab6 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processor_settings_form.tsx @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { i18n } from '@kbn/i18n'; +import React, { FunctionComponent, memo } from 'react'; +import { EuiButton, EuiHorizontalRule } from '@elastic/eui'; + +import { Form, useForm, FormDataProvider } from '../../../../../shared_imports'; + +import { ProcessorInternal } from '../../types'; + +import { getProcessorForm } from './map_processor_type_to_form'; +import { CommonProcessorFields, ProcessorTypeField } from './processors/common_fields'; +import { Custom } from './processors/custom'; + +export interface Props { + processor?: ProcessorInternal; + form: ReturnType['form']; +} + +export const ProcessorSettingsForm: FunctionComponent = memo( + ({ processor, form }) => { + return ( + + + + + + + {(arg: any) => { + const { type } = arg; + let formContent: React.ReactNode | undefined; + + if (type?.length) { + const ProcessorFormFields = getProcessorForm(type as any); + + if (ProcessorFormFields) { + formContent = ( + <> + + + + ); + } else { + formContent = ; + } + + return ( + <> + {formContent} + + {i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.settingsForm.submitButtonLabel', + { defaultMessage: 'Submit' } + )} + + + ); + } + + // If the user has not yet defined a type, we do not show any settings fields + return null; + }} + + + ); + }, + (previous, current) => { + return previous.processor === current.processor; + } +); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/common_fields/common_processor_fields.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/common_fields/common_processor_fields.tsx new file mode 100644 index 0000000000000..4802653f9e680 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/common_fields/common_processor_fields.tsx @@ -0,0 +1,55 @@ +/* + * 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, { FunctionComponent } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { + FieldConfig, + UseField, + FIELD_TYPES, + Field, + ToggleField, +} from '../../../../../../../shared_imports'; + +const ignoreFailureConfig: FieldConfig = { + defaultValue: false, + label: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.commonFields.ignoreFailureFieldLabel', + { + defaultMessage: 'Ignore failure', + } + ), + type: FIELD_TYPES.TOGGLE, +}; + +const ifConfig: FieldConfig = { + defaultValue: undefined, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.commonFields.ifFieldLabel', { + defaultMessage: 'Condition (optional)', + }), + type: FIELD_TYPES.TEXT, +}; + +const tagConfig: FieldConfig = { + defaultValue: undefined, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.commonFields.tagFieldLabel', { + defaultMessage: 'Tag (optional)', + }), + type: FIELD_TYPES.TEXT, +}; + +export const CommonProcessorFields: FunctionComponent = () => { + return ( + <> + + + + + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/common_fields/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/common_fields/index.ts new file mode 100644 index 0000000000000..f3fa0e028faaa --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/common_fields/index.ts @@ -0,0 +1,9 @@ +/* + * 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 { ProcessorTypeField } from './processor_type_field'; + +export { CommonProcessorFields } from './common_processor_fields'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/common_fields/processor_type_field.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/common_fields/processor_type_field.tsx new file mode 100644 index 0000000000000..6c86fc16bcdd0 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/common_fields/processor_type_field.tsx @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { i18n } from '@kbn/i18n'; +import React, { FunctionComponent } from 'react'; +import { + FIELD_TYPES, + FieldConfig, + UseField, + fieldValidators, + ComboBoxField, +} from '../../../../../../../shared_imports'; +import { types } from '../../map_processor_type_to_form'; + +interface Props { + initialType?: string; +} + +const { emptyField } = fieldValidators; + +const typeConfig: FieldConfig = { + type: FIELD_TYPES.COMBO_BOX, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.typeField.typeFieldLabel', { + defaultMessage: 'Processor', + }), + deserializer: (value: string | undefined) => { + if (value) { + return [value]; + } + return []; + }, + serializer: (value: string[]) => { + return value[0]; + }, + validations: [ + { + validator: emptyField( + i18n.translate('xpack.ingestPipelines.pipelineEditor.typeField.fieldRequiredError', { + defaultMessage: 'A type is required.', + }) + ), + }, + ], +}; + +export const ProcessorTypeField: FunctionComponent = ({ initialType }) => { + return ( + ({ label: type, value: type })), + noSuggestions: false, + singleSelection: { + asPlainText: true, + }, + }, + }} + /> + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/custom.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/custom.tsx new file mode 100644 index 0000000000000..61fc31a7b472a --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/custom.tsx @@ -0,0 +1,90 @@ +/* + * 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, { FunctionComponent } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { + FieldConfig, + FIELD_TYPES, + fieldValidators, + UseField, + JsonEditorField, +} from '../../../../../../shared_imports'; + +const { emptyField, isJsonField } = fieldValidators; + +const customConfig: FieldConfig = { + type: FIELD_TYPES.TEXT, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.customForm.optionsFieldLabel', { + defaultMessage: 'Configuration options', + }), + serializer: (value: string) => { + try { + return JSON.parse(value); + } catch (error) { + // swallow error and return non-parsed value; + return value; + } + }, + deserializer: (value: any) => { + if (value === '') { + return '{\n\n}'; + } + return JSON.stringify(value, null, 2); + }, + validations: [ + { + validator: emptyField( + i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.customForm.configurationRequiredError', + { + defaultMessage: 'Configuration options are required.', + } + ) + ), + }, + { + validator: isJsonField( + i18n.translate('xpack.ingestPipelines.pipelineEditor.customForm.invalidJsonError', { + defaultMessage: 'The input is not valid.', + }) + ), + }, + ], +}; + +interface Props { + defaultOptions?: any; +} + +/** + * This is a catch-all component to support settings for custom processors + * or existing processors not yet supported by the UI. + * + * We store the settings in a field called "customOptions" + **/ +export const Custom: FunctionComponent = ({ defaultOptions }) => { + return ( + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/gsub.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/gsub.tsx new file mode 100644 index 0000000000000..77f85e61eff6b --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/gsub.tsx @@ -0,0 +1,98 @@ +/* + * 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, { FunctionComponent } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { + FieldConfig, + FIELD_TYPES, + fieldValidators, + ToggleField, + UseField, + Field, +} from '../../../../../../shared_imports'; + +const { emptyField } = fieldValidators; + +const fieldConfig: FieldConfig = { + type: FIELD_TYPES.TEXT, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.gsubForm.fieldFieldLabel', { + defaultMessage: 'Field', + }), + validations: [ + { + validator: emptyField( + i18n.translate('xpack.ingestPipelines.pipelineEditor.gsubForm.fieldRequiredError', { + defaultMessage: 'A field value is required.', + }) + ), + }, + ], +}; + +const patternConfig: FieldConfig = { + type: FIELD_TYPES.TEXT, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.gsubForm.patternFieldLabel', { + defaultMessage: 'Pattern', + }), + validations: [ + { + validator: emptyField( + i18n.translate('xpack.ingestPipelines.pipelineEditor.gsubForm.patternRequiredError', { + defaultMessage: 'A pattern value is required.', + }) + ), + }, + ], +}; + +const replacementConfig: FieldConfig = { + type: FIELD_TYPES.TEXT, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.gsubForm.replacementFieldLabel', { + defaultMessage: 'Replacement', + }), + validations: [ + { + validator: emptyField( + i18n.translate('xpack.ingestPipelines.pipelineEditor.gsubForm.replacementRequiredError', { + defaultMessage: 'A replacement value is required.', + }) + ), + }, + ], +}; + +const targetConfig: FieldConfig = { + type: FIELD_TYPES.TEXT, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.gsubForm.targetFieldLabel', { + defaultMessage: 'Target field (optional)', + }), +}; + +const ignoreMissingConfig: FieldConfig = { + defaultValue: false, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.gsubForm.ignoreMissingFieldLabel', { + defaultMessage: 'Ignore missing', + }), + type: FIELD_TYPES.TOGGLE, +}; + +export const Gsub: FunctionComponent = () => { + return ( + <> + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/set.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/set.tsx new file mode 100644 index 0000000000000..1ba6a14d0448d --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_settings_form/processors/set.tsx @@ -0,0 +1,74 @@ +/* + * 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, { FunctionComponent } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { + FieldConfig, + FIELD_TYPES, + fieldValidators, + ToggleField, + UseField, + Field, +} from '../../../../../../shared_imports'; + +const { emptyField } = fieldValidators; + +const fieldConfig: FieldConfig = { + type: FIELD_TYPES.TEXT, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.setForm.fieldFieldLabel', { + defaultMessage: 'Field', + }), + validations: [ + { + validator: emptyField( + i18n.translate('xpack.ingestPipelines.pipelineEditor.setForm.fieldRequiredError', { + defaultMessage: 'A field value is required.', + }) + ), + }, + ], +}; + +const valueConfig: FieldConfig = { + type: FIELD_TYPES.TEXT, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.setForm.valueFieldLabel', { + defaultMessage: 'Value', + }), + validations: [ + { + validator: emptyField( + i18n.translate('xpack.ingestPipelines.pipelineEditor.setForm.valueRequiredError', { + defaultMessage: 'A value to set is required.', + }) + ), + }, + ], +}; + +const overrideConfig: FieldConfig = { + defaultValue: false, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.setForm.overrideFieldLabel', { + defaultMessage: 'Override', + }), + type: FIELD_TYPES.TOGGLE, +}; + +/** + * Disambiguate name from the Set data structure + */ +export const SetProcessor: FunctionComponent = () => { + return ( + <> + + + + + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_title_and_test_button.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_title_and_test_button.tsx new file mode 100644 index 0000000000000..bc646c9eefa55 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_title_and_test_button.tsx @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; +import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLink, EuiText, EuiTitle } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { usePipelineProcessorsContext } from '../context'; + +export interface Props { + onTestPipelineClick: () => void; + isTestButtonDisabled: boolean; +} + +export const ProcessorsTitleAndTestButton: FunctionComponent = ({ + onTestPipelineClick, + isTestButtonDisabled, +}) => { + const { links } = usePipelineProcessorsContext(); + return ( + + + +

+ {i18n.translate('xpack.ingestPipelines.pipelineEditor.processorsTreeTitle', { + defaultMessage: 'Processors', + })} +

+
+ + + {i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.processorsDocumentationLink', + { + defaultMessage: 'Learn more.', + } + )} + + ), + }} + /> + +
+ + + + + +
+ ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/components/drop_zone_button.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/components/drop_zone_button.tsx new file mode 100644 index 0000000000000..a47886292cf32 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/components/drop_zone_button.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import React, { FunctionComponent } from 'react'; +import classNames from 'classnames'; +import { EuiButtonIcon, EuiFlexItem } from '@elastic/eui'; + +export interface Props { + isDisabled: boolean; + onClick: (event: React.MouseEvent) => void; +} + +const MOVE_HERE_LABEL = i18n.translate('xpack.ingestPipelines.pipelineEditor.moveTargetLabel', { + defaultMessage: 'Move here', +}); + +export const DropZoneButton: FunctionComponent = ({ onClick, isDisabled }) => { + const containerClasses = classNames({ + 'pipelineProcessorsEditor__tree__dropZoneContainer--active': !isDisabled, + }); + const buttonClasses = classNames({ + 'pipelineProcessorsEditor__tree__dropZoneButton--active': !isDisabled, + }); + + return ( + + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/components/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/components/index.ts new file mode 100644 index 0000000000000..e9548624d2cef --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/components/index.ts @@ -0,0 +1,11 @@ +/* + * 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 { DropZoneButton } from './drop_zone_button'; + +export { PrivateTree } from './private_tree'; + +export { TreeNode } from './tree_node'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/components/private_tree.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/components/private_tree.tsx new file mode 100644 index 0000000000000..bdc6b2eb44e2d --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/components/private_tree.tsx @@ -0,0 +1,210 @@ +/* + * 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, { FunctionComponent, MutableRefObject, useEffect } from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { AutoSizer, List, WindowScroller } from 'react-virtualized'; + +import { DropSpecialLocations } from '../../../constants'; +import { ProcessorInternal, ProcessorSelector } from '../../../types'; +import { isChildPath } from '../../../processors_reducer'; + +import { DropZoneButton } from '.'; +import { TreeNode } from '.'; +import { calculateItemHeight } from '../utils'; +import { OnActionHandler, ProcessorInfo } from '../processors_tree'; + +export interface PrivateProps { + processors: ProcessorInternal[]; + selector: ProcessorSelector; + onAction: OnActionHandler; + level: number; + movingProcessor?: ProcessorInfo; + // Only passed into the top level list + windowScrollerRef?: MutableRefObject; + listRef?: MutableRefObject; +} + +const isDropZoneAboveDisabled = (processor: ProcessorInfo, selectedProcessor: ProcessorInfo) => { + return Boolean( + // Is the selected node first in a list? + (!selectedProcessor.aboveId && selectedProcessor.id === processor.id) || + isChildPath(selectedProcessor.selector, processor.selector) + ); +}; + +const isDropZoneBelowDisabled = (processor: ProcessorInfo, selectedProcessor: ProcessorInfo) => { + return ( + processor.id === selectedProcessor.id || + processor.belowId === selectedProcessor.id || + isChildPath(selectedProcessor.selector, processor.selector) + ); +}; + +/** + * Recursively rendering tree component for ingest pipeline processors. + * + * Note: this tree should start at level 1. It is the only level at + * which we render the optimised virtual component. This gives a + * massive performance boost to this component which can get very tall. + * + * The first level list also contains the outside click listener which + * enables users to click outside of the tree and cancel moving a + * processor. + */ +export const PrivateTree: FunctionComponent = ({ + processors, + selector, + movingProcessor, + onAction, + level, + windowScrollerRef, + listRef, +}) => { + const renderRow = ({ + idx, + info, + processor, + }: { + idx: number; + info: ProcessorInfo; + processor: ProcessorInternal; + }) => { + return ( + <> + {idx === 0 ? ( + { + event.preventDefault(); + onAction({ + type: 'move', + payload: { + destination: selector.concat(DropSpecialLocations.top), + source: movingProcessor!.selector, + }, + }); + }} + isDisabled={Boolean( + !movingProcessor || isDropZoneAboveDisabled(info, movingProcessor!) + )} + /> + ) : undefined} + + + + { + event.preventDefault(); + onAction({ + type: 'move', + payload: { + destination: selector.concat(String(idx + 1)), + source: movingProcessor!.selector, + }, + }); + }} + /> + + ); + }; + + useEffect(() => { + if (windowScrollerRef && windowScrollerRef.current) { + windowScrollerRef.current.updatePosition(); + } + if (listRef && listRef.current) { + listRef.current.recomputeRowHeights(); + } + }, [processors, listRef, windowScrollerRef, movingProcessor]); + + // A list optimized to handle very many items. + const renderVirtualList = () => { + return ( + + {({ height, registerChild, isScrolling, onChildScroll, scrollTop }: any) => { + return ( + + + {({ width }) => { + return ( +
+ { + const processor = processors[index]; + return calculateItemHeight({ + processor, + isFirstInArray: index === 0, + }); + }} + rowRenderer={({ index: idx, style }) => { + const processor = processors[idx]; + const above = processors[idx - 1]; + const below = processors[idx + 1]; + const info: ProcessorInfo = { + id: processor.id, + selector: selector.concat(String(idx)), + aboveId: above?.id, + belowId: below?.id, + }; + + return ( +
+ {renderRow({ processor, info, idx })} +
+ ); + }} + processors={processors} + /> +
+ ); + }} +
+
+ ); + }} +
+ ); + }; + + if (level === 1) { + // Only render the optimised list for the top level list because that is the list + // that will almost certainly be the tallest + return renderVirtualList(); + } + + return ( + + {processors.map((processor, idx) => { + const above = processors[idx - 1]; + const below = processors[idx + 1]; + const info: ProcessorInfo = { + id: processor.id, + selector: selector.concat(String(idx)), + aboveId: above?.id, + belowId: below?.id, + }; + + return
{renderRow({ processor, idx, info })}
; + })} +
+ ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/components/tree_node.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/components/tree_node.tsx new file mode 100644 index 0000000000000..ebe4ca4962b4c --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/components/tree_node.tsx @@ -0,0 +1,115 @@ +/* + * 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, { FunctionComponent, useMemo } from 'react'; +import classNames from 'classnames'; +import { i18n } from '@kbn/i18n'; +import { EuiPanel, EuiText } from '@elastic/eui'; + +import { ProcessorInternal } from '../../../types'; + +import { ProcessorInfo, OnActionHandler } from '../processors_tree'; + +import { PipelineProcessorsEditorItem, Handlers } from '../../pipeline_processors_editor_item'; +import { AddProcessorButton } from '../../add_processor_button'; + +import { PrivateTree } from './private_tree'; + +export interface Props { + processor: ProcessorInternal; + processorInfo: ProcessorInfo; + onAction: OnActionHandler; + level: number; + movingProcessor?: ProcessorInfo; +} + +const INDENTATION_PX = 34; + +export const TreeNode: FunctionComponent = ({ + processor, + processorInfo, + onAction, + movingProcessor, + level, +}) => { + const stringSelector = processorInfo.selector.join('.'); + const handlers = useMemo((): Handlers => { + return { + onMove: () => { + onAction({ type: 'selectToMove', payload: { info: processorInfo } }); + }, + onCancelMove: () => { + onAction({ type: 'cancelMove' }); + }, + }; + }, [onAction, stringSelector, processor]); // eslint-disable-line react-hooks/exhaustive-deps + + const selected = movingProcessor?.id === processor.id; + + const panelClasses = classNames({ + 'pipelineProcessorsEditor__tree__item--selected': selected, + }); + + const renderOnFailureHandlersTree = () => { + if (!processor.onFailure?.length) { + return; + } + + const onFailureHandlerLabelClasses = classNames({ + 'pipelineProcessorsEditor__tree__onFailureHandlerLabel--withDropZone': + movingProcessor != null && + movingProcessor.id !== processor.onFailure[0].id && + movingProcessor.id !== processor.id, + }); + + return ( +
+
+ + {i18n.translate('xpack.ingestPipelines.pipelineEditor.onFailureProcessorsLabel', { + defaultMessage: 'Failure handlers', + })} + +
+ + + onAction({ + type: 'addProcessor', + payload: { target: processorInfo.selector.concat('onFailure') }, + }) + } + /> +
+ ); + }; + + return ( + + + {renderOnFailureHandlersTree()} + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/index.ts new file mode 100644 index 0000000000000..5a09794fd4bee --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { ProcessorsTree, OnActionHandler, ProcessorInfo } from './processors_tree'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.scss b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.scss new file mode 100644 index 0000000000000..ad9058cea5e18 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.scss @@ -0,0 +1,74 @@ +@import '@elastic/eui/src/global_styling/variables/size'; + +.pipelineProcessorsEditor__tree { + + &__container { + background-color: $euiColorLightestShade; + padding: $euiSizeS; + } + + &__dropZoneContainer { + margin: 2px; + visibility: hidden; + border: 2px dashed $euiColorLightShade; + height: 12px; + border-radius: 2px; + + transition: border .5s; + + &--active { + &:hover { + border: 2px dashed $euiColorPrimary; + } + visibility: visible; + } + } + + &__dropZoneButton { + height: 8px; + opacity: 0; + text-decoration: none !important; + + &--active { + &:hover { + transform: none !important; + } + } + + &:disabled { + cursor: default !important; + & > * { + cursor: default !important; + } + } + } + + &__onFailureHandlerLabelContainer { + position: relative; + height: 14px; + } + &__onFailureHandlerLabel { + position: absolute; + bottom: -16px; + &--withDropZone { + bottom: -4px; + } + } + + + &__onFailureHandlerContainer { + margin-top: $euiSizeS; + margin-bottom: $euiSizeS; + & > * { + overflow: visible; + } + } + + &__item { + transition: border-color 1s; + min-height: 50px; + &--selected { + border: 1px solid $euiColorPrimary; + } + } +} diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.tsx new file mode 100644 index 0000000000000..d0661913515b2 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.tsx @@ -0,0 +1,110 @@ +/* + * 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, { FunctionComponent, memo, useRef, useEffect } from 'react'; +import { EuiFlexGroup, EuiFlexItem, keyCodes } from '@elastic/eui'; +import { List, WindowScroller } from 'react-virtualized'; + +import { ProcessorInternal, ProcessorSelector } from '../../types'; + +import './processors_tree.scss'; +import { AddProcessorButton } from '../add_processor_button'; +import { PrivateTree } from './components'; + +export interface ProcessorInfo { + id: string; + selector: ProcessorSelector; + aboveId?: string; + belowId?: string; +} + +export type Action = + | { type: 'move'; payload: { source: ProcessorSelector; destination: ProcessorSelector } } + | { type: 'selectToMove'; payload: { info: ProcessorInfo } } + | { type: 'cancelMove' } + | { type: 'addProcessor'; payload: { target: ProcessorSelector } }; + +export type OnActionHandler = (action: Action) => void; + +export interface Props { + processors: ProcessorInternal[]; + baseSelector: ProcessorSelector; + onAction: OnActionHandler; + movingProcessor?: ProcessorInfo; + 'data-test-subj'?: string; +} + +/** + * This component is the public interface to our optimised tree rendering private components and + * also contains top-level state concerns for an instance of the component + */ +export const ProcessorsTree: FunctionComponent = memo((props) => { + const { processors, baseSelector, onAction, movingProcessor } = props; + // These refs are created here so they can be shared with all + // recursively rendered trees. Their values should come from react-virtualized + // List component and WindowScroller component. + const windowScrollerRef = useRef(null); + const listRef = useRef(null); + + useEffect(() => { + const cancelMoveKbListener = (event: KeyboardEvent) => { + // x-browser support per https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode + if (event.keyCode === keyCodes.ESCAPE || event.code === 'Escape') { + onAction({ type: 'cancelMove' }); + } + }; + const cancelMoveClickListener = (ev: any) => { + onAction({ type: 'cancelMove' }); + }; + // Give the browser a chance to flush any click events including the click + // event that triggered any state transition into selecting a processor to move + setTimeout(() => { + if (movingProcessor) { + window.addEventListener('keyup', cancelMoveKbListener); + window.addEventListener('click', cancelMoveClickListener); + } else { + window.removeEventListener('keyup', cancelMoveKbListener); + window.removeEventListener('click', cancelMoveClickListener); + } + }); + return () => { + window.removeEventListener('keyup', cancelMoveKbListener); + window.removeEventListener('click', cancelMoveClickListener); + }; + }, [movingProcessor, onAction]); + + return ( + + + + + + + + { + onAction({ type: 'addProcessor', payload: { target: baseSelector } }); + }} + /> + + + + + ); +}); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/utils.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/utils.ts new file mode 100644 index 0000000000000..457e335602b9b --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/utils.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ProcessorInternal } from '../../types'; + +// These values are tied to the style and heights following components: +// Do not change these numbers without testing the component for visual +// regressions! +// - ./components/tree_node.tsx +// - ./components/drop_zone_button.tsx +// - ./components/pipeline_processors_editor_item.tsx +const itemHeightsPx = { + WITHOUT_NESTED_ITEMS: 67, + WITH_NESTED_ITEMS: 137, + TOP_PADDING: 16, +}; + +export const calculateItemHeight = ({ + processor, + isFirstInArray, +}: { + processor: ProcessorInternal; + isFirstInArray: boolean; +}): number => { + const padding = isFirstInArray ? itemHeightsPx.TOP_PADDING : 0; + + if (!processor.onFailure?.length) { + return padding + itemHeightsPx.WITHOUT_NESTED_ITEMS; + } + + return ( + padding + + itemHeightsPx.WITH_NESTED_ITEMS + + processor.onFailure.reduce((acc, p, idx) => { + return acc + calculateItemHeight({ processor: p, isFirstInArray: idx === 0 }); + }, 0) + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/settings_form_flyout.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/settings_form_flyout.tsx new file mode 100644 index 0000000000000..94d5f0eda6454 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/settings_form_flyout.tsx @@ -0,0 +1,67 @@ +/* + * 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 { EuiFlyout, EuiFlyoutBody, EuiFlyoutHeader, EuiTitle } from '@elastic/eui'; + +import React, { FunctionComponent, memo, useEffect } from 'react'; + +import { FormattedMessage } from '@kbn/i18n/react'; + +import { OnFormUpdateArg } from '../../../../shared_imports'; + +import { ProcessorInternal } from '../types'; + +import { ProcessorSettingsForm, ProcessorSettingsFromOnSubmitArg } from '.'; + +export type OnSubmitHandler = (processor: ProcessorSettingsFromOnSubmitArg) => void; + +export interface Props { + processor: ProcessorInternal | undefined; + onFormUpdate: (form: OnFormUpdateArg) => void; + onSubmit: OnSubmitHandler; + isOnFailureProcessor: boolean; + onOpen: () => void; + onClose: () => void; +} + +export const SettingsFormFlyout: FunctionComponent = memo( + ({ onClose, processor, onSubmit, onFormUpdate, onOpen, isOnFailureProcessor }) => { + useEffect( + () => { + onOpen(); + }, + [] /* eslint-disable-line react-hooks/exhaustive-deps */ + ); + const flyoutTitleContent = isOnFailureProcessor ? ( + + ) : ( + + ); + + return ( + + + +

{flyoutTitleContent}

+
+
+ + + +
+ ); + } +); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/constants.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/constants.ts new file mode 100644 index 0000000000000..46e3d1c803fd5 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/constants.ts @@ -0,0 +1,10 @@ +/* + * 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 enum DropSpecialLocations { + top = 'TOP', + bottom = 'BOTTOM', +} diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context.tsx new file mode 100644 index 0000000000000..150a52f1a5fe0 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context.tsx @@ -0,0 +1,55 @@ +/* + * 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, { createContext, Dispatch, FunctionComponent, useContext, useState } from 'react'; +import { EditorMode } from './types'; +import { ProcessorsDispatch } from './processors_reducer'; + +interface Links { + learnMoreAboutProcessorsUrl: string; + learnMoreAboutOnFailureProcessorsUrl: string; +} + +const PipelineProcessorsContext = createContext<{ + links: Links; + state: { + processorsDispatch: ProcessorsDispatch; + editor: { + mode: EditorMode; + setMode: Dispatch; + }; + }; +}>({} as any); + +interface Props { + links: Links; + processorsDispatch: ProcessorsDispatch; +} + +export const PipelineProcessorsContextProvider: FunctionComponent = ({ + links, + children, + processorsDispatch, +}) => { + const [mode, setMode] = useState({ id: 'idle' }); + return ( + + {children} + + ); +}; + +export const usePipelineProcessorsContext = () => { + const ctx = useContext(PipelineProcessorsContext); + if (!ctx) { + throw new Error( + 'usePipelineProcessorsContext can only be used inside of PipelineProcessorsContextProvider' + ); + } + return ctx; +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/deserialize.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/deserialize.ts new file mode 100644 index 0000000000000..fa1d041bdaba3 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/deserialize.ts @@ -0,0 +1,56 @@ +/* + * 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 uuid from 'uuid'; +import { Processor } from '../../../../common/types'; +import { ProcessorInternal } from './types'; + +export interface DeserializeArgs { + processors: Processor[]; + onFailure?: Processor[]; +} + +export interface DeserializeResult { + processors: ProcessorInternal[]; + onFailure?: ProcessorInternal[]; +} + +const getProcessorType = (processor: Processor): string => { + /** + * See the definition of {@link ProcessorInternal} for why this works to extract the + * processor type. + */ + return Object.keys(processor)[0]!; +}; + +const convertToPipelineInternalProcessor = (processor: Processor): ProcessorInternal => { + const type = getProcessorType(processor); + const { on_failure: originalOnFailure, ...options } = processor[type]; + const onFailure = originalOnFailure?.length + ? convertProcessors(originalOnFailure) + : (originalOnFailure as ProcessorInternal[] | undefined); + return { + id: uuid.v4(), + type, + onFailure, + options, + }; +}; + +const convertProcessors = (processors: Processor[]) => { + const convertedProcessors = []; + + for (const processor of processors) { + convertedProcessors.push(convertToPipelineInternalProcessor(processor)); + } + return convertedProcessors; +}; + +export const deserialize = ({ processors, onFailure }: DeserializeArgs): DeserializeResult => { + return { + processors: convertProcessors(processors), + onFailure: onFailure ? convertProcessors(onFailure) : undefined, + }; +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/index.ts new file mode 100644 index 0000000000000..58d6e492b85e5 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/index.ts @@ -0,0 +1,11 @@ +/* + * 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 { PipelineProcessorsEditor, OnUpdateHandler } from './pipeline_processors_editor.container'; + +export { OnUpdateHandlerArg } from './types'; + +export { SerializeResult } from './serialize'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/pipeline_processors_editor.container.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/pipeline_processors_editor.container.tsx new file mode 100644 index 0000000000000..057f8638700a4 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/pipeline_processors_editor.container.tsx @@ -0,0 +1,76 @@ +/* + * 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, { FunctionComponent, useMemo } from 'react'; + +import { Processor } from '../../../../common/types'; + +import { deserialize } from './deserialize'; + +import { useProcessorsState } from './processors_reducer'; + +import { PipelineProcessorsContextProvider } from './context'; + +import { OnUpdateHandlerArg } from './types'; + +import { PipelineProcessorsEditor as PipelineProcessorsEditorUI } from './pipeline_processors_editor'; + +export interface Props { + value: { + processors: Processor[]; + onFailure?: Processor[]; + }; + onUpdate: (arg: OnUpdateHandlerArg) => void; + isTestButtonDisabled: boolean; + onTestPipelineClick: () => void; + learnMoreAboutProcessorsUrl: string; + learnMoreAboutOnFailureProcessorsUrl: string; + /** + * Give users a way to react to this component opening a flyout + */ + onFlyoutOpen: () => void; +} + +export type OnUpdateHandler = (arg: OnUpdateHandlerArg) => void; + +export const PipelineProcessorsEditor: FunctionComponent = ({ + value: { processors: originalProcessors, onFailure: originalOnFailureProcessors }, + onFlyoutOpen, + onUpdate, + isTestButtonDisabled, + learnMoreAboutOnFailureProcessorsUrl, + learnMoreAboutProcessorsUrl, + onTestPipelineClick, +}) => { + const deserializedResult = useMemo( + () => + deserialize({ + processors: originalProcessors, + onFailure: originalOnFailureProcessors, + }), + // TODO: Re-add the dependency on the props and make the state set-able + // when new props come in so that this component will be controllable + [] // eslint-disable-line react-hooks/exhaustive-deps + ); + const [processorsState, processorsDispatch] = useProcessorsState(deserializedResult); + const { processors, onFailure } = processorsState; + + return ( + + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/pipeline_processors_editor.scss b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/pipeline_processors_editor.scss new file mode 100644 index 0000000000000..ee7421d7dbfa8 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/pipeline_processors_editor.scss @@ -0,0 +1,3 @@ +.pipelineProcessorsEditor { + margin-bottom: $euiSize; +} diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/pipeline_processors_editor.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/pipeline_processors_editor.tsx new file mode 100644 index 0000000000000..24b9598a74d47 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/pipeline_processors_editor.tsx @@ -0,0 +1,239 @@ +/* + * 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 { FormattedMessage } from '@kbn/i18n/react'; +import React, { FunctionComponent, useCallback, memo, useState, useEffect } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiSwitch } from '@elastic/eui'; + +import './pipeline_processors_editor.scss'; + +import { + ProcessorsTitleAndTestButton, + OnFailureProcessorsTitle, + ProcessorsTree, + SettingsFormFlyout, + ProcessorRemoveModal, + OnActionHandler, + OnSubmitHandler, +} from './components'; + +import { + ProcessorInternal, + ProcessorSelector, + OnUpdateHandlerArg, + FormValidityState, + OnFormUpdateArg, +} from './types'; + +import { serialize } from './serialize'; +import { getValue } from './utils'; +import { usePipelineProcessorsContext } from './context'; + +export interface Props { + processors: ProcessorInternal[]; + onFailureProcessors: ProcessorInternal[]; + onUpdate: (arg: OnUpdateHandlerArg) => void; + isTestButtonDisabled: boolean; + onTestPipelineClick: () => void; + onFlyoutOpen: () => void; +} + +const PROCESSOR_STATE_SCOPE: ProcessorSelector = ['processors']; +const ON_FAILURE_STATE_SCOPE: ProcessorSelector = ['onFailure']; + +export const PipelineProcessorsEditor: FunctionComponent = memo( + function PipelineProcessorsEditor({ + processors, + onFailureProcessors, + onTestPipelineClick, + isTestButtonDisabled, + onUpdate, + onFlyoutOpen, + }) { + const { + state: { editor, processorsDispatch }, + } = usePipelineProcessorsContext(); + + const { mode: editorMode, setMode: setEditorMode } = editor; + + const [formState, setFormState] = useState({ + validate: () => Promise.resolve(true), + }); + + const onFormUpdate = useCallback<(arg: OnFormUpdateArg) => void>( + ({ isValid, validate }) => { + setFormState({ + validate: async () => { + if (isValid === undefined) { + return validate(); + } + return isValid; + }, + }); + }, + [setFormState] + ); + + const [showGlobalOnFailure, setShowGlobalOnFailure] = useState( + Boolean(onFailureProcessors.length) + ); + + useEffect(() => { + onUpdate({ + validate: async () => { + const formValid = await formState.validate(); + return formValid && editorMode.id === 'idle'; + }, + getData: () => + serialize({ + onFailure: showGlobalOnFailure ? onFailureProcessors : undefined, + processors, + }), + }); + }, [processors, onFailureProcessors, onUpdate, formState, editorMode, showGlobalOnFailure]); + + const onSubmit = useCallback( + (processorTypeAndOptions) => { + switch (editorMode.id) { + case 'creatingProcessor': + processorsDispatch({ + type: 'addProcessor', + payload: { + processor: { ...processorTypeAndOptions }, + targetSelector: editorMode.arg.selector, + }, + }); + break; + case 'editingProcessor': + processorsDispatch({ + type: 'updateProcessor', + payload: { + processor: { + ...editorMode.arg.processor, + ...processorTypeAndOptions, + }, + selector: editorMode.arg.selector, + }, + }); + break; + default: + } + setEditorMode({ id: 'idle' }); + }, + [processorsDispatch, editorMode, setEditorMode] + ); + + const onCloseSettingsForm = useCallback(() => { + setEditorMode({ id: 'idle' }); + setFormState({ validate: () => Promise.resolve(true) }); + }, [setFormState, setEditorMode]); + + const onTreeAction = useCallback( + (action) => { + switch (action.type) { + case 'addProcessor': + setEditorMode({ id: 'creatingProcessor', arg: { selector: action.payload.target } }); + break; + case 'move': + setEditorMode({ id: 'idle' }); + processorsDispatch({ + type: 'moveProcessor', + payload: action.payload, + }); + break; + case 'selectToMove': + setEditorMode({ id: 'movingProcessor', arg: action.payload.info }); + break; + case 'cancelMove': + setEditorMode({ id: 'idle' }); + break; + } + }, + [processorsDispatch, setEditorMode] + ); + + const movingProcessor = editorMode.id === 'movingProcessor' ? editorMode.arg : undefined; + + return ( +
+ + + + + + + + + + + + + + + + } + checked={showGlobalOnFailure} + onChange={(e) => setShowGlobalOnFailure(e.target.checked)} + data-test-subj="pipelineEditorOnFailureToggle" + /> + + {showGlobalOnFailure ? ( + + + + ) : undefined} + + {editorMode.id === 'editingProcessor' || editorMode.id === 'creatingProcessor' ? ( + 1} + processor={editorMode.id === 'editingProcessor' ? editorMode.arg.processor : undefined} + onOpen={onFlyoutOpen} + onFormUpdate={onFormUpdate} + onSubmit={onSubmit} + onClose={onCloseSettingsForm} + /> + ) : undefined} + {editorMode.id === 'removingProcessor' && ( + { + if (confirmed) { + processorsDispatch({ + type: 'removeProcessor', + payload: { selector }, + }); + } + setEditorMode({ id: 'idle' }); + }} + /> + )} +
+ ); + } +); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/processors_reducer/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/processors_reducer/index.ts new file mode 100644 index 0000000000000..b43d94e19bf9f --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/processors_reducer/index.ts @@ -0,0 +1,15 @@ +/* + * 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 { + State, + reducer, + useProcessorsState, + ProcessorsDispatch, + Action, +} from './processors_reducer'; + +export { isChildPath } from './utils'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/processors_reducer/processors_reducer.test.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/processors_reducer/processors_reducer.test.ts new file mode 100644 index 0000000000000..43072d65bac4e --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/processors_reducer/processors_reducer.test.ts @@ -0,0 +1,376 @@ +/* + * 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 { reducer, State } from './processors_reducer'; +import { DropSpecialLocations } from '../constants'; +import { PARENT_CHILD_NEST_ERROR } from './utils'; + +const initialState: State = { + processors: [], + onFailure: [], + isRoot: true, +}; + +describe('Processors reducer', () => { + it('reorders processors', () => { + const processor1 = { id: expect.any(String), type: 'test1', options: {} }; + const processor2 = { id: expect.any(String), type: 'test2', options: {} }; + const processor3 = { id: expect.any(String), type: 'test3', options: {} }; + + const s1 = reducer(initialState, { + type: 'addProcessor', + payload: { processor: processor1, targetSelector: ['processors'] }, + }); + const s2 = reducer(s1, { + type: 'addProcessor', + payload: { processor: processor2, targetSelector: ['processors'] }, + }); + const s3 = reducer(s2, { + type: 'addProcessor', + payload: { processor: processor3, targetSelector: ['processors'] }, + }); + + expect(s3.processors).toEqual([processor1, processor2, processor3]); + + // Move the second processor to the first + const s4 = reducer(s3, { + type: 'moveProcessor', + payload: { + source: ['processors', '1'], + destination: ['processors', '0'], + }, + }); + + expect(s4.processors).toEqual([processor2, processor1, processor3]); + }); + + it('moves and orders processors out of lists', () => { + const processor1 = { id: expect.any(String), type: 'test1', options: {} }; + const processor2 = { id: expect.any(String), type: 'test2', options: {} }; + const processor3 = { id: expect.any(String), type: 'test3', options: {} }; + const processor4 = { id: expect.any(String), type: 'test4', options: {} }; + + const s1 = reducer(initialState, { + type: 'addProcessor', + payload: { processor: processor1, targetSelector: ['processors'] }, + }); + const s2 = reducer(s1, { + type: 'addProcessor', + payload: { processor: processor2, targetSelector: ['processors'] }, + }); + + const s3 = reducer(s2, { + type: 'addProcessor', + payload: { processor: processor3, targetSelector: ['processors', '1'] }, + }); + + const s4 = reducer(s3, { + type: 'addProcessor', + payload: { + processor: processor4, + targetSelector: ['processors', '1', 'onFailure', '0'], + }, + }); + + expect(s4.processors).toEqual([ + processor1, + { ...processor2, onFailure: [{ ...processor3, onFailure: [processor4] }] }, + ]); + + // Move the first on failure processor of the second processors on failure processor + // to the second position of the root level. + const s5 = reducer(s4, { + type: 'moveProcessor', + payload: { + source: ['processors', '1', 'onFailure', '0'], + destination: ['processors', '1'], + }, + }); + + expect(s5.processors).toEqual([ + processor1, + { ...processor3, onFailure: [processor4] }, + { ...processor2, onFailure: undefined }, + ]); + }); + + it('moves and orders processors into lists', () => { + const processor1 = { id: expect.any(String), type: 'test1', options: {} }; + const processor2 = { id: expect.any(String), type: 'test2', options: {} }; + const processor3 = { id: expect.any(String), type: 'test3', options: {} }; + const processor4 = { id: expect.any(String), type: 'test4', options: {} }; + + const s1 = reducer(initialState, { + type: 'addProcessor', + payload: { processor: processor1, targetSelector: ['processors'] }, + }); + const s2 = reducer(s1, { + type: 'addProcessor', + payload: { processor: processor2, targetSelector: ['processors'] }, + }); + + const s3 = reducer(s2, { + type: 'addProcessor', + payload: { processor: processor3, targetSelector: ['processors', '1'] }, + }); + + const s4 = reducer(s3, { + type: 'addProcessor', + payload: { + processor: processor4, + targetSelector: ['processors', '1', 'onFailure', '0'], + }, + }); + + expect(s4.processors).toEqual([ + processor1, + { ...processor2, onFailure: [{ ...processor3, onFailure: [processor4] }] }, + ]); + + // Move the first processor to the deepest most on-failure processor's failure processor + const s5 = reducer(s4, { + type: 'moveProcessor', + payload: { + source: ['processors', '0'], + destination: ['processors', '1', 'onFailure', '0', 'onFailure', '0', 'onFailure', '0'], + }, + }); + + expect(s5.processors).toEqual([ + { + ...processor2, + onFailure: [{ ...processor3, onFailure: [{ ...processor4, onFailure: [processor1] }] }], + }, + ]); + }); + + it('handles sending processor to bottom correctly', () => { + const processor1 = { id: expect.any(String), type: 'test1', options: {} }; + const processor2 = { id: expect.any(String), type: 'test2', options: {} }; + const processor3 = { id: expect.any(String), type: 'test3', options: {} }; + + const s1 = reducer(initialState, { + type: 'addProcessor', + payload: { processor: processor1, targetSelector: ['processors'] }, + }); + + const s2 = reducer(s1, { + type: 'addProcessor', + payload: { processor: processor2, targetSelector: ['processors'] }, + }); + + const s3 = reducer(s2, { + type: 'addProcessor', + payload: { processor: processor3, targetSelector: ['processors'] }, + }); + + // Move the parent into a child list + const s4 = reducer(s3, { + type: 'moveProcessor', + payload: { + source: ['processors', '0'], + destination: ['processors', DropSpecialLocations.bottom], + }, + }); + + // Assert nothing changed + expect(s4.processors).toEqual([processor2, processor3, processor1]); + }); + + it('will not set the root "onFailure" to "undefined" if it is empty', () => { + const processor1 = { id: expect.any(String), type: 'test1', options: {} }; + const processor2 = { id: expect.any(String), type: 'test2', options: {} }; + + const s1 = reducer(initialState, { + type: 'addProcessor', + payload: { processor: processor1, targetSelector: ['processors'] }, + }); + + const s2 = reducer(s1, { + type: 'addProcessor', + payload: { processor: processor2, targetSelector: ['onFailure'] }, + }); + + // Move the parent into a child list + const s3 = reducer(s2, { + type: 'moveProcessor', + payload: { + source: ['onFailure', '0'], + destination: ['processors', '1'], + }, + }); + + expect(s3).toEqual({ + processors: [processor1, processor2], + onFailure: [], + isRoot: true, + }); + }); + + it('places copies and places the copied processor below the original', () => { + const processor1 = { id: expect.any(String), type: 'test1', options: {} }; + const processor2 = { id: expect.any(String), type: 'test2', options: {} }; + const processor3 = { id: expect.any(String), type: 'test3', options: {} }; + const processor4 = { + id: expect.any(String), + type: 'test4', + options: { field: 'field_name', value: 'field_value' }, + }; + + const s1 = reducer(initialState, { + type: 'addProcessor', + payload: { processor: processor1, targetSelector: ['processors'] }, + }); + const s2 = reducer(s1, { + type: 'addProcessor', + payload: { processor: processor2, targetSelector: ['processors'] }, + }); + + const s3 = reducer(s2, { + type: 'addProcessor', + payload: { processor: processor3, targetSelector: ['processors', '1'] }, + }); + + const s4 = reducer(s3, { + type: 'addProcessor', + payload: { + processor: processor4, + targetSelector: ['processors', '1', 'onFailure', '0'], + }, + }); + + const s5 = reducer(s4, { + type: 'duplicateProcessor', + payload: { source: ['processors', '1', 'onFailure', '0', 'onFailure', '0'] }, + }); + + const s6 = reducer(s5, { + type: 'duplicateProcessor', + payload: { source: ['processors', '1', 'onFailure', '0', 'onFailure', '0'] }, + }); + + expect(s6.processors).toEqual([ + processor1, + { + ...processor2, + onFailure: [ + { + ...processor3, + onFailure: [processor4, processor4, processor4], + }, + ], + }, + ]); + }); + + describe('Error conditions', () => { + let originalErrorLogger: any; + beforeEach(() => { + // eslint-disable-next-line no-console + originalErrorLogger = console.error; + // eslint-disable-next-line no-console + console.error = jest.fn(); + }); + + afterEach(() => { + // eslint-disable-next-line no-console + console.error = originalErrorLogger; + }); + + it('prevents moving a parent into child list', () => { + const processor1 = { id: expect.any(String), type: 'test1', options: {} }; + const processor2 = { id: expect.any(String), type: 'test2', options: {} }; + const processor3 = { id: expect.any(String), type: 'test3', options: {} }; + const processor4 = { id: expect.any(String), type: 'test4', options: {} }; + + const s1 = reducer(initialState, { + type: 'addProcessor', + payload: { processor: processor1, targetSelector: ['processors'] }, + }); + + const s2 = reducer(s1, { + type: 'addProcessor', + payload: { processor: processor2, targetSelector: ['processors'] }, + }); + + const s3 = reducer(s2, { + type: 'addProcessor', + payload: { processor: processor3, targetSelector: ['processors', '1'] }, + }); + + const s4 = reducer(s3, { + type: 'addProcessor', + payload: { + processor: processor4, + targetSelector: ['processors', '1', 'onFailure', '0'], + }, + }); + + expect(s4.processors).toEqual([ + processor1, + { ...processor2, onFailure: [{ ...processor3, onFailure: [processor4] }] }, + ]); + + // Move the parent into a child list + const s5 = reducer(s4, { + type: 'moveProcessor', + payload: { + source: ['processors', '1'], + destination: ['processors', '1', 'onFailure', '0', 'onFailure', '0', 'onFailure', '0'], + }, + }); + + // eslint-disable-next-line no-console + expect(console.error).toHaveBeenCalledWith(new Error(PARENT_CHILD_NEST_ERROR)); + + // Assert nothing changed + expect(s5.processors).toEqual(s4.processors); + }); + + it('does not remove top level processor and onFailure arrays if they are emptied', () => { + const processor1 = { id: expect.any(String), type: 'test1', options: {} }; + const s1 = reducer(initialState, { + type: 'addProcessor', + payload: { processor: processor1, targetSelector: ['processors'] }, + }); + const s2 = reducer(s1, { + type: 'removeProcessor', + payload: { selector: ['processors', '0'] }, + }); + expect(s2.processors).not.toBe(undefined); + }); + + it('throws for bad move processor', () => { + const processor1 = { id: expect.any(String), type: 'test1', options: {} }; + const processor2 = { id: expect.any(String), type: 'test2', options: {} }; + + const s1 = reducer(initialState, { + type: 'addProcessor', + payload: { processor: processor1, targetSelector: ['processors'] }, + }); + + const s2 = reducer(s1, { + type: 'addProcessor', + payload: { processor: processor2, targetSelector: ['onFailure'] }, + }); + + const s3 = reducer(s2, { + type: 'moveProcessor', + payload: { + source: ['onFailure'], + destination: ['processors'], + }, + }); + + // eslint-disable-next-line no-console + expect(console.error).toHaveBeenCalledWith( + new Error('Expected number but received "processors"') + ); + + expect(s3.processors).toEqual(s2.processors); + }); + }); +}); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/processors_reducer/processors_reducer.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/processors_reducer/processors_reducer.ts new file mode 100644 index 0000000000000..4e069aab8bdd1 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/processors_reducer/processors_reducer.ts @@ -0,0 +1,136 @@ +/* + * 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 uuid from 'uuid'; +import { Reducer, useReducer, Dispatch } from 'react'; +import { DeserializeResult } from '../deserialize'; +import { getValue, setValue } from '../utils'; +import { ProcessorInternal, ProcessorSelector } from '../types'; + +import { unsafeProcessorMove, duplicateProcessor } from './utils'; + +export type State = Omit & { + onFailure: ProcessorInternal[]; + isRoot: true; +}; + +export type Action = + | { + type: 'addProcessor'; + payload: { processor: Omit; targetSelector: ProcessorSelector }; + } + | { + type: 'updateProcessor'; + payload: { processor: ProcessorInternal; selector: ProcessorSelector }; + } + | { + type: 'removeProcessor'; + payload: { selector: ProcessorSelector }; + } + | { + type: 'moveProcessor'; + payload: { source: ProcessorSelector; destination: ProcessorSelector }; + } + | { + type: 'duplicateProcessor'; + payload: { + source: ProcessorSelector; + }; + }; + +export type ProcessorsDispatch = Dispatch; + +export const reducer: Reducer = (state, action) => { + if (action.type === 'moveProcessor') { + const { destination, source } = action.payload; + try { + return unsafeProcessorMove(state, source, destination); + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + return { ...state }; + } + } + + if (action.type === 'removeProcessor') { + const { selector } = action.payload; + const processorsSelector = selector.slice(0, -1); + const parentProcessorSelector = processorsSelector.slice(0, -1); + const idx = parseInt(selector[selector.length - 1], 10); + const processors = getValue(processorsSelector, state); + processors.splice(idx, 1); + const parentProcessor = getValue(parentProcessorSelector, state); + if (!processors.length && selector.length && !(parentProcessor as State).isRoot) { + return setValue(processorsSelector, state, undefined); + } + return setValue(processorsSelector, state, [...processors]); + } + + if (action.type === 'addProcessor') { + const { processor, targetSelector } = action.payload; + if (!targetSelector.length) { + throw new Error('Expected target selector to contain a path, but received an empty array.'); + } + const targetProcessor = getValue( + targetSelector, + state + ); + if (!targetProcessor) { + throw new Error( + `Could not find processor or processors array at ${targetSelector.join('.')}` + ); + } + if (Array.isArray(targetProcessor)) { + return setValue( + targetSelector, + state, + targetProcessor.concat({ ...processor, id: uuid.v4() }) + ); + } else { + const processorWithId = { ...processor, id: uuid.v4() }; + targetProcessor.onFailure = targetProcessor.onFailure + ? targetProcessor.onFailure.concat(processorWithId) + : [processorWithId]; + return setValue(targetSelector, state, targetProcessor); + } + } + + if (action.type === 'updateProcessor') { + const { processor, selector } = action.payload; + const processorsSelector = selector.slice(0, -1); + const idx = parseInt(selector[selector.length - 1], 10); + + if (isNaN(idx)) { + throw new Error(`Expected numeric value, received ${idx}`); + } + + const processors = getValue(processorsSelector, state); + processors[idx] = processor; + return setValue(processorsSelector, state, [...processors]); + } + + if (action.type === 'duplicateProcessor') { + const sourceSelector = action.payload.source; + const sourceProcessor = getValue(sourceSelector, state); + const sourceIdx = parseInt(sourceSelector[sourceSelector.length - 1], 10); + const sourceProcessorsArraySelector = sourceSelector.slice(0, -1); + const sourceProcessorsArray = [ + ...getValue(sourceProcessorsArraySelector, state), + ]; + const copy = duplicateProcessor(sourceProcessor); + sourceProcessorsArray.splice(sourceIdx + 1, 0, copy); + return setValue(sourceProcessorsArraySelector, state, sourceProcessorsArray); + } + + return state; +}; + +export const useProcessorsState = (initialState: DeserializeResult) => { + const state = { + ...initialState, + onFailure: initialState.onFailure ?? [], + }; + return useReducer(reducer, { ...state, isRoot: true }); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/processors_reducer/utils.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/processors_reducer/utils.ts new file mode 100644 index 0000000000000..7cb7d076623aa --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/processors_reducer/utils.ts @@ -0,0 +1,100 @@ +/* + * 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 uuid from 'uuid'; +import { State } from './processors_reducer'; +import { ProcessorInternal, ProcessorSelector } from '../types'; +import { DropSpecialLocations } from '../constants'; +import { checkIfSamePath, getValue } from '../utils'; + +export const PARENT_CHILD_NEST_ERROR = 'PARENT_CHILD_NEST_ERROR'; + +export const duplicateProcessor = (sourceProcessor: ProcessorInternal): ProcessorInternal => { + const onFailure = sourceProcessor.onFailure + ? sourceProcessor.onFailure.map((p) => duplicateProcessor(p)) + : undefined; + return { + ...sourceProcessor, + onFailure, + id: uuid.v4(), + options: { + ...sourceProcessor.options, + }, + }; +}; + +export const isChildPath = (a: ProcessorSelector, b: ProcessorSelector) => { + return a.every((pathSegment, idx) => pathSegment === b[idx]); +}; + +/** + * Unsafe! + * + * This function takes a data structure and mutates it in place. + * + * It is convenient for updating the processors (see {@link ProcessorInternal}) + * structure in this way because the structure is recursive. We are moving processors between + * different arrays, removing in one, and adding to another. The end result should be consistent + * with these actions. + * + * @remark + * This function assumes parents cannot be moved into themselves. + */ +export const unsafeProcessorMove = ( + state: State, + source: ProcessorSelector, + destination: ProcessorSelector +): State => { + const pathToSourceArray = source.slice(0, -1); + const pathToDestArray = destination.slice(0, -1); + if (isChildPath(source, destination)) { + throw new Error(PARENT_CHILD_NEST_ERROR); + } + const isXArrayMove = !checkIfSamePath(pathToSourceArray, pathToDestArray); + + // Start by setting up references to objects of interest using our selectors + // At this point, our selectors are consistent with the data passed in. + const sourceProcessors = getValue(pathToSourceArray, state); + const destinationProcessors = getValue(pathToDestArray, state); + const sourceIndex = parseInt(source[source.length - 1], 10); + const sourceProcessor = getValue(pathToSourceArray.slice(0, -1), state); + const processor = sourceProcessors[sourceIndex]; + + const lastDestItem = destination[destination.length - 1]; + let destIndex: number; + if (lastDestItem === DropSpecialLocations.top) { + destIndex = 0; + } else if (lastDestItem === DropSpecialLocations.bottom) { + destIndex = Infinity; + } else if (/^-?[0-9]+$/.test(lastDestItem)) { + destIndex = parseInt(lastDestItem, 10); + } else { + throw new Error(`Expected number but received "${lastDestItem}"`); + } + + if (isXArrayMove) { + // First perform the add operation. + if (destinationProcessors) { + destinationProcessors.splice(destIndex, 0, processor); + } else { + const targetProcessor = getValue(pathToDestArray.slice(0, -1), state); + targetProcessor.onFailure = [processor]; + } + // !! Beyond this point, selectors are no longer usable because we have mutated the data structure! + // Second, we perform the deletion operation + sourceProcessors.splice(sourceIndex, 1); + + // If onFailure is empty, delete the array. + if (!sourceProcessors.length && !((sourceProcessor as unknown) as State).isRoot) { + delete sourceProcessor.onFailure; + } + } else { + destinationProcessors.splice(destIndex, 0, processor); + const targetIdx = sourceIndex > destIndex ? sourceIndex + 1 : sourceIndex; + sourceProcessors.splice(targetIdx, 1); + } + + return { ...state, processors: [...state.processors], onFailure: [...state.onFailure] }; +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/serialize.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/serialize.ts new file mode 100644 index 0000000000000..153c9e252ccc0 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/serialize.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { Processor } from '../../../../common/types'; + +import { DeserializeResult } from './deserialize'; +import { ProcessorInternal } from './types'; + +type SerializeArgs = DeserializeResult; + +export interface SerializeResult { + processors: Processor[]; + on_failure?: Processor[]; +} + +const convertProcessorInternalToProcessor = (processor: ProcessorInternal): Processor => { + const { options, onFailure, type } = processor; + const outProcessor = { + [type]: { + ...options, + }, + }; + + if (onFailure?.length) { + outProcessor[type].on_failure = convertProcessors(onFailure); + } else if (onFailure) { + outProcessor[type].on_failure = []; + } + + return outProcessor; +}; + +const convertProcessors = (processors: ProcessorInternal[]) => { + const convertedProcessors = []; + + for (const processor of processors) { + convertedProcessors.push(convertProcessorInternalToProcessor(processor)); + } + return convertedProcessors; +}; + +export const serialize = ({ processors, onFailure }: SerializeArgs): SerializeResult => { + return { + processors: convertProcessors(processors), + on_failure: onFailure?.length ? convertProcessors(onFailure) : undefined, + }; +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/types.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/types.ts new file mode 100644 index 0000000000000..aa39fca29fa8b --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/types.ts @@ -0,0 +1,51 @@ +/* + * 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 { OnFormUpdateArg } from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; +import { SerializeResult } from './serialize'; +import { ProcessorInfo } from './components/processors_tree'; + +/** + * An array of keys that map to a value in an object + * structure. + * + * For instance: + * ['a', 'b', '0', 'c'] given { a: { b: [ { c: [] } ] } } => [] + * + * Additionally, an empty selector `[]`, is a special indicator + * for the root level. + */ +export type ProcessorSelector = string[]; + +/** @private */ +export interface ProcessorInternal { + id: string; + type: string; + options: { [key: string]: any }; + onFailure?: ProcessorInternal[]; +} + +export { OnFormUpdateArg }; + +export interface FormValidityState { + validate: OnFormUpdateArg['validate']; +} + +export interface OnUpdateHandlerArg extends FormValidityState { + getData: () => SerializeResult; +} + +/** + * The editor can be in different modes. This enables us to hold + * a reference to data dispatch to the reducer (like the {@link ProcessorSelector} + * which will be used to update the in-memory processors data structure. + */ +export type EditorMode = + | { id: 'creatingProcessor'; arg: { selector: ProcessorSelector } } + | { id: 'movingProcessor'; arg: ProcessorInfo } + | { id: 'editingProcessor'; arg: { processor: ProcessorInternal; selector: ProcessorSelector } } + | { id: 'removingProcessor'; arg: { selector: ProcessorSelector } } + | { id: 'idle' }; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/utils.test.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/utils.test.ts new file mode 100644 index 0000000000000..0b7620f517161 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/utils.test.ts @@ -0,0 +1,36 @@ +/* + * 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 { getValue, setValue } from './utils'; + +describe('get and set values', () => { + const testObject = Object.freeze([{ onFailure: [{ onFailure: 1 }] }]); + describe('#getValue', () => { + it('gets a deeply nested value', () => { + expect(getValue(['0', 'onFailure', '0', 'onFailure'], testObject)).toBe(1); + }); + + it('empty array for path returns "root" value', () => { + const result = getValue([], testObject); + expect(result).toEqual(testObject); + // Getting does not create a copy + expect(result).toBe(testObject); + }); + }); + + describe('#setValue', () => { + it('sets a deeply nested value', () => { + const result = setValue(['0', 'onFailure', '0', 'onFailure'], testObject, 2); + expect(result).toEqual([{ onFailure: [{ onFailure: 2 }] }]); + expect(result).not.toBe(testObject); + }); + + it('returns value if no path was provided', () => { + setValue([], testObject, 2); + expect(testObject).toEqual([{ onFailure: [{ onFailure: 1 }] }]); + }); + }); +}); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/utils.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/utils.ts new file mode 100644 index 0000000000000..49d24e8dc35c3 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/utils.ts @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ProcessorSelector } from './types'; + +type Path = string[]; + +/** + * The below get and set functions are built with an API to make setting + * and getting and setting values more simple. + * + * @remark + * NEVER use these with objects that contain keys created by user input. + */ + +/** + * Given a path, get the value at the path + * + * @remark + * If path is an empty array, return the source. + */ +export const getValue = (path: Path, source: any) => { + let current = source; + for (const key of path) { + current = (current as any)[key]; + } + return (current as unknown) as Result; +}; + +const ARRAY_TYPE = Object.prototype.toString.call([]); +const OBJECT_TYPE = Object.prototype.toString.call({}); + +const dumbCopy = (value: R): R => { + const objectType = Object.prototype.toString.call(value); + if (objectType === ARRAY_TYPE) { + return ([...(value as any)] as unknown) as R; + } else if (objectType === OBJECT_TYPE) { + return { ...(value as any) } as R; + } + + throw new Error(`Expected (${ARRAY_TYPE}|${OBJECT_TYPE}) but received ${objectType}`); +}; + +const WHITELISTED_KEYS_REGEX = /^([0-9]+|onFailure|processors)$/; +/** + * Given a path, value and an object (array or object) set + * the value at the path and copy objects values on the + * path only. This is a partial copy mechanism that is best + * effort for providing state updates to the UI, could break down + * if other updates are made to non-copied parts of state in external + * references - but this should not happen. + * + * @remark + * If path is empty, just shallow copy source. + */ +export const setValue = ( + path: Path, + source: Target, + value: Value +): Target => { + if (!path.length) { + return dumbCopy(source); + } + + let current: any; + let result: Target; + + for (let idx = 0; idx < path.length; ++idx) { + const key = path[idx]; + if (!WHITELISTED_KEYS_REGEX.test(key)) { + // eslint-disable-next-line no-console + console.error( + `Received non-whitelisted key "${key}". Aborting set value operation; returning original.` + ); + return dumbCopy(source); + } + const atRoot = !current; + + if (atRoot) { + result = dumbCopy(source); + current = result; + } + + if (idx + 1 === path.length) { + current[key] = value; + } else { + current[key] = dumbCopy(current[key]); + current = current[key]; + } + } + + return result!; +}; + +export const checkIfSamePath = (pathA: ProcessorSelector, pathB: ProcessorSelector) => { + if (pathA.length !== pathB.length) return false; + return pathA.join('.') === pathB.join('.'); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/table.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/table.tsx index 97775965f9b45..0803b419bdbe4 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/table.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/table.tsx @@ -89,7 +89,7 @@ export const PipelineTable: FunctionComponent = ({ {...reactRouterNavigate(history, '/create')} > {i18n.translate('xpack.ingestPipelines.list.table.createPipelineButtonLabel', { - defaultMessage: 'Create a pipeline here', + defaultMessage: 'Create a pipeline', })} , ], diff --git a/x-pack/plugins/ingest_pipelines/public/shared_imports.ts b/x-pack/plugins/ingest_pipelines/public/shared_imports.ts index ab56ae427120b..9ddb953c71978 100644 --- a/x-pack/plugins/ingest_pipelines/public/shared_imports.ts +++ b/x-pack/plugins/ingest_pipelines/public/shared_imports.ts @@ -29,7 +29,13 @@ export { Form, getUseField, ValidationFuncArg, + FormData, + UseField, + FormHook, useFormContext, + FormDataProvider, + OnFormUpdateArg, + FieldConfig, } from '../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; export { @@ -41,6 +47,9 @@ export { getFormRow, Field, JsonEditorField, + FormRow, + ToggleField, + ComboBoxField, } from '../../../../src/plugins/es_ui_shared/static/forms/components'; export { diff --git a/x-pack/plugins/lens/kibana.json b/x-pack/plugins/lens/kibana.json index a8b22b3e22750..346a5a24c269f 100644 --- a/x-pack/plugins/lens/kibana.json +++ b/x-pack/plugins/lens/kibana.json @@ -13,5 +13,6 @@ "dashboard" ], "optionalPlugins": ["embeddable", "usageCollection", "taskManager", "uiActions"], - "configPath": ["xpack", "lens"] + "configPath": ["xpack", "lens"], + "extraPublicDirs": ["common/constants"] } diff --git a/x-pack/plugins/license_management/kibana.json b/x-pack/plugins/license_management/kibana.json index be28c8e978d8a..6da923c5cff5a 100644 --- a/x-pack/plugins/license_management/kibana.json +++ b/x-pack/plugins/license_management/kibana.json @@ -5,5 +5,6 @@ "ui": true, "requiredPlugins": ["home", "licensing", "management"], "optionalPlugins": ["telemetry"], - "configPath": ["xpack", "license_management"] + "configPath": ["xpack", "license_management"], + "extraPublicDirs": ["common/constants"] } diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.test.ts index 38541e205598b..69702a5e8a4f9 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.test.ts @@ -147,7 +147,7 @@ describe('update_exception_list_item_schema', () => { // TODO: Is it expected behavior for it not to auto-generate a uui or throw // error if item_id is not passed in? - xtest('it should accept an undefined for "item_id" and auto generate a uuid', () => { + test.skip('it should accept an undefined for "item_id" and auto generate a uuid', () => { const inputPayload = getUpdateExceptionListItemSchemaMock(); delete inputPayload.item_id; const decoded = updateExceptionListItemSchema.decode(inputPayload); diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.test.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.test.ts index b9d142fbccbee..ff900104251b7 100644 --- a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.test.ts @@ -101,7 +101,7 @@ describe('exception_list_item_schema', () => { // TODO: Should this throw an error? "namespace_type" gets auto-populated // with default "single", is that desired behavior? - xtest('it should NOT accept an undefined for "namespace_type"', () => { + test.skip('it should NOT accept an undefined for "namespace_type"', () => { const payload = getExceptionListItemSchemaMock(); delete payload.namespace_type; const decoded = exceptionListItemSchema.decode(payload); diff --git a/x-pack/plugins/lists/common/siem_common_deps.ts b/x-pack/plugins/lists/common/siem_common_deps.ts index 3759305987f79..b1bb7d8aace36 100644 --- a/x-pack/plugins/lists/common/siem_common_deps.ts +++ b/x-pack/plugins/lists/common/siem_common_deps.ts @@ -9,3 +9,5 @@ export { DefaultUuid } from '../../security_solution/common/detection_engine/sch export { DefaultStringArray } from '../../security_solution/common/detection_engine/schemas/types/default_string_array'; export { exactCheck } from '../../security_solution/common/exact_check'; export { getPaths, foldLeftRight } from '../../security_solution/common/test_utils'; +export { validate } from '../../security_solution/common/validate'; +export { formatErrors } from '../../security_solution/common/format_errors'; diff --git a/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts index 2cafd435e0853..375d25c6fa5f8 100644 --- a/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { CreateExceptionListItemSchemaDecoded, createExceptionListItemSchema, diff --git a/x-pack/plugins/lists/server/routes/create_exception_list_route.ts b/x-pack/plugins/lists/server/routes/create_exception_list_route.ts index 9be6b72dcd255..bd29a65c9450a 100644 --- a/x-pack/plugins/lists/server/routes/create_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/create_exception_list_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { EXCEPTION_LIST_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { CreateExceptionListSchemaDecoded, createExceptionListSchema, diff --git a/x-pack/plugins/lists/server/routes/create_list_index_route.ts b/x-pack/plugins/lists/server/routes/create_list_index_route.ts index 1c893fb757c5d..5ec2b36da61b0 100644 --- a/x-pack/plugins/lists/server/routes/create_list_index_route.ts +++ b/x-pack/plugins/lists/server/routes/create_list_index_route.ts @@ -6,7 +6,8 @@ import { IRouter } from 'kibana/server'; -import { buildSiemResponse, transformError, validate } from '../siem_server_deps'; +import { buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { LIST_INDEX } from '../../common/constants'; import { acknowledgeSchema } from '../../common/schemas'; diff --git a/x-pack/plugins/lists/server/routes/create_list_item_route.ts b/x-pack/plugins/lists/server/routes/create_list_item_route.ts index 68622e98cbc52..2e1b7fa07221f 100644 --- a/x-pack/plugins/lists/server/routes/create_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/create_list_item_route.ts @@ -7,13 +7,9 @@ import { IRouter } from 'kibana/server'; import { LIST_ITEM_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { createListItemSchema, listItemSchema } from '../../common/schemas'; +import { validate } from '../../common/siem_common_deps'; import { getListClient } from '.'; diff --git a/x-pack/plugins/lists/server/routes/create_list_route.ts b/x-pack/plugins/lists/server/routes/create_list_route.ts index 0f3c404c53cfd..9872bbfa09e23 100644 --- a/x-pack/plugins/lists/server/routes/create_list_route.ts +++ b/x-pack/plugins/lists/server/routes/create_list_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { LIST_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { createListSchema, listSchema } from '../../common/schemas'; import { getListClient } from '.'; diff --git a/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts index 2c91fe3c28681..f363252dada50 100644 --- a/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { DeleteExceptionListItemSchemaDecoded, deleteExceptionListItemSchema, diff --git a/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts b/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts index b4c67c0ab1418..b1bf705dcc5f6 100644 --- a/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { EXCEPTION_LIST_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { DeleteExceptionListSchemaDecoded, deleteExceptionListSchema, diff --git a/x-pack/plugins/lists/server/routes/delete_list_index_route.ts b/x-pack/plugins/lists/server/routes/delete_list_index_route.ts index 424c3f45aac40..cb2e16b3602a7 100644 --- a/x-pack/plugins/lists/server/routes/delete_list_index_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_list_index_route.ts @@ -7,7 +7,8 @@ import { IRouter } from 'kibana/server'; import { LIST_INDEX } from '../../common/constants'; -import { buildSiemResponse, transformError, validate } from '../siem_server_deps'; +import { buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { acknowledgeSchema } from '../../common/schemas'; import { getListClient } from '.'; diff --git a/x-pack/plugins/lists/server/routes/delete_list_item_route.ts b/x-pack/plugins/lists/server/routes/delete_list_item_route.ts index 82dfe8a4f29d0..510be764cefba 100644 --- a/x-pack/plugins/lists/server/routes/delete_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_list_item_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { LIST_ITEM_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { deleteListItemSchema, listItemArraySchema, listItemSchema } from '../../common/schemas'; import { getListClient } from '.'; diff --git a/x-pack/plugins/lists/server/routes/delete_list_route.ts b/x-pack/plugins/lists/server/routes/delete_list_route.ts index e89355b7689c5..600e4b00c29ca 100644 --- a/x-pack/plugins/lists/server/routes/delete_list_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_list_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { LIST_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { deleteListSchema, listSchema } from '../../common/schemas'; import { getListClient } from '.'; diff --git a/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts index 1820ffdeadb88..a6c2a18bb8c8a 100644 --- a/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { FindExceptionListItemSchemaDecoded, findExceptionListItemSchema, diff --git a/x-pack/plugins/lists/server/routes/find_exception_list_route.ts b/x-pack/plugins/lists/server/routes/find_exception_list_route.ts index 3181deda8b91d..97e1de834cd37 100644 --- a/x-pack/plugins/lists/server/routes/find_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/find_exception_list_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { EXCEPTION_LIST_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { FindExceptionListSchemaDecoded, findExceptionListSchema, diff --git a/x-pack/plugins/lists/server/routes/find_list_item_route.ts b/x-pack/plugins/lists/server/routes/find_list_item_route.ts index 37b5fe44b919c..1ccb948d0ad21 100644 --- a/x-pack/plugins/lists/server/routes/find_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/find_list_item_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { LIST_ITEM_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { findListItemSchema, foundListItemSchema } from '../../common/schemas'; import { decodeCursor } from '../services/utils'; diff --git a/x-pack/plugins/lists/server/routes/find_list_route.ts b/x-pack/plugins/lists/server/routes/find_list_route.ts index 04b33e3d67075..2fa43c6368b5c 100644 --- a/x-pack/plugins/lists/server/routes/find_list_route.ts +++ b/x-pack/plugins/lists/server/routes/find_list_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { LIST_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { findListSchema, foundListSchema } from '../../common/schemas'; import { decodeCursor } from '../services/utils'; diff --git a/x-pack/plugins/lists/server/routes/import_list_item_route.ts b/x-pack/plugins/lists/server/routes/import_list_item_route.ts index c951c9b337131..67f345c2c6c1d 100644 --- a/x-pack/plugins/lists/server/routes/import_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/import_list_item_route.ts @@ -9,12 +9,8 @@ import { Readable } from 'stream'; import { IRouter } from 'kibana/server'; import { LIST_ITEM_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { importListItemQuerySchema, importListItemSchema, listSchema } from '../../common/schemas'; import { getListClient } from '.'; diff --git a/x-pack/plugins/lists/server/routes/patch_list_item_route.ts b/x-pack/plugins/lists/server/routes/patch_list_item_route.ts index e18fd0618b133..f706559dffdbd 100644 --- a/x-pack/plugins/lists/server/routes/patch_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/patch_list_item_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { LIST_ITEM_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { listItemSchema, patchListItemSchema } from '../../common/schemas'; import { getListClient } from '.'; diff --git a/x-pack/plugins/lists/server/routes/patch_list_route.ts b/x-pack/plugins/lists/server/routes/patch_list_route.ts index 9d3fa4db8ccd0..3a0d8714a14cd 100644 --- a/x-pack/plugins/lists/server/routes/patch_list_route.ts +++ b/x-pack/plugins/lists/server/routes/patch_list_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { LIST_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { listSchema, patchListSchema } from '../../common/schemas'; import { getListClient } from '.'; diff --git a/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts index 083d4d7a0d479..c4e969b27fcf4 100644 --- a/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { ReadExceptionListItemSchemaDecoded, exceptionListItemSchema, diff --git a/x-pack/plugins/lists/server/routes/read_exception_list_route.ts b/x-pack/plugins/lists/server/routes/read_exception_list_route.ts index c295f045b38c2..6cb91c10aea55 100644 --- a/x-pack/plugins/lists/server/routes/read_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/read_exception_list_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { EXCEPTION_LIST_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { ReadExceptionListSchemaDecoded, exceptionListSchema, diff --git a/x-pack/plugins/lists/server/routes/read_list_index_route.ts b/x-pack/plugins/lists/server/routes/read_list_index_route.ts index 21f539d97fc74..4664bed3e7a8b 100644 --- a/x-pack/plugins/lists/server/routes/read_list_index_route.ts +++ b/x-pack/plugins/lists/server/routes/read_list_index_route.ts @@ -7,7 +7,8 @@ import { IRouter } from 'kibana/server'; import { LIST_INDEX } from '../../common/constants'; -import { buildSiemResponse, transformError, validate } from '../siem_server_deps'; +import { buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { listItemIndexExistSchema } from '../../common/schemas'; import { getListClient } from '.'; diff --git a/x-pack/plugins/lists/server/routes/read_list_item_route.ts b/x-pack/plugins/lists/server/routes/read_list_item_route.ts index 10c7f781f554c..24011d3b50d27 100644 --- a/x-pack/plugins/lists/server/routes/read_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/read_list_item_route.ts @@ -7,13 +7,9 @@ import { IRouter } from 'kibana/server'; import { LIST_ITEM_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { listItemArraySchema, listItemSchema, readListItemSchema } from '../../common/schemas'; +import { validate } from '../../common/siem_common_deps'; import { getListClient } from '.'; diff --git a/x-pack/plugins/lists/server/routes/read_list_route.ts b/x-pack/plugins/lists/server/routes/read_list_route.ts index c30eadfca0b65..34924b70fd4df 100644 --- a/x-pack/plugins/lists/server/routes/read_list_route.ts +++ b/x-pack/plugins/lists/server/routes/read_list_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { LIST_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { listSchema, readListSchema } from '../../common/schemas'; import { getListClient } from '.'; diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts index 73392c326056e..0ec33b7651982 100644 --- a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { UpdateExceptionListItemSchemaDecoded, exceptionListItemSchema, diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_route.ts index fe45d403c040f..cff78614d05ba 100644 --- a/x-pack/plugins/lists/server/routes/update_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/update_exception_list_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { EXCEPTION_LIST_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { UpdateExceptionListSchemaDecoded, exceptionListSchema, diff --git a/x-pack/plugins/lists/server/routes/update_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_list_item_route.ts index 494d57b93b8e4..3e231e319104b 100644 --- a/x-pack/plugins/lists/server/routes/update_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/update_list_item_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { LIST_ITEM_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { listItemSchema, updateListItemSchema } from '../../common/schemas'; import { getListClient } from '.'; diff --git a/x-pack/plugins/lists/server/routes/update_list_route.ts b/x-pack/plugins/lists/server/routes/update_list_route.ts index 6ace61e46a780..a6d9f8329c7c8 100644 --- a/x-pack/plugins/lists/server/routes/update_list_route.ts +++ b/x-pack/plugins/lists/server/routes/update_list_route.ts @@ -7,12 +7,8 @@ import { IRouter } from 'kibana/server'; import { LIST_URL } from '../../common/constants'; -import { - buildRouteValidation, - buildSiemResponse, - transformError, - validate, -} from '../siem_server_deps'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { validate } from '../../common/siem_common_deps'; import { listSchema, updateListSchema } from '../../common/schemas'; import { getListClient } from '.'; diff --git a/x-pack/plugins/lists/server/siem_server_deps.ts b/x-pack/plugins/lists/server/siem_server_deps.ts index df4b07fc46322..87a623c7a1892 100644 --- a/x-pack/plugins/lists/server/siem_server_deps.ts +++ b/x-pack/plugins/lists/server/siem_server_deps.ts @@ -17,5 +17,4 @@ export { createBootstrapIndex, getIndexExists, buildRouteValidation, - validate, } from '../../security_solution/server'; diff --git a/x-pack/plugins/maps/kibana.json b/x-pack/plugins/maps/kibana.json index 67520321de761..f8a30b8d0337e 100644 --- a/x-pack/plugins/maps/kibana.json +++ b/x-pack/plugins/maps/kibana.json @@ -18,5 +18,6 @@ "usageCollection" ], "ui": true, - "server": true + "server": true, + "extraPublicDirs": ["common/constants"] } diff --git a/x-pack/plugins/maps/public/api/create_security_layer_descriptors.ts b/x-pack/plugins/maps/public/api/create_security_layer_descriptors.ts new file mode 100644 index 0000000000000..afff92a584b3c --- /dev/null +++ b/x-pack/plugins/maps/public/api/create_security_layer_descriptors.ts @@ -0,0 +1,16 @@ +/* + * 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 { LayerDescriptor } from '../../common/descriptor_types'; +import { lazyLoadMapModules } from '../lazy_load_bundle'; + +export async function createSecurityLayerDescriptors( + indexPatternId: string, + indexPatternTitle: string +): Promise { + const mapModules = await lazyLoadMapModules(); + return mapModules.createSecurityLayerDescriptors(indexPatternId, indexPatternTitle); +} diff --git a/x-pack/plugins/maps/public/api/index.ts b/x-pack/plugins/maps/public/api/index.ts new file mode 100644 index 0000000000000..8b45d31b41d44 --- /dev/null +++ b/x-pack/plugins/maps/public/api/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { MapsStartApi } from './start_api'; diff --git a/x-pack/plugins/maps/public/api/start_api.ts b/x-pack/plugins/maps/public/api/start_api.ts new file mode 100644 index 0000000000000..d45b0df63c839 --- /dev/null +++ b/x-pack/plugins/maps/public/api/start_api.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { LayerDescriptor } from '../../common/descriptor_types'; + +export interface MapsStartApi { + createSecurityLayerDescriptors: ( + indexPatternId: string, + indexPatternTitle: string + ) => Promise; +} diff --git a/x-pack/plugins/maps/public/classes/layers/load_layer_wizards.ts b/x-pack/plugins/maps/public/classes/layers/load_layer_wizards.ts index 8b6f0a0f3f223..8357971a3778f 100644 --- a/x-pack/plugins/maps/public/classes/layers/load_layer_wizards.ts +++ b/x-pack/plugins/maps/public/classes/layers/load_layer_wizards.ts @@ -25,6 +25,7 @@ import { tmsLayerWizardConfig } from '../sources/xyz_tms_source'; import { wmsLayerWizardConfig } from '../sources/wms_source'; import { mvtVectorSourceWizardConfig } from '../sources/mvt_single_layer_vector_source'; import { ObservabilityLayerWizardConfig } from './solution_layers/observability'; +import { SecurityLayerWizardConfig } from './solution_layers/security'; import { getEnableVectorTiles } from '../../kibana_services'; let registered = false; @@ -36,6 +37,7 @@ export function registerLayerWizards() { // Registration order determines display order registerLayerWizard(uploadLayerWizardConfig); registerLayerWizard(ObservabilityLayerWizardConfig); + registerLayerWizard(SecurityLayerWizardConfig); // @ts-ignore registerLayerWizard(esDocumentsLayerWizardConfig); // @ts-ignore diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts b/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts index ba019f97b287f..85601cfc17e8f 100644 --- a/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts +++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts @@ -40,6 +40,7 @@ import { getDefaultDynamicProperties } from '../../../styles/vector/vector_style // redefining APM constant to avoid making maps app depend on APM plugin export const APM_INDEX_PATTERN_ID = 'apm_static_index_pattern_id'; +export const APM_INDEX_PATTERN_TITLE = 'apm-*'; const defaultDynamicProperties = getDefaultDynamicProperties(); @@ -173,7 +174,7 @@ export function createLayerDescriptor({ type: SOURCE_TYPES.ES_TERM_SOURCE, id: joinId, indexPatternId: APM_INDEX_PATTERN_ID, - indexPatternTitle: 'apm-*', // TODO look up from APM_OSS.indexPattern + indexPatternTitle: APM_INDEX_PATTERN_TITLE, // TODO look up from APM_OSS.indexPattern term: 'client.geo.country_iso_code', metrics: [metricsDescriptor], whereQuery: apmSourceQuery, diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/index.ts b/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/index.ts index ae6ade86de980..abe4eccb91e66 100644 --- a/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/index.ts +++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/index.ts @@ -5,3 +5,4 @@ */ export { ObservabilityLayerWizardConfig } from './observability_layer_wizard'; +export { APM_INDEX_PATTERN_TITLE } from './create_layer_descriptor'; diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.test.ts b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.test.ts new file mode 100644 index 0000000000000..49a86f45a681b --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.test.ts @@ -0,0 +1,688 @@ +/* + * 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. + */ + +jest.mock('../../../../kibana_services', () => { + const mockUiSettings = { + get: () => { + return undefined; + }, + }; + return { + getUiSettings: () => { + return mockUiSettings; + }, + }; +}); + +jest.mock('uuid/v4', () => { + return function () { + return '12345'; + }; +}); + +import { createSecurityLayerDescriptors } from './create_layer_descriptors'; + +describe('createLayerDescriptor', () => { + test('amp index', () => { + expect(createSecurityLayerDescriptors('id', 'apm-*-transaction*')).toEqual([ + { + __dataRequests: [], + alpha: 0.75, + id: '12345', + joins: [], + label: 'apm-*-transaction* | Source Point', + maxZoom: 24, + minZoom: 0, + sourceDescriptor: { + filterByMapBounds: true, + geoField: 'client.geo.location', + id: '12345', + indexPatternId: 'id', + scalingType: 'TOP_HITS', + sortField: '', + sortOrder: 'desc', + tooltipProperties: [ + 'host.name', + 'client.ip', + 'client.domain', + 'client.geo.country_iso_code', + 'client.as.organization.name', + ], + topHitsSize: 1, + topHitsSplitField: 'client.ip', + type: 'ES_SEARCH', + }, + style: { + isTimeAware: true, + properties: { + fillColor: { + options: { + color: '#6092C0', + }, + type: 'STATIC', + }, + icon: { + options: { + value: 'home', + }, + type: 'STATIC', + }, + iconOrientation: { + options: { + orientation: 0, + }, + type: 'STATIC', + }, + iconSize: { + options: { + size: 8, + }, + type: 'STATIC', + }, + labelBorderColor: { + options: { + color: '#FFFFFF', + }, + type: 'STATIC', + }, + labelBorderSize: { + options: { + size: 'SMALL', + }, + }, + labelColor: { + options: { + color: '#000000', + }, + type: 'STATIC', + }, + labelSize: { + options: { + size: 14, + }, + type: 'STATIC', + }, + labelText: { + options: { + value: '', + }, + type: 'STATIC', + }, + lineColor: { + options: { + color: '#FFFFFF', + }, + type: 'STATIC', + }, + lineWidth: { + options: { + size: 2, + }, + type: 'STATIC', + }, + symbolizeAs: { + options: { + value: 'icon', + }, + }, + }, + type: 'VECTOR', + }, + type: 'VECTOR', + visible: true, + }, + { + __dataRequests: [], + alpha: 0.75, + id: '12345', + joins: [], + label: 'apm-*-transaction* | Destination point', + maxZoom: 24, + minZoom: 0, + sourceDescriptor: { + filterByMapBounds: true, + geoField: 'server.geo.location', + id: '12345', + indexPatternId: 'id', + scalingType: 'TOP_HITS', + sortField: '', + sortOrder: 'desc', + tooltipProperties: [ + 'host.name', + 'server.ip', + 'server.domain', + 'server.geo.country_iso_code', + 'server.as.organization.name', + ], + topHitsSize: 1, + topHitsSplitField: 'server.ip', + type: 'ES_SEARCH', + }, + style: { + isTimeAware: true, + properties: { + fillColor: { + options: { + color: '#D36086', + }, + type: 'STATIC', + }, + icon: { + options: { + value: 'marker', + }, + type: 'STATIC', + }, + iconOrientation: { + options: { + orientation: 0, + }, + type: 'STATIC', + }, + iconSize: { + options: { + size: 8, + }, + type: 'STATIC', + }, + labelBorderColor: { + options: { + color: '#FFFFFF', + }, + type: 'STATIC', + }, + labelBorderSize: { + options: { + size: 'SMALL', + }, + }, + labelColor: { + options: { + color: '#000000', + }, + type: 'STATIC', + }, + labelSize: { + options: { + size: 14, + }, + type: 'STATIC', + }, + labelText: { + options: { + value: '', + }, + type: 'STATIC', + }, + lineColor: { + options: { + color: '#FFFFFF', + }, + type: 'STATIC', + }, + lineWidth: { + options: { + size: 2, + }, + type: 'STATIC', + }, + symbolizeAs: { + options: { + value: 'icon', + }, + }, + }, + type: 'VECTOR', + }, + type: 'VECTOR', + visible: true, + }, + { + __dataRequests: [], + alpha: 0.75, + id: '12345', + joins: [], + label: 'apm-*-transaction* | Line', + maxZoom: 24, + minZoom: 0, + sourceDescriptor: { + destGeoField: 'server.geo.location', + id: '12345', + indexPatternId: 'id', + metrics: [ + { + field: 'client.bytes', + type: 'sum', + }, + { + field: 'server.bytes', + type: 'sum', + }, + ], + sourceGeoField: 'client.geo.location', + type: 'ES_PEW_PEW', + }, + style: { + isTimeAware: true, + properties: { + fillColor: { + options: { + color: '#54B399', + }, + type: 'STATIC', + }, + icon: { + options: { + value: 'marker', + }, + type: 'STATIC', + }, + iconOrientation: { + options: { + orientation: 0, + }, + type: 'STATIC', + }, + iconSize: { + options: { + size: 6, + }, + type: 'STATIC', + }, + labelBorderColor: { + options: { + color: '#FFFFFF', + }, + type: 'STATIC', + }, + labelBorderSize: { + options: { + size: 'SMALL', + }, + }, + labelColor: { + options: { + color: '#000000', + }, + type: 'STATIC', + }, + labelSize: { + options: { + size: 14, + }, + type: 'STATIC', + }, + labelText: { + options: { + value: '', + }, + type: 'STATIC', + }, + lineColor: { + options: { + color: '#6092C0', + }, + type: 'STATIC', + }, + lineWidth: { + options: { + field: { + name: 'doc_count', + origin: 'source', + }, + fieldMetaOptions: { + isEnabled: true, + sigma: 3, + }, + maxSize: 8, + minSize: 1, + }, + type: 'DYNAMIC', + }, + symbolizeAs: { + options: { + value: 'circle', + }, + }, + }, + type: 'VECTOR', + }, + type: 'VECTOR', + visible: true, + }, + ]); + }); + + test('non-apm index', () => { + expect(createSecurityLayerDescriptors('id', 'filebeat-*')).toEqual([ + { + __dataRequests: [], + alpha: 0.75, + id: '12345', + joins: [], + label: 'filebeat-* | Source Point', + maxZoom: 24, + minZoom: 0, + sourceDescriptor: { + filterByMapBounds: true, + geoField: 'source.geo.location', + id: '12345', + indexPatternId: 'id', + scalingType: 'TOP_HITS', + sortField: '', + sortOrder: 'desc', + tooltipProperties: [ + 'host.name', + 'source.ip', + 'source.domain', + 'source.geo.country_iso_code', + 'source.as.organization.name', + ], + topHitsSize: 1, + topHitsSplitField: 'source.ip', + type: 'ES_SEARCH', + }, + style: { + isTimeAware: true, + properties: { + fillColor: { + options: { + color: '#6092C0', + }, + type: 'STATIC', + }, + icon: { + options: { + value: 'home', + }, + type: 'STATIC', + }, + iconOrientation: { + options: { + orientation: 0, + }, + type: 'STATIC', + }, + iconSize: { + options: { + size: 8, + }, + type: 'STATIC', + }, + labelBorderColor: { + options: { + color: '#FFFFFF', + }, + type: 'STATIC', + }, + labelBorderSize: { + options: { + size: 'SMALL', + }, + }, + labelColor: { + options: { + color: '#000000', + }, + type: 'STATIC', + }, + labelSize: { + options: { + size: 14, + }, + type: 'STATIC', + }, + labelText: { + options: { + value: '', + }, + type: 'STATIC', + }, + lineColor: { + options: { + color: '#FFFFFF', + }, + type: 'STATIC', + }, + lineWidth: { + options: { + size: 2, + }, + type: 'STATIC', + }, + symbolizeAs: { + options: { + value: 'icon', + }, + }, + }, + type: 'VECTOR', + }, + type: 'VECTOR', + visible: true, + }, + { + __dataRequests: [], + alpha: 0.75, + id: '12345', + joins: [], + label: 'filebeat-* | Destination point', + maxZoom: 24, + minZoom: 0, + sourceDescriptor: { + filterByMapBounds: true, + geoField: 'destination.geo.location', + id: '12345', + indexPatternId: 'id', + scalingType: 'TOP_HITS', + sortField: '', + sortOrder: 'desc', + tooltipProperties: [ + 'host.name', + 'destination.ip', + 'destination.domain', + 'destination.geo.country_iso_code', + 'destination.as.organization.name', + ], + topHitsSize: 1, + topHitsSplitField: 'destination.ip', + type: 'ES_SEARCH', + }, + style: { + isTimeAware: true, + properties: { + fillColor: { + options: { + color: '#D36086', + }, + type: 'STATIC', + }, + icon: { + options: { + value: 'marker', + }, + type: 'STATIC', + }, + iconOrientation: { + options: { + orientation: 0, + }, + type: 'STATIC', + }, + iconSize: { + options: { + size: 8, + }, + type: 'STATIC', + }, + labelBorderColor: { + options: { + color: '#FFFFFF', + }, + type: 'STATIC', + }, + labelBorderSize: { + options: { + size: 'SMALL', + }, + }, + labelColor: { + options: { + color: '#000000', + }, + type: 'STATIC', + }, + labelSize: { + options: { + size: 14, + }, + type: 'STATIC', + }, + labelText: { + options: { + value: '', + }, + type: 'STATIC', + }, + lineColor: { + options: { + color: '#FFFFFF', + }, + type: 'STATIC', + }, + lineWidth: { + options: { + size: 2, + }, + type: 'STATIC', + }, + symbolizeAs: { + options: { + value: 'icon', + }, + }, + }, + type: 'VECTOR', + }, + type: 'VECTOR', + visible: true, + }, + { + __dataRequests: [], + alpha: 0.75, + id: '12345', + joins: [], + label: 'filebeat-* | Line', + maxZoom: 24, + minZoom: 0, + sourceDescriptor: { + destGeoField: 'destination.geo.location', + id: '12345', + indexPatternId: 'id', + metrics: [ + { + field: 'source.bytes', + type: 'sum', + }, + { + field: 'destination.bytes', + type: 'sum', + }, + ], + sourceGeoField: 'source.geo.location', + type: 'ES_PEW_PEW', + }, + style: { + isTimeAware: true, + properties: { + fillColor: { + options: { + color: '#54B399', + }, + type: 'STATIC', + }, + icon: { + options: { + value: 'marker', + }, + type: 'STATIC', + }, + iconOrientation: { + options: { + orientation: 0, + }, + type: 'STATIC', + }, + iconSize: { + options: { + size: 6, + }, + type: 'STATIC', + }, + labelBorderColor: { + options: { + color: '#FFFFFF', + }, + type: 'STATIC', + }, + labelBorderSize: { + options: { + size: 'SMALL', + }, + }, + labelColor: { + options: { + color: '#000000', + }, + type: 'STATIC', + }, + labelSize: { + options: { + size: 14, + }, + type: 'STATIC', + }, + labelText: { + options: { + value: '', + }, + type: 'STATIC', + }, + lineColor: { + options: { + color: '#6092C0', + }, + type: 'STATIC', + }, + lineWidth: { + options: { + field: { + name: 'doc_count', + origin: 'source', + }, + fieldMetaOptions: { + isEnabled: true, + sigma: 3, + }, + maxSize: 8, + minSize: 1, + }, + type: 'DYNAMIC', + }, + symbolizeAs: { + options: { + value: 'circle', + }, + }, + }, + type: 'VECTOR', + }, + type: 'VECTOR', + visible: true, + }, + ]); + }); +}); diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.ts b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.ts new file mode 100644 index 0000000000000..909cd93b3df7a --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.ts @@ -0,0 +1,209 @@ +/* + * 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 minimatch from 'minimatch'; +import { i18n } from '@kbn/i18n'; +import { euiPaletteColorBlind } from '@elastic/eui'; +import { + LayerDescriptor, + SizeDynamicOptions, + VectorStylePropertiesDescriptor, +} from '../../../../../common/descriptor_types'; +import { + AGG_TYPE, + COUNT_PROP_NAME, + FIELD_ORIGIN, + SCALING_TYPES, + STYLE_TYPE, + SYMBOLIZE_AS_TYPES, + VECTOR_STYLES, +} from '../../../../../common/constants'; +import { VectorLayer } from '../../vector_layer/vector_layer'; +import { VectorStyle } from '../../../styles/vector/vector_style'; +// @ts-ignore +import { ESSearchSource } from '../../../sources/es_search_source'; +// @ts-ignore +import { ESPewPewSource } from '../../../sources/es_pew_pew_source'; +import { getDefaultDynamicProperties } from '../../../styles/vector/vector_style_defaults'; +import { APM_INDEX_PATTERN_TITLE } from '../observability'; + +const defaultDynamicProperties = getDefaultDynamicProperties(); +const euiVisColorPalette = euiPaletteColorBlind(); + +function isApmIndex(indexPatternTitle: string) { + return minimatch(indexPatternTitle, APM_INDEX_PATTERN_TITLE); +} + +function getSourceField(indexPatternTitle: string) { + return isApmIndex(indexPatternTitle) ? 'client.geo.location' : 'source.geo.location'; +} + +function getDestinationField(indexPatternTitle: string) { + return isApmIndex(indexPatternTitle) ? 'server.geo.location' : 'destination.geo.location'; +} + +function createSourceLayerDescriptor(indexPatternId: string, indexPatternTitle: string) { + const sourceDescriptor = ESSearchSource.createDescriptor({ + indexPatternId, + geoField: getSourceField(indexPatternTitle), + scalingType: SCALING_TYPES.TOP_HITS, + topHitsSplitField: isApmIndex(indexPatternTitle) ? 'client.ip' : 'source.ip', + tooltipProperties: isApmIndex(indexPatternTitle) + ? [ + 'host.name', + 'client.ip', + 'client.domain', + 'client.geo.country_iso_code', + 'client.as.organization.name', + ] + : [ + 'host.name', + 'source.ip', + 'source.domain', + 'source.geo.country_iso_code', + 'source.as.organization.name', + ], + }); + + const styleProperties: VectorStylePropertiesDescriptor = { + [VECTOR_STYLES.FILL_COLOR]: { + type: STYLE_TYPE.STATIC, + options: { color: euiVisColorPalette[1] }, + }, + [VECTOR_STYLES.LINE_COLOR]: { + type: STYLE_TYPE.STATIC, + options: { color: '#FFFFFF' }, + }, + [VECTOR_STYLES.LINE_WIDTH]: { type: STYLE_TYPE.STATIC, options: { size: 2 } }, + [VECTOR_STYLES.SYMBOLIZE_AS]: { + options: { value: SYMBOLIZE_AS_TYPES.ICON }, + }, + [VECTOR_STYLES.ICON]: { + type: STYLE_TYPE.STATIC, + options: { value: 'home' }, + }, + [VECTOR_STYLES.ICON_SIZE]: { type: STYLE_TYPE.STATIC, options: { size: 8 } }, + }; + + return VectorLayer.createDescriptor({ + label: i18n.translate('xpack.maps.sescurity.sourceLayerLabel', { + defaultMessage: '{indexPatternTitle} | Source Point', + values: { indexPatternTitle }, + }), + sourceDescriptor, + style: VectorStyle.createDescriptor(styleProperties), + }); +} + +function createDestinationLayerDescriptor(indexPatternId: string, indexPatternTitle: string) { + const sourceDescriptor = ESSearchSource.createDescriptor({ + indexPatternId, + geoField: getDestinationField(indexPatternTitle), + scalingType: SCALING_TYPES.TOP_HITS, + topHitsSplitField: isApmIndex(indexPatternTitle) ? 'server.ip' : 'destination.ip', + tooltipProperties: isApmIndex(indexPatternTitle) + ? [ + 'host.name', + 'server.ip', + 'server.domain', + 'server.geo.country_iso_code', + 'server.as.organization.name', + ] + : [ + 'host.name', + 'destination.ip', + 'destination.domain', + 'destination.geo.country_iso_code', + 'destination.as.organization.name', + ], + }); + + const styleProperties: VectorStylePropertiesDescriptor = { + [VECTOR_STYLES.FILL_COLOR]: { + type: STYLE_TYPE.STATIC, + options: { color: euiVisColorPalette[2] }, + }, + [VECTOR_STYLES.LINE_COLOR]: { + type: STYLE_TYPE.STATIC, + options: { color: '#FFFFFF' }, + }, + [VECTOR_STYLES.LINE_WIDTH]: { type: STYLE_TYPE.STATIC, options: { size: 2 } }, + [VECTOR_STYLES.SYMBOLIZE_AS]: { + options: { value: SYMBOLIZE_AS_TYPES.ICON }, + }, + [VECTOR_STYLES.ICON]: { + type: STYLE_TYPE.STATIC, + options: { value: 'marker' }, + }, + [VECTOR_STYLES.ICON_SIZE]: { type: STYLE_TYPE.STATIC, options: { size: 8 } }, + }; + + return VectorLayer.createDescriptor({ + label: i18n.translate('xpack.maps.sescurity.destinationLayerLabel', { + defaultMessage: '{indexPatternTitle} | Destination point', + values: { indexPatternTitle }, + }), + sourceDescriptor, + style: VectorStyle.createDescriptor(styleProperties), + }); +} + +function createLineLayerDescriptor(indexPatternId: string, indexPatternTitle: string) { + const sourceDescriptor = ESPewPewSource.createDescriptor({ + indexPatternId, + sourceGeoField: getSourceField(indexPatternTitle), + destGeoField: getDestinationField(indexPatternTitle), + metrics: [ + { + type: AGG_TYPE.SUM, + field: isApmIndex(indexPatternTitle) ? 'client.bytes' : 'source.bytes', + }, + { + type: AGG_TYPE.SUM, + field: isApmIndex(indexPatternTitle) ? 'server.bytes' : 'destination.bytes', + }, + ], + }); + + const styleProperties: VectorStylePropertiesDescriptor = { + [VECTOR_STYLES.LINE_COLOR]: { + type: STYLE_TYPE.STATIC, + options: { color: euiVisColorPalette[1] }, + }, + [VECTOR_STYLES.LINE_WIDTH]: { + type: STYLE_TYPE.DYNAMIC, + options: { + ...(defaultDynamicProperties[VECTOR_STYLES.LINE_WIDTH]!.options as SizeDynamicOptions), + field: { + name: COUNT_PROP_NAME, + origin: FIELD_ORIGIN.SOURCE, + }, + minSize: 1, + maxSize: 8, + }, + }, + }; + + return VectorLayer.createDescriptor({ + label: i18n.translate('xpack.maps.sescurity.lineLayerLabel', { + defaultMessage: '{indexPatternTitle} | Line', + values: { indexPatternTitle }, + }), + sourceDescriptor, + style: VectorStyle.createDescriptor(styleProperties), + }); +} + +export function createSecurityLayerDescriptors( + indexPatternId: string, + indexPatternTitle: string +): LayerDescriptor[] { + return [ + createSourceLayerDescriptor(indexPatternId, indexPatternTitle), + createDestinationLayerDescriptor(indexPatternId, indexPatternTitle), + createLineLayerDescriptor(indexPatternId, indexPatternTitle), + ]; +} diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/index.ts b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/index.ts new file mode 100644 index 0000000000000..f0cc4f26fc1dd --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/index.ts @@ -0,0 +1,8 @@ +/* + * 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 { createSecurityLayerDescriptors } from './create_layer_descriptors'; +export { SecurityLayerWizardConfig } from './security_layer_wizard'; diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/index_pattern_select.tsx b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/index_pattern_select.tsx new file mode 100644 index 0000000000000..1e4d9a57a336c --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/index_pattern_select.tsx @@ -0,0 +1,96 @@ +/* + * 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, { ChangeEvent, Component } from 'react'; +import { EuiFormRow, EuiSelect, EuiSelectOption } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { getSecurityIndexPatterns, IndexPatternMeta } from './security_index_pattern_utils'; + +interface Props { + value: string; + onChange: (indexPatternMeta: IndexPatternMeta | null) => void; +} + +interface State { + hasLoaded: boolean; + options: EuiSelectOption[]; +} + +export class IndexPatternSelect extends Component { + private _isMounted: boolean = false; + + state = { + hasLoaded: false, + options: [], + }; + + componentWillUnmount() { + this._isMounted = false; + } + + componentDidMount() { + this._isMounted = true; + this._loadOptions(); + } + + async _loadOptions() { + const indexPatterns = await getSecurityIndexPatterns(); + if (!this._isMounted) { + return; + } + + this.setState({ + hasLoaded: true, + options: [ + { value: '', text: '' }, + ...indexPatterns.map(({ id, title }: IndexPatternMeta) => { + return { + value: id, + text: title, + }; + }), + ], + }); + } + + _onChange = (event: ChangeEvent) => { + const targetOption = this.state.options.find(({ value, text }: EuiSelectOption) => { + return event.target.value === value; + }); + + if (event.target.value === '' || !targetOption) { + this.props.onChange(null); + return; + } + + this.props.onChange({ + // @ts-expect-error - avoid wrong "Property does not exist on type 'never'." compile error + id: targetOption.value, + // @ts-expect-error - avoid wrong "Property does not exist on type 'never'." compile error + title: targetOption.text, + }); + }; + + render() { + if (!this.state.hasLoaded) { + return null; + } + + return ( + + + + ); + } +} diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/security_index_pattern_utils.ts b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/security_index_pattern_utils.ts new file mode 100644 index 0000000000000..141b9133505b7 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/security_index_pattern_utils.ts @@ -0,0 +1,44 @@ +/* + * 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. + */ +/* eslint-disable @typescript-eslint/consistent-type-definitions */ + +import minimatch from 'minimatch'; +import { SimpleSavedObject } from 'src/core/public'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { IndexPatternSavedObjectAttrs } from 'src/plugins/data/common/index_patterns/index_patterns/index_patterns'; +import { getIndexPatternService, getUiSettings } from '../../../../kibana_services'; + +export type IndexPatternMeta = { + id: string; + title: string; +}; + +export async function getSecurityIndexPatterns(): Promise { + const uiSettings = getUiSettings(); + let securityIndexPatternTitles: string[]; + try { + securityIndexPatternTitles = uiSettings.get('securitySolution:defaultIndex'); + } catch (error) { + // UiSettings throws with unreconized configuration setting + // siem:defaultIndex configuration setting is not registered if security app is not running + return []; + } + + const indexPatternCache = await getIndexPatternService().getCache(); + return indexPatternCache! + .filter((savedObject: SimpleSavedObject) => { + return (securityIndexPatternTitles as string[]).some((indexPatternTitle) => { + // glob matching index pattern title + return minimatch(indexPatternTitle, savedObject?.attributes?.title); + }); + }) + .map((savedObject: SimpleSavedObject) => { + return { + id: savedObject.id, + title: savedObject.attributes.title, + }; + }); +} diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/security_layer_template.tsx b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/security_layer_template.tsx new file mode 100644 index 0000000000000..eda489c88fda2 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/security_layer_template.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Component, Fragment } from 'react'; +import { RenderWizardArguments } from '../../layer_wizard_registry'; +import { IndexPatternSelect } from './index_pattern_select'; +import { createSecurityLayerDescriptors } from './create_layer_descriptors'; +import { IndexPatternMeta } from './security_index_pattern_utils'; + +interface State { + indexPatternId: string | null; + indexPatternTitle: string | null; +} + +export class SecurityLayerTemplate extends Component { + state = { + indexPatternId: null, + indexPatternTitle: null, + }; + + _onIndexPatternChange = (indexPatternMeta: IndexPatternMeta | null) => { + this.setState( + { + indexPatternId: indexPatternMeta ? indexPatternMeta.id : null, + indexPatternTitle: indexPatternMeta ? indexPatternMeta.title : null, + }, + this._previewLayer + ); + }; + + _previewLayer() { + if (!this.state.indexPatternId || !this.state.indexPatternTitle) { + this.props.previewLayers([]); + return; + } + + this.props.previewLayers( + createSecurityLayerDescriptors(this.state.indexPatternId!, this.state.indexPatternTitle!) + ); + } + + render() { + return ( + + + + ); + } +} diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/security_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/security_layer_wizard.tsx new file mode 100644 index 0000000000000..cece00fa37350 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/security_layer_wizard.tsx @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { LayerWizard, RenderWizardArguments } from '../../layer_wizard_registry'; +import { getSecurityIndexPatterns } from './security_index_pattern_utils'; +import { SecurityLayerTemplate } from './security_layer_template'; + +export const SecurityLayerWizardConfig: LayerWizard = { + checkVisibility: async () => { + const indexPatterns = await getSecurityIndexPatterns(); + return indexPatterns.length > 0; + }, + description: i18n.translate('xpack.maps.security.desc', { + defaultMessage: 'Security layers', + }), + icon: 'logoSecurity', + renderWizard: (renderWizardArguments: RenderWizardArguments) => { + return ; + }, + title: i18n.translate('xpack.maps.security.title', { + defaultMessage: 'Security', + }), +}; 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 0d15cff032410..fda73bc0f73a0 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 @@ -26,13 +26,14 @@ export const sourceTitle = i18n.translate('xpack.maps.source.pewPewTitle', { export class ESPewPewSource extends AbstractESAggSource { static type = SOURCE_TYPES.ES_PEW_PEW; - static createDescriptor({ indexPatternId, sourceGeoField, destGeoField }) { + static createDescriptor({ indexPatternId, sourceGeoField, destGeoField, metrics }) { return { type: ESPewPewSource.type, id: uuid(), indexPatternId: indexPatternId, sourceGeoField, destGeoField, + metrics: metrics ? metrics : [], }; } diff --git a/x-pack/plugins/maps/public/connected_components/add_layer_panel/flyout_body/layer_wizard_select.tsx b/x-pack/plugins/maps/public/connected_components/add_layer_panel/flyout_body/layer_wizard_select.tsx index 2b1bbfa81c743..b0c50133ceabb 100644 --- a/x-pack/plugins/maps/public/connected_components/add_layer_panel/flyout_body/layer_wizard_select.tsx +++ b/x-pack/plugins/maps/public/connected_components/add_layer_panel/flyout_body/layer_wizard_select.tsx @@ -7,6 +7,7 @@ import _ from 'lodash'; import React, { Component, Fragment } from 'react'; import { EuiSpacer, EuiCard, EuiIcon } from '@elastic/eui'; +import { EuiLoadingContent } from '@elastic/eui'; import { getLayerWizards, LayerWizard } from '../../../classes/layers/layer_wizard_registry'; interface Props { @@ -15,6 +16,7 @@ interface Props { interface State { layerWizards: LayerWizard[]; + hasLoadedWizards: boolean; } export class LayerWizardSelect extends Component { @@ -22,6 +24,7 @@ export class LayerWizardSelect extends Component { state = { layerWizards: [], + hasLoadedWizards: false, }; componentDidMount() { @@ -36,11 +39,18 @@ export class LayerWizardSelect extends Component { async _loadLayerWizards() { const layerWizards = await getLayerWizards(); if (this._isMounted) { - this.setState({ layerWizards }); + this.setState({ layerWizards, hasLoadedWizards: true }); } } render() { + if (!this.state.hasLoadedWizards) { + return ( +
+ } layout="horizontal" /> +
+ ); + } return this.state.layerWizards.map((layerWizard: LayerWizard) => { const icon = layerWizard.icon ? : undefined; diff --git a/x-pack/plugins/maps/public/index.ts b/x-pack/plugins/maps/public/index.ts index 9428946bb62e0..6a144e84b05e0 100644 --- a/x-pack/plugins/maps/public/index.ts +++ b/x-pack/plugins/maps/public/index.ts @@ -18,3 +18,4 @@ export const plugin: PluginInitializer = ( export { MAP_SAVED_OBJECT_TYPE } from '../common/constants'; export { ITooltipProperty } from './classes/tooltips/tooltip_property'; +export { MapsPluginStart } from './plugin'; diff --git a/x-pack/plugins/maps/public/lazy_load_bundle/index.ts b/x-pack/plugins/maps/public/lazy_load_bundle/index.ts index 92cefd76aa047..152412376fb09 100644 --- a/x-pack/plugins/maps/public/lazy_load_bundle/index.ts +++ b/x-pack/plugins/maps/public/lazy_load_bundle/index.ts @@ -36,6 +36,10 @@ interface LazyLoadedMapModules { initialLayers?: LayerDescriptor[] ) => LayerDescriptor[]; mergeInputWithSavedMap: any; + createSecurityLayerDescriptors: ( + indexPatternId: string, + indexPatternTitle: string + ) => LayerDescriptor[]; } export async function lazyLoadMapModules(): Promise { @@ -56,6 +60,7 @@ export async function lazyLoadMapModules(): Promise { addLayerWithoutDataSync, getInitialLayers, mergeInputWithSavedMap, + createSecurityLayerDescriptors, } = await import('./lazy'); resolve({ @@ -69,6 +74,7 @@ export async function lazyLoadMapModules(): Promise { addLayerWithoutDataSync, getInitialLayers, mergeInputWithSavedMap, + createSecurityLayerDescriptors, }); }); return loadModulesPromise; diff --git a/x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts b/x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts index b650678b3105c..0600b8d5073c6 100644 --- a/x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts +++ b/x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts @@ -16,3 +16,4 @@ export * from '../../actions'; export * from '../../selectors/map_selectors'; export * from '../../angular/get_initial_layers'; export * from '../../embeddable/merge_input_with_saved_map'; +export * from '../../classes/layers/solution_layers/security'; diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 319be46870ebc..e0639c9d0f365 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -46,6 +46,8 @@ import { MapEmbeddableFactory } from './embeddable/map_embeddable_factory'; import { EmbeddableSetup } from '../../../../src/plugins/embeddable/public'; import { MapsConfigType, MapsXPackConfig } from '../config'; import { ILicense } from '../../licensing/common/types'; +import { MapsStartApi } from './api'; +import { createSecurityLayerDescriptors } from './api/create_security_layer_descriptors'; export interface MapsPluginSetupDependencies { inspector: InspectorSetupContract; @@ -143,7 +145,10 @@ export class MapsPlugin }; } - public start(core: CoreStart, plugins: any) { + public start(core: CoreStart, plugins: any): MapsStartApi { bindStartCoreAndPlugins(core, plugins); + return { + createSecurityLayerDescriptors, + }; } } diff --git a/x-pack/plugins/ml/common/constants/time_format.ts b/x-pack/plugins/ml/common/constants/time_format.ts new file mode 100644 index 0000000000000..109dad2d40ac8 --- /dev/null +++ b/x-pack/plugins/ml/common/constants/time_format.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; diff --git a/x-pack/plugins/ml/common/types/anomaly_detection_jobs/index.ts b/x-pack/plugins/ml/common/types/anomaly_detection_jobs/index.ts index 9c299c628426a..deb41fbb832cd 100644 --- a/x-pack/plugins/ml/common/types/anomaly_detection_jobs/index.ts +++ b/x-pack/plugins/ml/common/types/anomaly_detection_jobs/index.ts @@ -10,3 +10,4 @@ export * from './datafeed'; export * from './datafeed_stats'; export * from './combined_job'; export * from './summary_job'; +export * from './model_snapshot'; diff --git a/x-pack/plugins/ml/common/types/anomaly_detection_jobs/model_snapshot.ts b/x-pack/plugins/ml/common/types/anomaly_detection_jobs/model_snapshot.ts new file mode 100644 index 0000000000000..367a16965a90b --- /dev/null +++ b/x-pack/plugins/ml/common/types/anomaly_detection_jobs/model_snapshot.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { JobId } from './job'; +import { ModelSizeStats } from './job_stats'; + +export interface ModelSnapshot { + job_id: JobId; + min_version: string; + timestamp: number; + description: string; + snapshot_id: string; + snapshot_doc_count: number; + model_size_stats: ModelSizeStats; + latest_record_time_stamp: number; + latest_result_time_stamp: number; + retain: boolean; +} diff --git a/x-pack/plugins/ml/public/application/components/annotations/annotations_table/annotations_table.js b/x-pack/plugins/ml/public/application/components/annotations/annotations_table/annotations_table.js index 52d266cde1a2c..a091da6c359d1 100644 --- a/x-pack/plugins/ml/public/application/components/annotations/annotations_table/annotations_table.js +++ b/x-pack/plugins/ml/public/application/components/annotations/annotations_table/annotations_table.js @@ -43,6 +43,7 @@ import { getLatestDataOrBucketTimestamp, isTimeSeriesViewJob, } from '../../../../../common/util/job_utils'; +import { TIME_FORMAT } from '../../../../../common/constants/time_format'; import { annotation$, @@ -50,8 +51,6 @@ import { annotationsRefreshed, } from '../../../services/annotations_service'; -const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; - /** * Table component for rendering the lists of annotations for an ML job. */ diff --git a/x-pack/plugins/ml/public/application/components/color_range_legend/index.ts b/x-pack/plugins/ml/public/application/components/color_range_legend/index.ts index 93a1ec40f1d5e..8c92f47e5aa07 100644 --- a/x-pack/plugins/ml/public/application/components/color_range_legend/index.ts +++ b/x-pack/plugins/ml/public/application/components/color_range_legend/index.ts @@ -11,4 +11,5 @@ export { useColorRange, COLOR_RANGE, COLOR_RANGE_SCALE, + useCurrentEuiTheme, } from './use_color_range'; diff --git a/x-pack/plugins/ml/public/application/components/color_range_legend/use_color_range.ts b/x-pack/plugins/ml/public/application/components/color_range_legend/use_color_range.ts index 1d5f5cf3a0309..f674372da6785 100644 --- a/x-pack/plugins/ml/public/application/components/color_range_legend/use_color_range.ts +++ b/x-pack/plugins/ml/public/application/components/color_range_legend/use_color_range.ts @@ -5,7 +5,7 @@ */ import d3 from 'd3'; - +import { useMemo } from 'react'; import euiThemeLight from '@elastic/eui/dist/eui_theme_light.json'; import euiThemeDark from '@elastic/eui/dist/eui_theme_dark.json'; @@ -150,7 +150,7 @@ export const useColorRange = ( colorRangeScale = COLOR_RANGE_SCALE.LINEAR, featureCount = 1 ) => { - const euiTheme = useUiSettings().get('theme:darkMode') ? euiThemeDark : euiThemeLight; + const { euiTheme } = useCurrentEuiTheme(); const colorRanges: Record = { [COLOR_RANGE.BLUE]: [ @@ -186,3 +186,11 @@ export const useColorRange = ( return scaleTypes[colorRangeScale]; }; + +export function useCurrentEuiTheme() { + const uiSettings = useUiSettings(); + return useMemo( + () => ({ euiTheme: uiSettings.get('theme:darkMode') ? euiThemeDark : euiThemeLight }), + [uiSettings] + ); +} diff --git a/x-pack/plugins/ml/public/application/components/job_messages/job_messages.tsx b/x-pack/plugins/ml/public/application/components/job_messages/job_messages.tsx index fd2b7902833a6..798ceae0f0732 100644 --- a/x-pack/plugins/ml/public/application/components/job_messages/job_messages.tsx +++ b/x-pack/plugins/ml/public/application/components/job_messages/job_messages.tsx @@ -13,10 +13,9 @@ import { i18n } from '@kbn/i18n'; import theme from '@elastic/eui/dist/eui_theme_light.json'; import { JobMessage } from '../../../../common/types/audit_message'; +import { TIME_FORMAT } from '../../../../common/constants/time_format'; import { JobIcon } from '../job_message_icon'; -const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; - interface JobMessagesProps { messages: JobMessage[]; loading: boolean; diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/close_job_confirm/close_job_confirm.tsx b/x-pack/plugins/ml/public/application/components/model_snapshots/close_job_confirm/close_job_confirm.tsx new file mode 100644 index 0000000000000..8716f8c85f208 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/close_job_confirm/close_job_confirm.tsx @@ -0,0 +1,79 @@ +/* + * 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, { FC } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { FormattedMessage } from '@kbn/i18n/react'; + +import { EuiOverlayMask, EuiConfirmModal } from '@elastic/eui'; + +import { COMBINED_JOB_STATE } from '../model_snapshots_table'; + +interface Props { + combinedJobState: COMBINED_JOB_STATE; + hideCloseJobModalVisible(): void; + forceCloseJob(): void; +} +export const CloseJobConfirm: FC = ({ + combinedJobState, + hideCloseJobModalVisible, + forceCloseJob, +}) => { + return ( + + +

+ {combinedJobState === COMBINED_JOB_STATE.OPEN_AND_RUNNING && ( + + )} + {combinedJobState === COMBINED_JOB_STATE.OPEN_AND_STOPPED && ( + + )} +
+ +

+
+
+ ); +}; diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/close_job_confirm/index.ts b/x-pack/plugins/ml/public/application/components/model_snapshots/close_job_confirm/index.ts new file mode 100644 index 0000000000000..195d14160b5e6 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/close_job_confirm/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { CloseJobConfirm } from './close_job_confirm'; diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/edit_model_snapshot_flyout/edit_model_snapshot_flyout.tsx b/x-pack/plugins/ml/public/application/components/model_snapshots/edit_model_snapshot_flyout/edit_model_snapshot_flyout.tsx new file mode 100644 index 0000000000000..1e99fd12a9fa6 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/edit_model_snapshot_flyout/edit_model_snapshot_flyout.tsx @@ -0,0 +1,218 @@ +/* + * 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. + */ + +/* + * 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, { FC, useCallback, useState, useEffect } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiFlyout, + EuiFlyoutFooter, + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiButtonEmpty, + EuiTitle, + EuiFlyoutBody, + EuiSpacer, + EuiTextArea, + EuiFormRow, + EuiSwitch, + EuiConfirmModal, + EuiOverlayMask, + EuiCallOut, +} from '@elastic/eui'; + +import { + ModelSnapshot, + CombinedJobWithStats, +} from '../../../../../common/types/anomaly_detection_jobs'; +import { ml } from '../../../services/ml_api_service'; +import { useNotifications } from '../../../contexts/kibana'; + +interface Props { + snapshot: ModelSnapshot; + job: CombinedJobWithStats; + closeFlyout(reload: boolean): void; +} + +export const EditModelSnapshotFlyout: FC = ({ snapshot, job, closeFlyout }) => { + const { toasts } = useNotifications(); + const [description, setDescription] = useState(snapshot.description); + const [retain, setRetain] = useState(snapshot.retain); + const [deleteModalVisible, setDeleteModalVisible] = useState(false); + const [isCurrentSnapshot, setIsCurrentSnapshot] = useState( + snapshot.snapshot_id === job.model_snapshot_id + ); + + useEffect(() => { + setIsCurrentSnapshot(snapshot.snapshot_id === job.model_snapshot_id); + }, [snapshot]); + + const updateSnapshot = useCallback(async () => { + try { + await ml.updateModelSnapshot(snapshot.job_id, snapshot.snapshot_id, { + description, + retain, + }); + closeWithReload(); + } catch (error) { + toasts.addError(new Error(error.body.message), { + title: i18n.translate('xpack.ml.editModelSnapshotFlyout.saveErrorTitle', { + defaultMessage: 'Model snapshot update failed', + }), + }); + } + }, [retain, description, snapshot]); + + const deleteSnapshot = useCallback(async () => { + try { + await ml.deleteModelSnapshot(snapshot.job_id, snapshot.snapshot_id); + hideDeleteModal(); + closeWithReload(); + } catch (error) { + toasts.addError(new Error(error.body.message), { + title: i18n.translate('xpack.ml.editModelSnapshotFlyout.deleteErrorTitle', { + defaultMessage: 'Model snapshot deletion failed', + }), + }); + } + }, [snapshot]); + + function closeWithReload() { + closeFlyout(true); + } + function closeWithoutReload() { + closeFlyout(false); + } + function showDeleteModal() { + setDeleteModalVisible(true); + } + function hideDeleteModal() { + setDeleteModalVisible(false); + } + + return ( + <> + + + + +
+ +
+
+ + {isCurrentSnapshot && ( + <> + + + + + + )} + + + + + setDescription(e.target.value)} + /> + + + + setRetain(e.target.checked)} + /> + +
+
+ + + + + + + + + + + + + + + + + + + + +
+ + {deleteModalVisible && ( + + + + )} + + ); +}; diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/edit_model_snapshot_flyout/index.ts b/x-pack/plugins/ml/public/application/components/model_snapshots/edit_model_snapshot_flyout/index.ts new file mode 100644 index 0000000000000..fcb534620e438 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/edit_model_snapshot_flyout/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { EditModelSnapshotFlyout } from './edit_model_snapshot_flyout'; diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/index.ts b/x-pack/plugins/ml/public/application/components/model_snapshots/index.ts new file mode 100644 index 0000000000000..e16d69ea3eb83 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { ModelSnapshotTable } from './model_snapshots_table'; diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/model_snapshots_table.tsx b/x-pack/plugins/ml/public/application/components/model_snapshots/model_snapshots_table.tsx new file mode 100644 index 0000000000000..64fdd97903b60 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/model_snapshots_table.tsx @@ -0,0 +1,267 @@ +/* + * 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, { FC, useEffect, useCallback, useState } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiInMemoryTable, + EuiLoadingSpinner, + EuiBasicTableColumn, + formatDate, +} from '@elastic/eui'; + +import { checkPermission } from '../../capabilities/check_capabilities'; +import { EditModelSnapshotFlyout } from './edit_model_snapshot_flyout'; +import { RevertModelSnapshotFlyout } from './revert_model_snapshot_flyout'; +import { ml } from '../../services/ml_api_service'; +import { JOB_STATE, DATAFEED_STATE } from '../../../../common/constants/states'; +import { TIME_FORMAT } from '../../../../common/constants/time_format'; +import { CloseJobConfirm } from './close_job_confirm'; +import { + ModelSnapshot, + CombinedJobWithStats, +} from '../../../../common/types/anomaly_detection_jobs'; + +interface Props { + job: CombinedJobWithStats; + refreshJobList: () => void; +} + +export enum COMBINED_JOB_STATE { + OPEN_AND_RUNNING, + OPEN_AND_STOPPED, + CLOSED, + UNKNOWN, +} + +export const ModelSnapshotTable: FC = ({ job, refreshJobList }) => { + const canCreateJob = checkPermission('canCreateJob'); + const canStartStopDatafeed = checkPermission('canStartStopDatafeed'); + + const [snapshots, setSnapshots] = useState([]); + const [snapshotsLoaded, setSnapshotsLoaded] = useState(false); + const [editSnapshot, setEditSnapshot] = useState(null); + const [revertSnapshot, setRevertSnapshot] = useState(null); + const [closeJobModalVisible, setCloseJobModalVisible] = useState(null); + const [combinedJobState, setCombinedJobState] = useState(null); + + useEffect(() => { + loadModelSnapshots(); + }, []); + + const loadModelSnapshots = useCallback(async () => { + const { model_snapshots: ms } = await ml.getModelSnapshots(job.job_id); + setSnapshots(ms); + setSnapshotsLoaded(true); + }, [job]); + + const checkJobIsClosed = useCallback( + async (snapshot: ModelSnapshot) => { + const state = await getCombinedJobState(job.job_id); + if (state === COMBINED_JOB_STATE.UNKNOWN) { + // this will only happen if the job has been deleted by another user + // between the time the row has been expended and now + // eslint-disable-next-line no-console + console.error(`Error retrieving state for job ${job.job_id}`); + return; + } + + setCombinedJobState(state); + + if (state === COMBINED_JOB_STATE.CLOSED) { + // show flyout + setRevertSnapshot(snapshot); + } else { + // show close job modal + setCloseJobModalVisible(snapshot); + } + }, + [job] + ); + + function hideCloseJobModalVisible() { + setCombinedJobState(null); + setCloseJobModalVisible(null); + } + + const forceCloseJob = useCallback(async () => { + await ml.jobs.forceStopAndCloseJob(job.job_id); + if (closeJobModalVisible !== null) { + const state = await getCombinedJobState(job.job_id); + if (state === COMBINED_JOB_STATE.CLOSED) { + setRevertSnapshot(closeJobModalVisible); + } + } + hideCloseJobModalVisible(); + }, [job, closeJobModalVisible]); + + const closeEditFlyout = useCallback((reload: boolean) => { + setEditSnapshot(null); + if (reload) { + loadModelSnapshots(); + } + }, []); + + const closeRevertFlyout = useCallback((reload: boolean) => { + setRevertSnapshot(null); + if (reload) { + loadModelSnapshots(); + // wait half a second before refreshing the jobs list + setTimeout(refreshJobList, 500); + } + }, []); + + const columns: Array> = [ + { + field: 'snapshot_id', + name: i18n.translate('xpack.ml.modelSnapshotTable.id', { + defaultMessage: 'ID', + }), + sortable: true, + }, + { + field: 'description', + name: i18n.translate('xpack.ml.modelSnapshotTable.description', { + defaultMessage: 'Description', + }), + sortable: true, + }, + { + field: 'timestamp', + name: i18n.translate('xpack.ml.modelSnapshotTable.time', { + defaultMessage: 'Date created', + }), + dataType: 'date', + render: renderDate, + sortable: true, + }, + { + field: 'latest_record_time_stamp', + name: i18n.translate('xpack.ml.modelSnapshotTable.latestTimestamp', { + defaultMessage: 'Latest timestamp', + }), + dataType: 'date', + render: renderDate, + sortable: true, + }, + { + field: 'retain', + name: i18n.translate('xpack.ml.modelSnapshotTable.retain', { + defaultMessage: 'Retain', + }), + width: '100px', + sortable: true, + }, + { + field: '', + width: '100px', + name: i18n.translate('xpack.ml.modelSnapshotTable.actions', { + defaultMessage: 'Actions', + }), + actions: [ + { + name: i18n.translate('xpack.ml.modelSnapshotTable.actions.revert.name', { + defaultMessage: 'Revert', + }), + description: i18n.translate('xpack.ml.modelSnapshotTable.actions.revert.description', { + defaultMessage: 'Revert to this snapshot', + }), + enabled: () => canCreateJob && canStartStopDatafeed, + type: 'icon', + icon: 'crosshairs', + onClick: checkJobIsClosed, + }, + { + name: i18n.translate('xpack.ml.modelSnapshotTable.actions.edit.name', { + defaultMessage: 'Edit', + }), + description: i18n.translate('xpack.ml.modelSnapshotTable.actions.edit.description', { + defaultMessage: 'Edit this snapshot', + }), + enabled: () => canCreateJob, + type: 'icon', + icon: 'pencil', + onClick: setEditSnapshot, + }, + ], + }, + ]; + + if (snapshotsLoaded === false) { + return ( + <> + + + + + + + ); + } + + return ( + <> + + {editSnapshot !== null && ( + + )} + + {revertSnapshot !== null && ( + + )} + + {closeJobModalVisible !== null && combinedJobState !== null && ( + + )} + + ); +}; + +function renderDate(date: number) { + return formatDate(date, TIME_FORMAT); +} + +async function getCombinedJobState(jobId: string) { + const jobs = await ml.jobs.jobs([jobId]); + + if (jobs.length !== 1) { + return COMBINED_JOB_STATE.UNKNOWN; + } + + if (jobs[0].state !== JOB_STATE.CLOSED) { + if (jobs[0].datafeed_config.state !== DATAFEED_STATE.STOPPED) { + return COMBINED_JOB_STATE.OPEN_AND_RUNNING; + } + return COMBINED_JOB_STATE.OPEN_AND_STOPPED; + } + return COMBINED_JOB_STATE.CLOSED; +} diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/chart_loader.ts b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/chart_loader.ts new file mode 100644 index 0000000000000..2da1e914b8139 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/chart_loader.ts @@ -0,0 +1,87 @@ +/* + * 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 { MlResultsService } from '../../../services/results_service'; +import { CombinedJobWithStats } from '../../../../../common/types/anomaly_detection_jobs'; +import { getSeverityType } from '../../../../../common/util/anomaly_utils'; +import { Anomaly } from '../../../jobs/new_job/common/results_loader/results_loader'; +import { LineChartPoint } from '../../../jobs/new_job/common/chart_loader/chart_loader'; + +export function chartLoaderProvider(mlResultsService: MlResultsService) { + async function loadEventRateForJob( + job: CombinedJobWithStats, + bucketSpanMs: number, + bars: number + ): Promise { + const intervalMs = Math.max( + Math.floor( + (job.data_counts.latest_record_timestamp - job.data_counts.earliest_record_timestamp) / bars + ), + bucketSpanMs + ); + const resp = await mlResultsService.getEventRateData( + job.datafeed_config.indices.join(), + job.datafeed_config.query, + job.data_description.time_field, + job.data_counts.earliest_record_timestamp, + job.data_counts.latest_record_timestamp, + intervalMs + ); + if (resp.error !== undefined) { + throw resp.error; + } + + const events = Object.entries(resp.results).map(([time, value]) => ({ + time: +time, + value: value as number, + })); + + if (events.length) { + // add one extra bucket with a value of 0 + // so that an extra blank bar gets drawn at the end of the chart + // this solves an issue with elastic charts where the rect annotation + // never covers the last bar. + events.push({ time: events[events.length - 1].time + intervalMs, value: 0 }); + } + + return events; + } + + async function loadAnomalyDataForJob( + job: CombinedJobWithStats, + bucketSpanMs: number, + bars: number + ) { + const intervalMs = Math.max( + Math.floor( + (job.data_counts.latest_record_timestamp - job.data_counts.earliest_record_timestamp) / bars + ), + bucketSpanMs + ); + + const resp = await mlResultsService.getScoresByBucket( + [job.job_id], + job.data_counts.earliest_record_timestamp, + job.data_counts.latest_record_timestamp, + intervalMs, + 1 + ); + + const results = resp.results[job.job_id]; + if (results === undefined) { + return []; + } + + const anomalies: Record = {}; + anomalies[0] = Object.entries(results).map( + ([time, value]) => + ({ time: +time, value, severity: getSeverityType(value as number) } as Anomaly) + ); + return anomalies; + } + + return { loadEventRateForJob, loadAnomalyDataForJob }; +} diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/create_calendar.tsx b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/create_calendar.tsx new file mode 100644 index 0000000000000..937c394e35bc1 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/create_calendar.tsx @@ -0,0 +1,310 @@ +/* + * 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. + */ + +/* + * 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. + */ + +/* + * 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, { FC, Fragment, useCallback, memo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import moment from 'moment'; +import { XYBrushArea } from '@elastic/charts'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiFormRow, + EuiFieldText, + EuiDatePicker, + EuiButtonIcon, + EuiPanel, +} from '@elastic/eui'; + +import { EventRateChart } from '../../../jobs/new_job/pages/components/charts/event_rate_chart/event_rate_chart'; +import { Anomaly } from '../../../jobs/new_job/common/results_loader/results_loader'; +import { useCurrentEuiTheme } from '../../../components/color_range_legend'; +import { LineChartPoint } from '../../../jobs/new_job/common/chart_loader/chart_loader'; + +export interface CalendarEvent { + start: moment.Moment | null; + end: moment.Moment | null; + description: string; +} + +interface Props { + calendarEvents: CalendarEvent[]; + setCalendarEvents: (calendars: CalendarEvent[]) => void; + minSelectableTimeStamp: number; + maxSelectableTimeStamp: number; + eventRateData: LineChartPoint[]; + anomalies: Anomaly[]; + chartReady: boolean; +} + +export const CreateCalendar: FC = ({ + calendarEvents, + setCalendarEvents, + minSelectableTimeStamp, + maxSelectableTimeStamp, + eventRateData, + anomalies, + chartReady, +}) => { + const maxSelectableTimeMoment = moment(maxSelectableTimeStamp); + const minSelectableTimeMoment = moment(minSelectableTimeStamp); + + const { euiTheme } = useCurrentEuiTheme(); + + const onBrushEnd = useCallback( + ({ x }: XYBrushArea) => { + if (x && x.length === 2) { + const end = x[1] < minSelectableTimeStamp ? null : x[1]; + if (end !== null) { + const start = x[0] < minSelectableTimeStamp ? minSelectableTimeStamp : x[0]; + + setCalendarEvents([ + ...calendarEvents, + { + start: moment(start), + end: moment(end), + description: createDefaultEventDescription(calendarEvents.length + 1), + }, + ]); + } + } + }, + [calendarEvents] + ); + + const setStartDate = useCallback( + (start: moment.Moment | null, index: number) => { + const event = calendarEvents[index]; + if (event === undefined) { + setCalendarEvents([ + ...calendarEvents, + { start, end: null, description: createDefaultEventDescription(index) }, + ]); + } else { + event.start = start; + setCalendarEvents([...calendarEvents]); + } + }, + [calendarEvents] + ); + + const setEndDate = useCallback( + (end: moment.Moment | null, index: number) => { + const event = calendarEvents[index]; + if (event === undefined) { + setCalendarEvents([ + ...calendarEvents, + { start: null, end, description: createDefaultEventDescription(index) }, + ]); + } else { + event.end = end; + setCalendarEvents([...calendarEvents]); + } + }, + [calendarEvents] + ); + + const setDescription = useCallback( + (description: string, index: number) => { + const event = calendarEvents[index]; + if (event !== undefined) { + event.description = description; + setCalendarEvents([...calendarEvents]); + } + }, + [calendarEvents] + ); + + const removeCalendarEvent = useCallback( + (index: number) => { + if (calendarEvents[index] !== undefined) { + const ce = [...calendarEvents]; + ce.splice(index, 1); + setCalendarEvents(ce); + } + }, + [calendarEvents] + ); + + return ( + <> + +
+ +
+ + ({ + start: c.start!.valueOf(), + end: c.end!.valueOf(), + }))} + onBrushEnd={onBrushEnd} + overlayColor={euiTheme.euiColorPrimary} + /> + + + {calendarEvents.map((c, i) => ( + + + + + + + + setStartDate(d, i)} + /> + + + + + setEndDate(d, i)} + /> + + + + + + + + setDescription(e.target.value, i)} + /> + + + + + + + removeCalendarEvent(i)} + iconType="trash" + aria-label={i18n.translate( + 'xpack.ml.revertModelSnapshotFlyout.createCalendar.deleteLabel', + { + defaultMessage: 'Delete event', + } + )} + /> + + + + + + ))} + + ); +}; + +interface ChartProps { + eventRateData: LineChartPoint[]; + anomalies: Anomaly[]; + loading: boolean; + onBrushEnd(area: XYBrushArea): void; + overlayRanges: Array<{ start: number; end: number }>; + overlayColor: string; +} + +const Chart: FC = memo( + ({ eventRateData, anomalies, loading, onBrushEnd, overlayRanges, overlayColor }) => ( + ({ + start: c.start, + end: c.end, + color: overlayColor, + showMarker: false, + }))} + onBrushEnd={onBrushEnd} + /> + ), + (prev: ChartProps, next: ChartProps) => { + // only redraw if the calendar ranges have changes + return ( + prev.overlayRanges.length === next.overlayRanges.length && + JSON.stringify(prev.overlayRanges) === JSON.stringify(next.overlayRanges) + ); + } +); + +function filterIncompleteEvents(event: CalendarEvent): event is CalendarEvent { + return event.start !== null && event.end !== null; +} + +function createDefaultEventDescription(index: number) { + return i18n.translate( + 'xpack.ml.revertModelSnapshotFlyout.createCalendar.defaultEventDescription', + { + defaultMessage: 'Auto created event {index}', + values: { index }, + } + ); +} diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/index.ts b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/index.ts new file mode 100644 index 0000000000000..33adc65a9e327 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { RevertModelSnapshotFlyout } from './revert_model_snapshot_flyout'; diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx new file mode 100644 index 0000000000000..ad5915b39d521 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx @@ -0,0 +1,409 @@ +/* + * 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. + */ + +/* + * 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, { FC, useState, useCallback, useMemo, useEffect } from 'react'; +import { i18n } from '@kbn/i18n'; +import {} from 'lodash'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiFlyout, + EuiFlyoutHeader, + EuiFlyoutFooter, + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiButtonEmpty, + EuiTitle, + EuiFlyoutBody, + EuiSpacer, + EuiFormRow, + EuiSwitch, + EuiConfirmModal, + EuiOverlayMask, + EuiCallOut, + EuiHorizontalRule, + EuiSuperSelect, + EuiText, + formatDate, +} from '@elastic/eui'; + +import { + ModelSnapshot, + CombinedJobWithStats, +} from '../../../../../common/types/anomaly_detection_jobs'; +import { ml } from '../../../services/ml_api_service'; +import { useNotifications } from '../../../contexts/kibana'; +import { chartLoaderProvider } from './chart_loader'; +import { mlResultsService } from '../../../services/results_service'; +import { LineChartPoint } from '../../../jobs/new_job/common/chart_loader'; +import { EventRateChart } from '../../../jobs/new_job/pages/components/charts/event_rate_chart/event_rate_chart'; +import { Anomaly } from '../../../jobs/new_job/common/results_loader/results_loader'; +import { parseInterval } from '../../../../../common/util/parse_interval'; +import { TIME_FORMAT } from '../../../../../common/constants/time_format'; +import { CreateCalendar, CalendarEvent } from './create_calendar'; + +interface Props { + snapshot: ModelSnapshot; + snapshots: ModelSnapshot[]; + job: CombinedJobWithStats; + closeFlyout(reload: boolean): void; +} + +export const RevertModelSnapshotFlyout: FC = ({ snapshot, snapshots, job, closeFlyout }) => { + const { toasts } = useNotifications(); + const { loadAnomalyDataForJob, loadEventRateForJob } = useMemo( + () => chartLoaderProvider(mlResultsService), + [] + ); + const [currentSnapshot, setCurrentSnapshot] = useState(snapshot); + const [revertModalVisible, setRevertModalVisible] = useState(false); + const [replay, setReplay] = useState(false); + const [runInRealTime, setRunInRealTime] = useState(false); + const [createCalendar, setCreateCalendar] = useState(false); + const [calendarEvents, setCalendarEvents] = useState([]); + const [calendarEventsValid, setCalendarEventsValid] = useState(true); + + const [eventRateData, setEventRateData] = useState([]); + const [anomalies, setAnomalies] = useState([]); + const [chartReady, setChartReady] = useState(false); + const [applying, setApplying] = useState(false); + + useEffect(() => { + createChartData(); + }, [currentSnapshot]); + + useEffect(() => { + const invalid = calendarEvents.some( + (c) => c.description === '' || c.end === null || c.start === null + ); + setCalendarEventsValid(invalid === false); + + // a bug in elastic charts selection can + // cause duplicate selected areas to be added + // dedupe the calendars based on start and end times + const calMap = new Map( + calendarEvents.map((c) => [`${c.start?.valueOf()}${c.end?.valueOf()}`, c]) + ); + const dedupedCalendarEvents = [...calMap.values()]; + + if (dedupedCalendarEvents.length < calendarEvents.length) { + // deduped list is shorter, we must have removed something. + setCalendarEvents(dedupedCalendarEvents); + } + }, [calendarEvents]); + + const createChartData = useCallback(async () => { + const bucketSpanMs = parseInterval(job.analysis_config.bucket_span)!.asMilliseconds(); + const eventRate = await loadEventRateForJob(job, bucketSpanMs, 100); + const anomalyData = await loadAnomalyDataForJob(job, bucketSpanMs, 100); + setEventRateData(eventRate); + if (anomalyData[0] !== undefined) { + setAnomalies(anomalyData[0]); + } + setChartReady(true); + }, [job]); + + function closeWithReload() { + closeFlyout(true); + } + function closeWithoutReload() { + closeFlyout(false); + } + + function showRevertModal() { + setRevertModalVisible(true); + } + function hideRevertModal() { + setRevertModalVisible(false); + } + + async function applyRevert() { + setApplying(true); + const end = + replay && runInRealTime === false ? job.data_counts.latest_record_timestamp : undefined; + try { + const events = + replay && createCalendar + ? calendarEvents.filter(filterIncompleteEvents).map((c) => ({ + start: c.start!.valueOf(), + end: c.end!.valueOf(), + description: c.description, + })) + : undefined; + + await ml.jobs.revertModelSnapshot( + job.job_id, + currentSnapshot.snapshot_id, + replay, + end, + events + ); + hideRevertModal(); + closeWithReload(); + } catch (error) { + setApplying(false); + toasts.addError(new Error(error.body.message), { + title: i18n.translate('xpack.ml.revertModelSnapshotFlyout.revertErrorTitle', { + defaultMessage: 'Model snapshot revert failed', + }), + }); + } + } + + function onSnapshotChange(ssId: string) { + const ss = snapshots.find((s) => s.snapshot_id === ssId); + if (ss !== undefined) { + setCurrentSnapshot(ss); + } + } + + return ( + <> + + + +
+ +
+
+ + +

{currentSnapshot.description}

+
+
+ + {false && ( // disabled for now + <> + + + + ({ + value: s.snapshot_id, + inputDisplay: s.snapshot_id, + dropdownDisplay: ( + <> + {s.snapshot_id} + +

{s.description}

+
+ + ), + })) + .reverse()} + valueOfSelected={currentSnapshot.snapshot_id} + onChange={onSnapshotChange} + itemLayoutAlign="top" + hasDividers + /> +
+ + + + )} + + + + + + + + + + + + + + setReplay(e.target.checked)} + /> + + + {replay && ( + <> + + setRunInRealTime(e.target.checked)} + /> + + + + setCreateCalendar(e.target.checked)} + /> + + + {createCalendar && ( + + )} + + )} +
+ + + + + + + + + + + + + + + + +
+ + {revertModalVisible && ( + + + + )} + + ); +}; + +function filterIncompleteEvents(event: CalendarEvent): event is CalendarEvent { + return event.start !== null && event.end !== null; +} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/back_to_list_panel/back_to_list_panel.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/back_to_list_panel/back_to_list_panel.tsx index e437d27372a3e..b6b335afa53f5 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/back_to_list_panel/back_to_list_panel.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/back_to_list_panel/back_to_list_panel.tsx @@ -7,29 +7,38 @@ import React, { FC, Fragment } from 'react'; import { EuiCard, EuiHorizontalRule, EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { useMlKibana } from '../../../../../contexts/kibana'; -function redirectToAnalyticsManagementPage() { - window.location.href = '#/data_frame_analytics?'; -} +export const BackToListPanel: FC = () => { + const { + services: { + application: { navigateToUrl }, + }, + } = useMlKibana(); -export const BackToListPanel: FC = () => ( - - - } - title={i18n.translate('xpack.ml.dataframe.analytics.create.analyticsListCardTitle', { - defaultMessage: 'Data Frame Analytics', - })} - description={i18n.translate( - 'xpack.ml.dataframe.analytics.create.analyticsListCardDescription', - { - defaultMessage: 'Return to the analytics management page.', - } - )} - onClick={redirectToAnalyticsManagementPage} - data-test-subj="analyticsWizardCardManagement" - /> - -); + const redirectToAnalyticsManagementPage = async () => { + await navigateToUrl('#/data_frame_analytics?'); + }; + + return ( + + + } + title={i18n.translate('xpack.ml.dataframe.analytics.create.analyticsListCardTitle', { + defaultMessage: 'Data Frame Analytics', + })} + description={i18n.translate( + 'xpack.ml.dataframe.analytics.create.analyticsListCardDescription', + { + defaultMessage: 'Return to the analytics management page.', + } + )} + onClick={redirectToAnalyticsManagementPage} + data-test-subj="analyticsWizardCardManagement" + /> + + ); +}; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/exploration_results_table.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/exploration_results_table.tsx index 24e5785c6e808..105eb9f73804d 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/exploration_results_table.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/exploration_results_table.tsx @@ -74,8 +74,9 @@ export const ExplorationResultsTable: FC = React.memo( if (jobConfig === undefined || classificationData === undefined) { return null; } + // if it's a searchBar syntax error leave the table visible so they can try again - if (status === INDEX_STATUS.ERROR && !errorMessage.includes('parsing_exception')) { + if (status === INDEX_STATUS.ERROR && !errorMessage.includes('failed to create query')) { return ( diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx index 58f8528236bb9..917ab1b0ed1dd 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx @@ -56,7 +56,7 @@ export const OutlierExploration: FC = React.memo(({ jobId }) = const { columns, errorMessage, status, tableItems } = outlierData; // if it's a searchBar syntax error leave the table visible so they can try again - if (status === INDEX_STATUS.ERROR && !errorMessage.includes('parsing_exception')) { + if (status === INDEX_STATUS.ERROR && !errorMessage.includes('failed to create query')) { return ( diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx index e8b1cd1a5696a..df7dce7217fd4 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx @@ -360,7 +360,14 @@ export const CloneAction: FC = ({ createAnalyticsForm, item }) defaultMessage: 'Clone job', }); - const { notifications, savedObjects } = useMlKibana().services; + const { + services: { + application: { navigateToUrl }, + notifications: { toasts }, + savedObjects, + }, + } = useMlKibana(); + const savedObjectsClient = savedObjects.client; const onClick = async () => { @@ -385,7 +392,6 @@ export const CloneAction: FC = ({ createAnalyticsForm, item }) sourceIndexId = ip.id; } } catch (e) { - const { toasts } = notifications; const error = extractErrorMessage(e); toasts.addDanger( @@ -401,9 +407,11 @@ export const CloneAction: FC = ({ createAnalyticsForm, item }) } if (sourceIndexId) { - window.location.href = `ml#/data_frame_analytics/new_job?index=${encodeURIComponent( - sourceIndexId - )}&jobId=${item.config.id}`; + await navigateToUrl( + `ml#/data_frame_analytics/new_job?index=${encodeURIComponent(sourceIndexId)}&jobId=${ + item.config.id + }` + ); } }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.tsx index d20afe93d2b9d..b03a58a02309d 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.tsx @@ -26,12 +26,20 @@ interface Props { } export const SourceSelection: FC = ({ onClose }) => { - const { uiSettings, savedObjects } = useMlKibana().services; + const { + services: { + application: { navigateToUrl }, + savedObjects, + uiSettings, + }, + } = useMlKibana(); - const onSearchSelected = (id: string, type: string) => { - window.location.href = `ml#/data_frame_analytics/new_job?${ - type === 'index-pattern' ? 'index' : 'savedSearchId' - }=${encodeURIComponent(id)}`; + const onSearchSelected = async (id: string, type: string) => { + await navigateToUrl( + `ml#/data_frame_analytics/new_job?${ + type === 'index-pattern' ? 'index' : 'savedSearchId' + }=${encodeURIComponent(id)}` + ); }; return ( diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/forecasts_table/forecasts_table.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/forecasts_table/forecasts_table.js index 817715dbf6413..2f40941fd20fe 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/forecasts_table/forecasts_table.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/forecasts_table/forecasts_table.js @@ -21,6 +21,7 @@ import { import { formatDate, formatNumber } from '@elastic/eui/lib/services/format'; import { FORECAST_REQUEST_STATE } from '../../../../../../../common/constants/states'; +import { TIME_FORMAT } from '../../../../../../../common/constants/time_format'; import { addItemToRecentlyAccessed } from '../../../../../util/recently_accessed'; import { mlForecastService } from '../../../../../services/forecast_service'; import { i18n } from '@kbn/i18n'; @@ -31,7 +32,6 @@ import { } from '../../../../../../../common/util/job_utils'; const MAX_FORECASTS = 500; -const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; /** * Table component for rendering the lists of forecasts run on an ML job. diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/format_values.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/format_values.js index 9194f7537cf3d..883ddfca70cd7 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/format_values.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/format_values.js @@ -8,8 +8,8 @@ import numeral from '@elastic/numeral'; import { formatDate } from '@elastic/eui/lib/services/format'; import { roundToDecimalPlace } from '../../../../formatters/round_to_decimal_place'; import { toLocaleString } from '../../../../util/string_utils'; +import { TIME_FORMAT } from '../../../../../../common/constants/time_format'; -const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; const DATA_FORMAT = '0.0 b'; function formatData(txt) { diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_details.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_details.js index 56da4f1e0ff84..9a5cea62cf6ff 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_details.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_details.js @@ -14,6 +14,7 @@ import { JsonPane } from './json_tab'; import { DatafeedPreviewPane } from './datafeed_preview_tab'; import { AnnotationsTable } from '../../../../components/annotations/annotations_table'; import { AnnotationFlyout } from '../../../../components/annotations/annotation_flyout'; +import { ModelSnapshotTable } from '../../../../components/model_snapshots'; import { ForecastsTable } from './forecasts_table'; import { JobDetailsPane } from './job_details_pane'; import { JobMessagesPane } from './job_messages_pane'; @@ -25,7 +26,7 @@ export class JobDetails extends Component { this.state = {}; if (this.props.addYourself) { - this.props.addYourself(props.jobId, this); + this.props.addYourself(props.jobId, (j) => this.updateJob(j)); } } @@ -33,9 +34,8 @@ export class JobDetails extends Component { this.props.removeYourself(this.props.jobId); } - static getDerivedStateFromProps(props) { - const { job, loading } = props; - return { job, loading }; + updateJob(job) { + this.setState({ job }); } render() { @@ -64,8 +64,7 @@ export class JobDetails extends Component { datafeedTimingStats, } = extractJobDetails(job); - const { showFullDetails } = this.props; - + const { showFullDetails, refreshJobList } = this.props; const tabs = [ { id: 'job-settings', @@ -175,6 +174,19 @@ export class JobDetails extends Component { ), }); + + tabs.push({ + id: 'modelSnapshots', + 'data-test-subj': 'mlJobListTab-modelSnapshots', + name: i18n.translate('xpack.ml.jobsList.jobDetails.tabs.modelSnapshotsLabel', { + defaultMessage: 'Model snapshots', + }), + content: ( + + + + ), + }); } return ( @@ -191,4 +203,5 @@ JobDetails.propTypes = { addYourself: PropTypes.func.isRequired, removeYourself: PropTypes.func.isRequired, showFullDetails: PropTypes.bool, + refreshJobList: PropTypes.func, }; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js index 0afaca3ec12e1..23b68551ca0f5 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js @@ -15,6 +15,7 @@ import { ResultLinks, actionsMenuContent } from '../job_actions'; import { JobDescription } from './job_description'; import { JobIcon } from '../../../../components/job_message_icon'; import { getJobIdUrl } from '../../../../util/get_job_id_url'; +import { TIME_FORMAT } from '../../../../../../common/constants/time_format'; import { EuiBadge, EuiBasicTable, EuiButtonIcon, EuiLink, EuiScreenReaderOnly } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -22,7 +23,6 @@ import { FormattedMessage } from '@kbn/i18n/react'; const PAGE_SIZE = 10; const PAGE_SIZE_OPTIONS = [10, 25, 50]; -const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; // 'isManagementTable' bool prop to determine when to configure table for use in Kibana management page export class JobsList extends Component { diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js index bb9e532245d6d..a3b6cb39815a3 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js @@ -112,6 +112,7 @@ export class JobsListView extends Component { addYourself={this.addUpdateFunction} removeYourself={this.removeUpdateFunction} showFullDetails={this.props.isManagementTable !== true} + refreshJobList={this.onRefreshClick} /> ); } else { @@ -121,6 +122,7 @@ export class JobsListView extends Component { addYourself={this.addUpdateFunction} removeYourself={this.removeUpdateFunction} showFullDetails={this.props.isManagementTable !== true} + refreshJobList={this.onRefreshClick} /> ); } @@ -143,10 +145,13 @@ export class JobsListView extends Component { addYourself={this.addUpdateFunction} removeYourself={this.removeUpdateFunction} showFullDetails={this.props.isManagementTable !== true} + refreshJobList={this.onRefreshClick} /> ); } - this.setState({ itemIdToExpandedRowMap }); + this.setState({ itemIdToExpandedRowMap }, () => { + this.updateFunctions[jobId](job); + }); }); }) .catch((error) => { @@ -254,7 +259,7 @@ export class JobsListView extends Component { ); Object.keys(this.updateFunctions).forEach((j) => { - this.updateFunctions[j].setState({ job: fullJobsList[j] }); + this.updateFunctions[j](fullJobsList[j]); }); jobs.forEach((job) => { diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector.js index 55c87bbc90b10..8cf244c3c7d8a 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector.js @@ -13,8 +13,7 @@ import { EuiDatePicker, EuiFieldText } from '@elastic/eui'; import moment from 'moment'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; - -const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; +import { TIME_FORMAT } from '../../../../../../../common/constants/time_format'; export class TimeRangeSelector extends Component { constructor(props) { diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/charts/event_rate_chart/event_rate_chart.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/charts/event_rate_chart/event_rate_chart.tsx index 2fb8ea2820b29..bd6fedd4ba21c 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/charts/event_rate_chart/event_rate_chart.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/charts/event_rate_chart/event_rate_chart.tsx @@ -5,13 +5,22 @@ */ import React, { FC } from 'react'; -import { BarSeries, Chart, ScaleType, Settings, TooltipType } from '@elastic/charts'; +import { + HistogramBarSeries, + Chart, + ScaleType, + Settings, + TooltipType, + BrushEndListener, + PartialTheme, +} from '@elastic/charts'; import { Axes } from '../common/axes'; import { LineChartPoint } from '../../../../common/chart_loader'; import { Anomaly } from '../../../../common/results_loader'; import { useChartColors } from '../common/settings'; import { LoadingWrapper } from '../loading_wrapper'; import { Anomalies } from '../common/anomalies'; +import { OverlayRange } from './overlay_range'; interface Props { eventRateChartData: LineChartPoint[]; @@ -21,6 +30,13 @@ interface Props { showAxis?: boolean; loading?: boolean; fadeChart?: boolean; + overlayRanges?: Array<{ + start: number; + end: number; + color: string; + showMarker?: boolean; + }>; + onBrushEnd?: BrushEndListener; } export const EventRateChart: FC = ({ @@ -31,10 +47,16 @@ export const EventRateChart: FC = ({ showAxis, loading = false, fadeChart, + overlayRanges, + onBrushEnd, }) => { const { EVENT_RATE_COLOR_WITH_ANOMALIES, EVENT_RATE_COLOR } = useChartColors(); const barColor = fadeChart ? EVENT_RATE_COLOR_WITH_ANOMALIES : EVENT_RATE_COLOR; + const theme: PartialTheme = { + scales: { histogramPadding: 0.2 }, + }; + return (
= ({ {showAxis === true && } - + {onBrushEnd === undefined ? ( + + ) : ( + + )} + + {overlayRanges && + overlayRanges.map((range, i) => ( + + ))} + - = ({ + overlayKey, + eventRateChartData, + start, + end, + color, + showMarker = true, +}) => { + const maxHeight = Math.max(...eventRateChartData.map((e) => e.value)); + + return ( + <> + + +
+
+ +
+
+ {formatDate(start, TIME_FORMAT)} +
+
+ + ) : undefined + } + /> + + ); +}; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/create_result_callout.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/create_result_callout.tsx index 9d0cf705aaba6..4602ceeec905f 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/create_result_callout.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/create_result_callout.tsx @@ -67,7 +67,7 @@ export const CreateResultCallout: FC = memo( color="primary" fill={false} aria-label={i18n.translate( - 'xpack.ml.newJi18n(ob.recognize.jobsCreationFailed.resetButtonAriaLabel', + 'xpack.ml.newJob.recognize.jobsCreationFailed.resetButtonAriaLabel', { defaultMessage: 'Reset' } )} onClick={onReset} diff --git a/x-pack/plugins/ml/public/application/services/job_service.js b/x-pack/plugins/ml/public/application/services/job_service.js index a3be479571702..6c0f393c267aa 100644 --- a/x-pack/plugins/ml/public/application/services/job_service.js +++ b/x-pack/plugins/ml/public/application/services/job_service.js @@ -13,6 +13,7 @@ import { ml } from './ml_api_service'; import { mlMessageBarService } from '../components/messagebar'; import { isWebUrl } from '../util/url_utils'; import { ML_DATA_PREVIEW_COUNT } from '../../../common/util/job_utils'; +import { TIME_FORMAT } from '../../../common/constants/time_format'; import { parseInterval } from '../../../common/util/parse_interval'; const msgs = mlMessageBarService; @@ -929,10 +930,8 @@ function createResultsUrlForJobs(jobsList, resultsPage) { } } - const timeFormat = 'YYYY-MM-DD HH:mm:ss'; - - const fromString = moment(from).format(timeFormat); // Defaults to 'now' if 'from' is undefined - const toString = moment(to).format(timeFormat); // Defaults to 'now' if 'to' is undefined + const fromString = moment(from).format(TIME_FORMAT); // Defaults to 'now' if 'from' is undefined + const toString = moment(to).format(TIME_FORMAT); // Defaults to 'now' if 'to' is undefined const jobIds = jobsList.map((j) => j.id); return createResultsUrl(jobIds, fromString, toString, resultsPage); diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts index 6e3fd08e90e38..fdaa3c2ffe79e 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts @@ -23,6 +23,7 @@ import { CombinedJob, Detector, AnalysisConfig, + ModelSnapshot, } from '../../../../common/types/anomaly_detection_jobs'; import { ES_AGGREGATION } from '../../../../common/constants/aggregation_types'; import { FieldRequestConfig } from '../../datavisualizer/index_based/common'; @@ -77,6 +78,11 @@ export interface CardinalityModelPlotHigh { export type CardinalityValidationResult = SuccessCardinality | CardinalityModelPlotHigh; export type CardinalityValidationResults = CardinalityValidationResult[]; +export interface GetModelSnapshotsResponse { + count: number; + model_snapshots: ModelSnapshot[]; +} + export function basePath() { return '/api/ml'; } @@ -119,6 +125,13 @@ export const ml = { }); }, + forceCloseJob({ jobId }: { jobId: string }) { + return http({ + path: `${basePath()}/anomaly_detectors/${jobId}/_close?force=true`, + method: 'POST', + }); + }, + deleteJob({ jobId }: { jobId: string }) { return http({ path: `${basePath()}/anomaly_detectors/${jobId}`, @@ -242,6 +255,13 @@ export const ml = { }); }, + forceStopDatafeed({ datafeedId }: { datafeedId: string }) { + return http({ + path: `${basePath()}/datafeeds/${datafeedId}/_stop?force=true`, + method: 'POST', + }); + }, + datafeedPreview({ datafeedId }: { datafeedId: string }) { return http({ path: `${basePath()}/datafeeds/${datafeedId}/_preview`, @@ -640,6 +660,33 @@ export const ml = { }); }, + getModelSnapshots(jobId: string, snapshotId?: string) { + return http({ + path: `${basePath()}/anomaly_detectors/${jobId}/model_snapshots${ + snapshotId !== undefined ? `/${snapshotId}` : '' + }`, + }); + }, + + updateModelSnapshot( + jobId: string, + snapshotId: string, + body: { description?: string; retain?: boolean } + ) { + return http({ + path: `${basePath()}/anomaly_detectors/${jobId}/model_snapshots/${snapshotId}/_update`, + method: 'POST', + body: JSON.stringify(body), + }); + }, + + deleteModelSnapshot(jobId: string, snapshotId: string) { + return http({ + path: `${basePath()}/anomaly_detectors/${jobId}/model_snapshots/${snapshotId}`, + method: 'DELETE', + }); + }, + annotations, dataFrameAnalytics, filters, diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts index e2569f6217b34..6aa62da3f0768 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts @@ -8,7 +8,11 @@ import { http } from '../http_service'; import { basePath } from './index'; import { Dictionary } from '../../../../common/types/common'; -import { MlJobWithTimeRange, MlSummaryJobs } from '../../../../common/types/anomaly_detection_jobs'; +import { + MlJobWithTimeRange, + MlSummaryJobs, + CombinedJobWithStats, +} from '../../../../common/types/anomaly_detection_jobs'; import { JobMessage } from '../../../../common/types/audit_message'; import { AggFieldNamePair } from '../../../../common/types/fields'; import { ExistingJobsAndGroups } from '../job_service'; @@ -41,7 +45,7 @@ export const jobs = { jobs(jobIds: string[]) { const body = JSON.stringify({ jobIds }); - return http({ + return http({ path: `${basePath()}/jobs/jobs`, method: 'POST', body, @@ -95,6 +99,7 @@ export const jobs = { body, }); }, + closeJobs(jobIds: string[]) { const body = JSON.stringify({ jobIds }); return http({ @@ -104,6 +109,15 @@ export const jobs = { }); }, + forceStopAndCloseJob(jobId: string) { + const body = JSON.stringify({ jobId }); + return http<{ success: boolean }>({ + path: `${basePath()}/jobs/force_stop_and_close_job`, + method: 'POST', + body, + }); + }, + jobAuditMessages(jobId: string, from?: number) { const jobIdString = jobId !== undefined ? `/${jobId}` : ''; const query = from !== undefined ? { from } : {}; @@ -255,4 +269,19 @@ export const jobs = { body, }); }, + + revertModelSnapshot( + jobId: string, + snapshotId: string, + replay: boolean, + end?: number, + calendarEvents?: Array<{ start: number; end: number; description: string }> + ) { + const body = JSON.stringify({ jobId, snapshotId, replay, end, calendarEvents }); + return http<{ total: number; categories: Array<{ count?: number; category: Category }> }>({ + path: `${basePath()}/jobs/revert_model_snapshot`, + method: 'POST', + body, + }); + }, }; diff --git a/x-pack/plugins/ml/public/application/settings/calendars/edit/events_table/events_table.js b/x-pack/plugins/ml/public/application/settings/calendars/edit/events_table/events_table.js index 815aa4810ebaa..9069e8078fca6 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/edit/events_table/events_table.js +++ b/x-pack/plugins/ml/public/application/settings/calendars/edit/events_table/events_table.js @@ -12,8 +12,7 @@ import { EuiButton, EuiButtonEmpty, EuiInMemoryTable, EuiSpacer } from '@elastic import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; - -export const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; +import { TIME_FORMAT } from '../../../../../../common/constants/time_format'; function DeleteButton({ onClick, canDeleteCalendar }) { return ( diff --git a/x-pack/plugins/ml/public/application/settings/calendars/edit/events_table/index.js b/x-pack/plugins/ml/public/application/settings/calendars/edit/events_table/index.js index 0910a04051bf4..89bd0235df996 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/edit/events_table/index.js +++ b/x-pack/plugins/ml/public/application/settings/calendars/edit/events_table/index.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { EventsTable, TIME_FORMAT } from './events_table'; +export { EventsTable } from './events_table'; diff --git a/x-pack/plugins/ml/public/application/settings/calendars/edit/new_event_modal/new_event_modal.js b/x-pack/plugins/ml/public/application/settings/calendars/edit/new_event_modal/new_event_modal.js index 8380fd36b458c..d80e248674a8f 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/edit/new_event_modal/new_event_modal.js +++ b/x-pack/plugins/ml/public/application/settings/calendars/edit/new_event_modal/new_event_modal.js @@ -24,7 +24,7 @@ import { EuiFlexItem, } from '@elastic/eui'; import moment from 'moment'; -import { TIME_FORMAT } from '../events_table'; +import { TIME_FORMAT } from '../../../../../../common/constants/time_format'; import { generateTempId } from '../utils'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/plugins/ml/server/client/elasticsearch_ml.ts b/x-pack/plugins/ml/server/client/elasticsearch_ml.ts index d5c7882a30d20..07159534e1e2c 100644 --- a/x-pack/plugins/ml/server/client/elasticsearch_ml.ts +++ b/x-pack/plugins/ml/server/client/elasticsearch_ml.ts @@ -393,6 +393,17 @@ export const elasticsearchJsPlugin = (Client: any, config: any, components: any) ml.stopDatafeed = ca({ urls: [ + { + fmt: '/_ml/datafeeds/<%=datafeedId%>/_stop?force=<%=force%>', + req: { + datafeedId: { + type: 'string', + }, + force: { + type: 'boolean', + }, + }, + }, { fmt: '/_ml/datafeeds/<%=datafeedId%>/_stop', req: { @@ -823,4 +834,81 @@ export const elasticsearchJsPlugin = (Client: any, config: any, components: any) ], method: 'GET', }); + + ml.modelSnapshots = ca({ + urls: [ + { + fmt: '/_xpack/ml/anomaly_detectors/<%=jobId%>/model_snapshots/<%=snapshotId%>', + req: { + jobId: { + type: 'string', + }, + snapshotId: { + type: 'string', + }, + }, + }, + { + fmt: '/_xpack/ml/anomaly_detectors/<%=jobId%>/model_snapshots', + req: { + jobId: { + type: 'string', + }, + }, + }, + ], + method: 'GET', + }); + + ml.updateModelSnapshot = ca({ + urls: [ + { + fmt: '/_xpack/ml/anomaly_detectors/<%=jobId%>/model_snapshots/<%=snapshotId%>/_update', + req: { + jobId: { + type: 'string', + }, + snapshotId: { + type: 'string', + }, + }, + }, + ], + method: 'POST', + needBody: true, + }); + + ml.deleteModelSnapshot = ca({ + urls: [ + { + fmt: '/_xpack/ml/anomaly_detectors/<%=jobId%>/model_snapshots/<%=snapshotId%>', + req: { + jobId: { + type: 'string', + }, + snapshotId: { + type: 'string', + }, + }, + }, + ], + method: 'DELETE', + }); + + ml.revertModelSnapshot = ca({ + urls: [ + { + fmt: '/_xpack/ml/anomaly_detectors/<%=jobId%>/model_snapshots/<%=snapshotId%>/_revert', + req: { + jobId: { + type: 'string', + }, + snapshotId: { + type: 'string', + }, + }, + }, + ], + method: 'POST', + }); }; diff --git a/x-pack/plugins/ml/server/models/calendar/calendar_manager.ts b/x-pack/plugins/ml/server/models/calendar/calendar_manager.ts index acb1bed6a37c0..2eec704f1e784 100644 --- a/x-pack/plugins/ml/server/models/calendar/calendar_manager.ts +++ b/x-pack/plugins/ml/server/models/calendar/calendar_manager.ts @@ -5,7 +5,7 @@ */ import { difference } from 'lodash'; -import { IScopedClusterClient } from 'kibana/server'; +import { APICaller } from 'kibana/server'; import { EventManager, CalendarEvent } from './event_manager'; interface BasicCalendar { @@ -23,16 +23,16 @@ export interface FormCalendar extends BasicCalendar { } export class CalendarManager { - private _client: IScopedClusterClient['callAsCurrentUser']; - private _eventManager: any; + private _callAsCurrentUser: APICaller; + private _eventManager: EventManager; - constructor(client: any) { - this._client = client; - this._eventManager = new EventManager(client); + constructor(callAsCurrentUser: APICaller) { + this._callAsCurrentUser = callAsCurrentUser; + this._eventManager = new EventManager(callAsCurrentUser); } async getCalendar(calendarId: string) { - const resp = await this._client('ml.calendars', { + const resp = await this._callAsCurrentUser('ml.calendars', { calendarId, }); @@ -43,7 +43,7 @@ export class CalendarManager { } async getAllCalendars() { - const calendarsResp = await this._client('ml.calendars'); + const calendarsResp = await this._callAsCurrentUser('ml.calendars'); const events: CalendarEvent[] = await this._eventManager.getAllEvents(); const calendars: Calendar[] = calendarsResp.calendars; @@ -74,7 +74,7 @@ export class CalendarManager { const events = calendar.events; delete calendar.calendarId; delete calendar.events; - await this._client('ml.addCalendar', { + await this._callAsCurrentUser('ml.addCalendar', { calendarId, body: calendar, }); @@ -109,7 +109,7 @@ export class CalendarManager { // add all new jobs if (jobsToAdd.length) { - await this._client('ml.addJobToCalendar', { + await this._callAsCurrentUser('ml.addJobToCalendar', { calendarId, jobId: jobsToAdd.join(','), }); @@ -117,7 +117,7 @@ export class CalendarManager { // remove all removed jobs if (jobsToRemove.length) { - await this._client('ml.removeJobFromCalendar', { + await this._callAsCurrentUser('ml.removeJobFromCalendar', { calendarId, jobId: jobsToRemove.join(','), }); @@ -131,7 +131,7 @@ export class CalendarManager { // remove all removed events await Promise.all( eventsToRemove.map(async (event) => { - await this._eventManager.deleteEvent(calendarId, event.event_id); + await this._eventManager.deleteEvent(calendarId, event.event_id!); }) ); @@ -140,6 +140,6 @@ export class CalendarManager { } async deleteCalendar(calendarId: string) { - return this._client('ml.deleteCalendar', { calendarId }); + return this._callAsCurrentUser('ml.deleteCalendar', { calendarId }); } } diff --git a/x-pack/plugins/ml/server/models/calendar/event_manager.ts b/x-pack/plugins/ml/server/models/calendar/event_manager.ts index 41240e2695f6f..02da0718d83ae 100644 --- a/x-pack/plugins/ml/server/models/calendar/event_manager.ts +++ b/x-pack/plugins/ml/server/models/calendar/event_manager.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { APICaller } from 'kibana/server'; import { GLOBAL_CALENDAR } from '../../../common/constants/calendars'; export interface CalendarEvent { @@ -15,13 +16,10 @@ export interface CalendarEvent { } export class EventManager { - private _client: any; - constructor(client: any) { - this._client = client; - } + constructor(private _callAsCurrentUser: APICaller) {} async getCalendarEvents(calendarId: string) { - const resp = await this._client('ml.events', { calendarId }); + const resp = await this._callAsCurrentUser('ml.events', { calendarId }); return resp.events; } @@ -29,7 +27,7 @@ export class EventManager { // jobId is optional async getAllEvents(jobId?: string) { const calendarId = GLOBAL_CALENDAR; - const resp = await this._client('ml.events', { + const resp = await this._callAsCurrentUser('ml.events', { calendarId, jobId, }); @@ -40,14 +38,14 @@ export class EventManager { async addEvents(calendarId: string, events: CalendarEvent[]) { const body = { events }; - return await this._client('ml.addEvent', { + return await this._callAsCurrentUser('ml.addEvent', { calendarId, body, }); } async deleteEvent(calendarId: string, eventId: string) { - return this._client('ml.deleteEvent', { calendarId, eventId }); + return this._callAsCurrentUser('ml.deleteEvent', { calendarId, eventId }); } isEqual(ev1: CalendarEvent, ev2: CalendarEvent) { diff --git a/x-pack/plugins/ml/server/models/calendar/index.ts b/x-pack/plugins/ml/server/models/calendar/index.ts index 2364c3ac73811..1a35f9f13368e 100644 --- a/x-pack/plugins/ml/server/models/calendar/index.ts +++ b/x-pack/plugins/ml/server/models/calendar/index.ts @@ -5,3 +5,4 @@ */ export { CalendarManager, Calendar, FormCalendar } from './calendar_manager'; +export { CalendarEvent } from './event_manager'; diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/auditbeat_process_docker_ecs/kibana/visualization/ml_auditbeat_docker_process_occurrence_ecs.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/auditbeat_process_docker_ecs/kibana/visualization/ml_auditbeat_docker_process_occurrence_ecs.json index 1737039093432..f671d3fa1dec0 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/auditbeat_process_docker_ecs/kibana/visualization/ml_auditbeat_docker_process_occurrence_ecs.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/auditbeat_process_docker_ecs/kibana/visualization/ml_auditbeat_docker_process_occurrence_ecs.json @@ -1,6 +1,6 @@ { "title": "ML Auditbeat Docker: Process Occurrence - experimental (ECS)", - "visState": "{\"type\":\"vega\",\"params\":{\"spec\":\"{\\n $schema: https://vega.github.io/schema/vega-lite/v2.json\\n mark: {type: \\\"point\\\"}\\n data: {\\n url: {\\n index: \\\"INDEX_PATTERN_NAME\\\"\\n body: {\\n size: 10000\\n query: {\\n bool: {\\n must: [\\n %dashboard_context-must_clause%\\n {\\n exists: {field: \\\"process.executable\\\"}\\n }\\n {\\n function_score: {\\n random_score: {seed: 10, field: \\\"_seq_no\\\"}\\n }\\n }\\n {\\n range: {\\n @timestamp: {\\n %timefilter%: true\\n }\\n }\\n }\\n ]\\n must_not: [\\n \\\"%dashboard_context-must_not_clause%\\\"\\n ]\\n }\\n }\\n script_fields: {\\n process_exe: {\\n script: {source: \\\"params['_source']['process']['executable']\\\"}\\n }\\n }\\n _source: [\\\"@timestamp\\\", \\\"process_exe\\\"]\\n }\\n }\\n format: {property: \\\"hits.hits\\\"}\\n }\\n transform: [\\n {calculate: \\\"toDate(datum._source['@timestamp'])\\\", as: \\\"time\\\"}\\n ]\\n encoding: {\\n x: {\\n field: time\\n type: temporal\\n axis: {labels: true, ticks: true, title: false},\\n timeUnit: utcyearmonthdatehoursminutes\\n }\\n y: {\\n field: fields.process_exe\\n type: ordinal\\n sort: {op: \\\"count\\\", order: \\\"descending\\\"}\\n axis: {labels: true, title: \\\"occurrence of process.executable\\\", ticks: false}\\n }\\n }\\n config: {\\n style: {\\n point: {filled: true}\\n }\\n }\\n}\"},\"aggs\":[]}", + "visState": "{\"type\":\"vega\",\"params\":{\"spec\":\"{\\n $schema: https://vega.github.io/schema/vega-lite/v4.json\\n width: \\\"container\\\"\\n mark: {type: \\\"point\\\"}\\n data: {\\n url: {\\n index: \\\"INDEX_PATTERN_NAME\\\"\\n body: {\\n size: 10000\\n query: {\\n bool: {\\n must: [\\n %dashboard_context-must_clause%\\n {\\n exists: {field: \\\"process.executable\\\"}\\n }\\n {\\n function_score: {\\n random_score: {seed: 10, field: \\\"_seq_no\\\"}\\n }\\n }\\n {\\n range: {\\n @timestamp: {\\n %timefilter%: true\\n }\\n }\\n }\\n ]\\n must_not: [\\n \\\"%dashboard_context-must_not_clause%\\\"\\n ]\\n }\\n }\\n script_fields: {\\n process_exe: {\\n script: {source: \\\"params['_source']['process']['executable']\\\"}\\n }\\n }\\n _source: [\\\"@timestamp\\\", \\\"process_exe\\\"]\\n }\\n }\\n format: {property: \\\"hits.hits\\\"}\\n }\\n transform: [\\n {calculate: \\\"toDate(datum._source['@timestamp'])\\\", as: \\\"time\\\"}\\n ]\\n encoding: {\\n x: {\\n field: time\\n type: temporal\\n axis: {labels: true, ticks: true, title: false},\\n timeUnit: utcyearmonthdatehoursminutes\\n }\\n y: {\\n field: fields.process_exe\\n type: ordinal\\n sort: {op: \\\"count\\\", order: \\\"descending\\\"}\\n axis: {labels: true, title: \\\"occurrence of process.executable\\\", ticks: false}\\n }\\n }\\n config: {\\n style: {\\n point: {filled: true}\\n }\\n }\\n}\"},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "savedSearchId": "ml_auditbeat_docker_process_events_ecs", diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/auditbeat_process_hosts_ecs/kibana/visualization/ml_auditbeat_hosts_process_occurrence_ecs.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/auditbeat_process_hosts_ecs/kibana/visualization/ml_auditbeat_hosts_process_occurrence_ecs.json index b172ac7ff21cc..0d28081818ac7 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/auditbeat_process_hosts_ecs/kibana/visualization/ml_auditbeat_hosts_process_occurrence_ecs.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/auditbeat_process_hosts_ecs/kibana/visualization/ml_auditbeat_hosts_process_occurrence_ecs.json @@ -1,6 +1,6 @@ { "title": "ML Auditbeat Hosts: Process Occurrence - experimental (ECS)", - "visState": "{\"type\":\"vega\",\"params\":{\"spec\":\"{\\n $schema: https://vega.github.io/schema/vega-lite/v2.json\\n mark: {type: \\\"point\\\"}\\n data: {\\n url: {\\n index: \\\"INDEX_PATTERN_NAME\\\"\\n body: {\\n size: 10000\\n query: {\\n bool: {\\n must: [\\n %dashboard_context-must_clause%\\n {\\n exists: {field: \\\"process.executable\\\"}\\n }\\n {\\n function_score: {\\n random_score: {seed: 10, field: \\\"_seq_no\\\"}\\n }\\n }\\n {\\n range: {\\n @timestamp: {\\n %timefilter%: true\\n }\\n }\\n }\\n ]\\n must_not: [\\n \\\"%dashboard_context-must_not_clause%\\\"\\n ]\\n }\\n }\\n script_fields: {\\n process_exe: {\\n script: {source: \\\"params['_source']['process']['executable']\\\"}\\n }\\n }\\n _source: [\\\"@timestamp\\\", \\\"process_exe\\\"]\\n }\\n }\\n format: {property: \\\"hits.hits\\\"}\\n }\\n transform: [\\n {calculate: \\\"toDate(datum._source['@timestamp'])\\\", as: \\\"time\\\"}\\n ]\\n encoding: {\\n x: {\\n field: time\\n type: temporal\\n axis: {labels: true, ticks: true, title: false},\\n timeUnit: utcyearmonthdatehoursminutes\\n }\\n y: {\\n field: fields.process_exe\\n type: ordinal\\n sort: {op: \\\"count\\\", order: \\\"descending\\\"}\\n axis: {labels: true, title: \\\"occurrence of process.executable\\\", ticks: false}\\n }\\n }\\n config: {\\n style: {\\n point: {filled: true}\\n }\\n }\\n}\"},\"aggs\":[]}", + "visState": "{\"type\":\"vega\",\"params\":{\"spec\":\"{\\n $schema: https://vega.github.io/schema/vega-lite/v4.json\\n width: \\\"container\\\"\\n mark: {type: \\\"point\\\"}\\n data: {\\n url: {\\n index: \\\"INDEX_PATTERN_NAME\\\"\\n body: {\\n size: 10000\\n query: {\\n bool: {\\n must: [\\n %dashboard_context-must_clause%\\n {\\n exists: {field: \\\"process.executable\\\"}\\n }\\n {\\n function_score: {\\n random_score: {seed: 10, field: \\\"_seq_no\\\"}\\n }\\n }\\n {\\n range: {\\n @timestamp: {\\n %timefilter%: true\\n }\\n }\\n }\\n ]\\n must_not: [\\n \\\"%dashboard_context-must_not_clause%\\\"\\n ]\\n }\\n }\\n script_fields: {\\n process_exe: {\\n script: {source: \\\"params['_source']['process']['executable']\\\"}\\n }\\n }\\n _source: [\\\"@timestamp\\\", \\\"process_exe\\\"]\\n }\\n }\\n format: {property: \\\"hits.hits\\\"}\\n }\\n transform: [\\n {calculate: \\\"toDate(datum._source['@timestamp'])\\\", as: \\\"time\\\"}\\n ]\\n encoding: {\\n x: {\\n field: time\\n type: temporal\\n axis: {labels: true, ticks: true, title: false},\\n timeUnit: utcyearmonthdatehoursminutes\\n }\\n y: {\\n field: fields.process_exe\\n type: ordinal\\n sort: {op: \\\"count\\\", order: \\\"descending\\\"}\\n axis: {labels: true, title: \\\"occurrence of process.executable\\\", ticks: false}\\n }\\n }\\n config: {\\n style: {\\n point: {filled: true}\\n }\\n }\\n}\"},\"aggs\":[]}", "uiStateJSON": "{}", "description": "", "savedSearchId": "ml_auditbeat_hosts_process_events_ecs", diff --git a/x-pack/plugins/ml/server/models/job_service/datafeeds.ts b/x-pack/plugins/ml/server/models/job_service/datafeeds.ts index 4090a59c461da..f016898075918 100644 --- a/x-pack/plugins/ml/server/models/job_service/datafeeds.ts +++ b/x-pack/plugins/ml/server/models/job_service/datafeeds.ts @@ -27,7 +27,7 @@ interface Results { } export function datafeedsProvider(callAsCurrentUser: APICaller) { - async function forceStartDatafeeds(datafeedIds: string[], start: number, end: number) { + async function forceStartDatafeeds(datafeedIds: string[], start?: number, end?: number) { const jobIds = await getJobIdsByDatafeedId(); const doStartsCalled = datafeedIds.reduce((acc, cur) => { acc[cur] = false; @@ -96,7 +96,7 @@ export function datafeedsProvider(callAsCurrentUser: APICaller) { return opened; } - async function startDatafeed(datafeedId: string, start: number, end: number) { + async function startDatafeed(datafeedId: string, start?: number, end?: number) { return callAsCurrentUser('ml.startDatafeed', { datafeedId, start, end }); } diff --git a/x-pack/plugins/ml/server/models/job_service/index.ts b/x-pack/plugins/ml/server/models/job_service/index.ts index eb70a3ccecfc1..b6e87b98735ee 100644 --- a/x-pack/plugins/ml/server/models/job_service/index.ts +++ b/x-pack/plugins/ml/server/models/job_service/index.ts @@ -10,6 +10,7 @@ import { jobsProvider } from './jobs'; import { groupsProvider } from './groups'; import { newJobCapsProvider } from './new_job_caps'; import { newJobChartsProvider, topCategoriesProvider } from './new_job'; +import { modelSnapshotProvider } from './model_snapshots'; export function jobServiceProvider(callAsCurrentUser: APICaller) { return { @@ -19,5 +20,6 @@ export function jobServiceProvider(callAsCurrentUser: APICaller) { ...newJobCapsProvider(callAsCurrentUser), ...newJobChartsProvider(callAsCurrentUser), ...topCategoriesProvider(callAsCurrentUser), + ...modelSnapshotProvider(callAsCurrentUser), }; } diff --git a/x-pack/plugins/ml/server/models/job_service/jobs.ts b/x-pack/plugins/ml/server/models/job_service/jobs.ts index 5503169f2d371..852264b3d0337 100644 --- a/x-pack/plugins/ml/server/models/job_service/jobs.ts +++ b/x-pack/plugins/ml/server/models/job_service/jobs.ts @@ -6,6 +6,7 @@ import { i18n } from '@kbn/i18n'; import { uniq } from 'lodash'; +import Boom from 'boom'; import { APICaller } from 'kibana/server'; import { JOB_STATE, DATAFEED_STATE } from '../../../common/constants/states'; import { @@ -128,6 +129,23 @@ export function jobsProvider(callAsCurrentUser: APICaller) { return results; } + async function forceStopAndCloseJob(jobId: string) { + const datafeedIds = await getDatafeedIdsByJobId(); + const datafeedId = datafeedIds[jobId]; + if (datafeedId === undefined) { + throw Boom.notFound(`Cannot find datafeed for job ${jobId}`); + } + + const dfResult = await callAsCurrentUser('ml.stopDatafeed', { datafeedId, force: true }); + if (!dfResult || dfResult.stopped !== true) { + return { success: false }; + } + + await callAsCurrentUser('ml.closeJob', { jobId, force: true }); + + return { success: true }; + } + async function jobsSummary(jobIds: string[] = []) { const fullJobsList: CombinedJobWithStats[] = await createFullJobsList(); const fullJobsIds = fullJobsList.map((job) => job.job_id); @@ -472,6 +490,7 @@ export function jobsProvider(callAsCurrentUser: APICaller) { forceDeleteJob, deleteJobs, closeJobs, + forceStopAndCloseJob, jobsSummary, jobsWithTimerange, createFullJobsList, diff --git a/x-pack/plugins/ml/server/models/job_service/model_snapshots.ts b/x-pack/plugins/ml/server/models/job_service/model_snapshots.ts new file mode 100644 index 0000000000000..4ffae17fc1c06 --- /dev/null +++ b/x-pack/plugins/ml/server/models/job_service/model_snapshots.ts @@ -0,0 +1,102 @@ +/* + * 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 Boom from 'boom'; +import { i18n } from '@kbn/i18n'; +import { APICaller } from 'kibana/server'; +import { ModelSnapshot } from '../../../common/types/anomaly_detection_jobs'; +import { datafeedsProvider, MlDatafeedsResponse } from './datafeeds'; +import { MlJobsResponse } from './jobs'; +import { FormCalendar, CalendarManager } from '../calendar'; + +export interface ModelSnapshotsResponse { + count: number; + model_snapshots: ModelSnapshot[]; +} +export interface RevertModelSnapshotResponse { + model: ModelSnapshot; +} + +export function modelSnapshotProvider(callAsCurrentUser: APICaller) { + const { forceStartDatafeeds, getDatafeedIdsByJobId } = datafeedsProvider(callAsCurrentUser); + + async function revertModelSnapshot( + jobId: string, + snapshotId: string, + replay: boolean, + end?: number, + deleteInterveningResults: boolean = true, + calendarEvents?: [{ start: number; end: number; description: string }] + ) { + let datafeedId = `datafeed-${jobId}`; + // ensure job exists + await callAsCurrentUser('ml.jobs', { jobId: [jobId] }); + + try { + // ensure the datafeed exists + // the datafeed is probably called datafeed- + await callAsCurrentUser('ml.datafeeds', { + datafeedId: [datafeedId], + }); + } catch (e) { + // if the datafeed isn't called datafeed- + // check all datafeeds to see if one exists that is matched to this job id + const datafeedIds = await getDatafeedIdsByJobId(); + datafeedId = datafeedIds[jobId]; + if (datafeedId === undefined) { + throw Boom.notFound(`Cannot find datafeed for job ${jobId}`); + } + } + + // ensure the snapshot exists + const snapshot = await callAsCurrentUser('ml.modelSnapshots', { + jobId, + snapshotId, + }); + + // apply the snapshot revert + const { model } = await callAsCurrentUser( + 'ml.revertModelSnapshot', + { + jobId, + snapshotId, + body: { + delete_intervening_results: deleteInterveningResults, + }, + } + ); + + // create calendar (if specified) and replay datafeed + if (replay && model.snapshot_id === snapshotId && snapshot.model_snapshots.length) { + // create calendar before starting restarting the datafeed + if (calendarEvents !== undefined && calendarEvents.length) { + const calendar: FormCalendar = { + calendarId: String(Date.now()), + job_ids: [jobId], + description: i18n.translate( + 'xpack.ml.models.jobService.revertModelSnapshot.autoCreatedCalendar.description', + { + defaultMessage: 'Auto created', + } + ), + events: calendarEvents.map((s) => ({ + description: s.description, + start_time: s.start, + end_time: s.end, + })), + }; + const cm = new CalendarManager(callAsCurrentUser); + await cm.newCalendar(calendar); + } + + forceStartDatafeeds([datafeedId], snapshot.model_snapshots[0].latest_record_time_stamp, end); + } + + return { success: true }; + } + + return { revertModelSnapshot }; +} diff --git a/x-pack/plugins/ml/server/routes/anomaly_detectors.ts b/x-pack/plugins/ml/server/routes/anomaly_detectors.ts index 63cd5498231af..0b544d4eca0ed 100644 --- a/x-pack/plugins/ml/server/routes/anomaly_detectors.ts +++ b/x-pack/plugins/ml/server/routes/anomaly_detectors.ts @@ -17,6 +17,8 @@ import { getCategoriesSchema, forecastAnomalyDetector, getBucketParamsSchema, + getModelSnapshotsSchema, + updateModelSnapshotSchema, } from './schemas/anomaly_detectors_schema'; /** @@ -526,7 +528,7 @@ export function jobRoutes({ router, mlLicense }: RouteInitialization) { /** * @apiGroup AnomalyDetectors * - * @api {get} /api/ml/anomaly_detectors/:jobId/results/categories/:categoryId Get results category data by job id and category id + * @api {get} /api/ml/anomaly_detectors/:jobId/results/categories/:categoryId Get results category data by job ID and category ID * @apiName GetCategories * @apiDescription Returns the categories results for the specified job ID and category ID. * @@ -544,11 +546,148 @@ export function jobRoutes({ router, mlLicense }: RouteInitialization) { }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { - const options = { + const results = await context.ml!.mlClient.callAsCurrentUser('ml.categories', { jobId: request.params.jobId, categoryId: request.params.categoryId, - }; - const results = await context.ml!.mlClient.callAsCurrentUser('ml.categories', options); + }); + return response.ok({ + body: results, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup AnomalyDetectors + * + * @api {get} /api/ml/anomaly_detectors/:jobId/model_snapshots Get model snapshots by job ID + * @apiName GetModelSnapshots + * @apiDescription Returns the model snapshots for the specified job ID + * + * @apiSchema (params) getModelSnapshotsSchema + */ + router.get( + { + path: '/api/ml/anomaly_detectors/{jobId}/model_snapshots', + validate: { + params: getModelSnapshotsSchema, + }, + options: { + tags: ['access:ml:canGetJobs'], + }, + }, + mlLicense.fullLicenseAPIGuard(async (context, request, response) => { + try { + const results = await context.ml!.mlClient.callAsCurrentUser('ml.modelSnapshots', { + jobId: request.params.jobId, + }); + return response.ok({ + body: results, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup AnomalyDetectors + * + * @api {get} /api/ml/anomaly_detectors/:jobId/model_snapshots/:snapshotId Get model snapshots by job ID and snapshot ID + * @apiName GetModelSnapshotsById + * @apiDescription Returns the model snapshots for the specified job ID and snapshot ID + * + * @apiSchema (params) getModelSnapshotsSchema + */ + router.get( + { + path: '/api/ml/anomaly_detectors/{jobId}/model_snapshots/{snapshotId}', + validate: { + params: getModelSnapshotsSchema, + }, + options: { + tags: ['access:ml:canGetJobs'], + }, + }, + mlLicense.fullLicenseAPIGuard(async (context, request, response) => { + try { + const results = await context.ml!.mlClient.callAsCurrentUser('ml.modelSnapshots', { + jobId: request.params.jobId, + snapshotId: request.params.snapshotId, + }); + return response.ok({ + body: results, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup AnomalyDetectors + * + * @api {post} /api/ml/anomaly_detectors/:jobId/model_snapshots/:snapshotId/_update update model snapshot by snapshot ID + * @apiName UpdateModelSnapshotsById + * @apiDescription Updates the model snapshot for the specified snapshot ID + * + * @apiSchema (params) getModelSnapshotsSchema + * @apiSchema (body) updateModelSnapshotSchema + */ + router.post( + { + path: '/api/ml/anomaly_detectors/{jobId}/model_snapshots/{snapshotId}/_update', + validate: { + params: getModelSnapshotsSchema, + body: updateModelSnapshotSchema, + }, + options: { + tags: ['access:ml:canCreateJob'], + }, + }, + mlLicense.fullLicenseAPIGuard(async (context, request, response) => { + try { + const results = await context.ml!.mlClient.callAsCurrentUser('ml.updateModelSnapshot', { + jobId: request.params.jobId, + snapshotId: request.params.snapshotId, + body: request.body, + }); + return response.ok({ + body: results, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup AnomalyDetectors + * + * @api {delete} /api/ml/anomaly_detectors/:jobId/model_snapshots/:snapshotId Delete model snapshots by snapshot ID + * @apiName GetModelSnapshotsById + * @apiDescription Deletes the model snapshot for the specified snapshot ID + * + * @apiSchema (params) getModelSnapshotsSchema + */ + router.delete( + { + path: '/api/ml/anomaly_detectors/{jobId}/model_snapshots/{snapshotId}', + validate: { + params: getModelSnapshotsSchema, + }, + options: { + tags: ['access:ml:canCreateJob'], + }, + }, + mlLicense.fullLicenseAPIGuard(async (context, request, response) => { + try { + const results = await context.ml!.mlClient.callAsCurrentUser('ml.deleteModelSnapshot', { + jobId: request.params.jobId, + snapshotId: request.params.snapshotId, + }); return response.ok({ body: results, }); diff --git a/x-pack/plugins/ml/server/routes/job_service.ts b/x-pack/plugins/ml/server/routes/job_service.ts index 05c44e1da9757..10d1c9952b540 100644 --- a/x-pack/plugins/ml/server/routes/job_service.ts +++ b/x-pack/plugins/ml/server/routes/job_service.ts @@ -17,8 +17,11 @@ import { lookBackProgressSchema, topCategoriesSchema, updateGroupsSchema, + revertModelSnapshotSchema, } from './schemas/job_service_schema'; +import { jobIdSchema } from './schemas/anomaly_detectors_schema'; + import { jobServiceProvider } from '../models/job_service'; import { categorizationExamplesProvider } from '../models/job_service/new_job'; @@ -162,6 +165,40 @@ export function jobServiceRoutes({ router, mlLicense }: RouteInitialization) { }) ); + /** + * @apiGroup JobService + * + * @api {post} /api/ml/jobs/force_stop_and_close_job Force stop and close job + * @apiName ForceStopAndCloseJob + * @apiDescription Force stops the datafeed and then force closes the anomaly detection job specified by job ID + * + * @apiSchema (body) jobIdSchema + */ + router.post( + { + path: '/api/ml/jobs/force_stop_and_close_job', + validate: { + body: jobIdSchema, + }, + options: { + tags: ['access:ml:canCloseJob', 'access:ml:canStartStopDatafeed'], + }, + }, + mlLicense.fullLicenseAPIGuard(async (context, request, response) => { + try { + const { forceStopAndCloseJob } = jobServiceProvider(context.ml!.mlClient.callAsCurrentUser); + const { jobId } = request.body; + const resp = await forceStopAndCloseJob(jobId); + + return response.ok({ + body: resp, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + /** * @apiGroup JobService * @@ -691,4 +728,52 @@ export function jobServiceRoutes({ router, mlLicense }: RouteInitialization) { } }) ); + + /** + * @apiGroup JobService + * + * @api {post} /api/ml/jobs/revert_model_snapshot Revert model snapshot + * @apiName RevertModelSnapshot + * @apiDescription Reverts a job to a specified snapshot. Also allows the job to replayed to a specified date and to auto create calendars to skip analysis of specified date ranges + * + * @apiSchema (body) revertModelSnapshotSchema + */ + router.post( + { + path: '/api/ml/jobs/revert_model_snapshot', + validate: { + body: revertModelSnapshotSchema, + }, + options: { + tags: ['access:ml:canCreateJob', 'access:ml:canStartStopDatafeed'], + }, + }, + mlLicense.fullLicenseAPIGuard(async (context, request, response) => { + try { + const { revertModelSnapshot } = jobServiceProvider(context.ml!.mlClient.callAsCurrentUser); + const { + jobId, + snapshotId, + replay, + end, + deleteInterveningResults, + calendarEvents, + } = request.body; + const resp = await revertModelSnapshot( + jobId, + snapshotId, + replay, + end, + deleteInterveningResults, + calendarEvents + ); + + return response.ok({ + body: resp, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); } diff --git a/x-pack/plugins/ml/server/routes/schemas/anomaly_detectors_schema.ts b/x-pack/plugins/ml/server/routes/schemas/anomaly_detectors_schema.ts index 3a01a616a4f7c..16eaab20fe8cb 100644 --- a/x-pack/plugins/ml/server/routes/schemas/anomaly_detectors_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/anomaly_detectors_schema.ts @@ -178,10 +178,24 @@ export const getOverallBucketsSchema = schema.object({ }); export const getCategoriesSchema = schema.object({ - /** Category id */ + /** Category ID */ categoryId: schema.string(), - /** Job id */ + /** Job ID */ jobId: schema.string(), }); +export const getModelSnapshotsSchema = schema.object({ + /** Snapshot ID */ + snapshotId: schema.maybe(schema.string()), + /** Job ID */ + jobId: schema.string(), +}); + +export const updateModelSnapshotSchema = schema.object({ + /** description */ + description: schema.maybe(schema.string()), + /** retain */ + retain: schema.maybe(schema.boolean()), +}); + export const forecastAnomalyDetector = schema.object({ duration: schema.any() }); diff --git a/x-pack/plugins/ml/server/routes/schemas/job_service_schema.ts b/x-pack/plugins/ml/server/routes/schemas/job_service_schema.ts index be107db9508fd..36e340adf0b31 100644 --- a/x-pack/plugins/ml/server/routes/schemas/job_service_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/job_service_schema.ts @@ -66,3 +66,20 @@ export const updateGroupsSchema = { ) ), }; + +export const revertModelSnapshotSchema = schema.object({ + jobId: schema.string(), + snapshotId: schema.string(), + replay: schema.boolean(), + end: schema.maybe(schema.number()), + deleteInterveningResults: schema.maybe(schema.boolean()), + calendarEvents: schema.maybe( + schema.arrayOf( + schema.object({ + start: schema.number(), + end: schema.number(), + description: schema.string(), + }) + ) + ), +}); diff --git a/x-pack/plugins/observability/public/pages/home/index.tsx b/x-pack/plugins/observability/public/pages/home/index.tsx index 072d3c47d3a55..696361393ef82 100644 --- a/x-pack/plugins/observability/public/pages/home/index.tsx +++ b/x-pack/plugins/observability/public/pages/home/index.tsx @@ -10,7 +10,6 @@ import { EuiFlexGrid, EuiFlexGroup, EuiFlexItem, - EuiHorizontalRule, EuiIcon, EuiImage, EuiSpacer, @@ -21,7 +20,7 @@ import { i18n } from '@kbn/i18n'; import React, { useEffect } from 'react'; import styled from 'styled-components'; import { usePluginContext } from '../../hooks/use_plugin_context'; -import { appsSection, tryItOutItemsSection } from './section'; +import { appsSection } from './section'; const Container = styled.div` min-height: calc(100vh - 48px); @@ -158,46 +157,6 @@ export const Home = () => { - - - - {/* Try it out */} - - - - -

- {i18n.translate('xpack.observability.home.tryItOut', { - defaultMessage: 'Try it out', - })} -

-
-
-
-
- - {/* Try it out sections */} - - - {tryItOutItemsSection.map((item) => ( - - } - title={ - -

{item.title}

-
- } - description={item.description} - target={item.target} - href={item.href} - /> -
- ))} -
- -
diff --git a/x-pack/plugins/observability/public/pages/home/section.ts b/x-pack/plugins/observability/public/pages/home/section.ts index f8bbfbfa30548..a2b82c31bf2ab 100644 --- a/x-pack/plugins/observability/public/pages/home/section.ts +++ b/x-pack/plugins/observability/public/pages/home/section.ts @@ -60,25 +60,3 @@ export const appsSection: ISection[] = [ }), }, ]; - -export const tryItOutItemsSection: ISection[] = [ - { - id: 'demo', - title: i18n.translate('xpack.observability.section.tryItOut.demo.title', { - defaultMessage: 'Demo Playground', - }), - icon: 'play', - description: '', - href: 'https://demo.elastic.co/', - target: '_blank', - }, - { - id: 'sampleData', - title: i18n.translate('xpack.observability.section.tryItOut.sampleData.title', { - defaultMessage: 'Add sample data', - }), - icon: 'documents', - description: '', - href: '/app/home#/tutorial_directory/sampleData', - }, -]; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts index 93883eeff5271..0c7bcdefd360d 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts @@ -235,6 +235,7 @@ export const pageOrUndefined = t.union([page, t.undefined]); export type PageOrUndefined = t.TypeOf; export const signal_ids = t.array(t.string); +export type SignalIds = t.TypeOf; // TODO: Can this be more strict or is this is the set of all Elastic Queries? export const signal_status_query = t.object; diff --git a/x-pack/plugins/security_solution/common/validate.test.ts b/x-pack/plugins/security_solution/common/validate.test.ts new file mode 100644 index 0000000000000..032f6d9590168 --- /dev/null +++ b/x-pack/plugins/security_solution/common/validate.test.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; + +import { validate } from './validate'; + +describe('validate', () => { + test('it should do a validation correctly', () => { + const schema = t.exact(t.type({ a: t.number })); + const payload = { a: 1 }; + const [validated, errors] = validate(payload, schema); + + expect(validated).toEqual(payload); + expect(errors).toEqual(null); + }); + + test('it should do an in-validation correctly', () => { + const schema = t.exact(t.type({ a: t.number })); + const payload = { a: 'some other value' }; + const [validated, errors] = validate(payload, schema); + + expect(validated).toEqual(null); + expect(errors).toEqual('Invalid value "some other value" supplied to "a"'); + }); +}); diff --git a/x-pack/plugins/security_solution/common/validate.ts b/x-pack/plugins/security_solution/common/validate.ts new file mode 100644 index 0000000000000..db9e286e2ebc2 --- /dev/null +++ b/x-pack/plugins/security_solution/common/validate.ts @@ -0,0 +1,25 @@ +/* + * 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 { fold } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import * as t from 'io-ts'; +import { exactCheck } from './exact_check'; +import { formatErrors } from './format_errors'; + +export const validate = ( + obj: object, + schema: T +): [t.TypeOf | null, string | null] => { + const decoded = schema.decode(obj); + const checked = exactCheck(obj, decoded); + const left = (errors: t.Errors): [T | null, string | null] => [ + null, + formatErrors(errors).join(','), + ]; + const right = (output: T): [T | null, string | null] => [output, null]; + return pipe(checked, fold(left, right)); +}; diff --git a/x-pack/plugins/security_solution/server/index.ts b/x-pack/plugins/security_solution/server/index.ts index 586b9dec2f4ab..8a77137c20c11 100644 --- a/x-pack/plugins/security_solution/server/index.ts +++ b/x-pack/plugins/security_solution/server/index.ts @@ -27,5 +27,4 @@ export { getPolicyExists } from './lib/detection_engine/index/get_policy_exists' export { createBootstrapIndex } from './lib/detection_engine/index/create_bootstrap_index'; export { getIndexExists } from './lib/detection_engine/index/get_index_exists'; export { buildRouteValidation } from './utils/build_validation/route_validation'; -export { validate } from './lib/detection_engine/routes/rules/validate'; export { transformError, buildSiemResponse } from './lib/detection_engine/routes/utils'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts index 39eea16c6290a..9878521c49322 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { validate } from '../../../../../common/validate'; import { PrePackagedRulesSchema, prePackagedRulesSchema, @@ -18,7 +19,6 @@ import { updatePrepackagedRules } from '../../rules/update_prepacked_rules'; import { getRulesToInstall } from '../../rules/get_rules_to_install'; import { getRulesToUpdate } from '../../rules/get_rules_to_update'; import { getExistingPrepackagedRules } from '../../rules/get_existing_prepackaged_rules'; -import { validate } from './validate'; export const addPrepackedRulesRoute = (router: IRouter) => { router.put( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts index 7af58adca7529..92a7ea17e7eaf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { validate } from '../../../../../common/validate'; import { createRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/create_rules_type_dependents'; import { RuleAlertAction } from '../../../../../common/detection_engine/types'; import { @@ -19,7 +20,7 @@ import { throwHttpError } from '../../../machine_learning/validation'; import { createRules } from '../../rules/create_rules'; import { readRules } from '../../rules/read_rules'; import { getDuplicates } from './utils'; -import { transformValidateBulkError, validate } from './validate'; +import { transformValidateBulkError } from './validate'; import { getIndexExists } from '../../index/get_index_exists'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts index 12f908ce7e8b5..99bf16aadc815 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { validate } from '../../../../../common/validate'; import { queryRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/query_rules_type_dependents'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; import { @@ -14,7 +15,7 @@ import { rulesBulkSchema } from '../../../../../common/detection_engine/schemas/ import { IRouter, RouteConfig, RequestHandler } from '../../../../../../../../src/core/server'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { getIdBulkError } from './utils'; -import { transformValidateBulkError, validate } from './validate'; +import { transformValidateBulkError } from './validate'; import { transformBulkError, buildSiemResponse, createBulkErrorObject } from '../utils'; import { deleteRules } from '../../rules/delete_rules'; import { deleteNotifications } from '../../notifications/delete_notifications'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts index c3f4695a20461..bc199ee132e96 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { validate } from '../../../../../common/validate'; import { PrePackagedRulesStatusSchema, prePackagedRulesStatusSchema, @@ -16,7 +17,6 @@ import { getRulesToInstall } from '../../rules/get_rules_to_install'; import { getRulesToUpdate } from '../../rules/get_rules_to_update'; import { findRules } from '../../rules/find_rules'; import { getExistingPrepackagedRules } from '../../rules/get_existing_prepackaged_rules'; -import { validate } from './validate'; export const getPrepackagedRulesStatusRoute = (router: IRouter) => { router.get( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts index c8b61414608a9..a277f97ccf9f0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts @@ -7,6 +7,7 @@ import { chunk } from 'lodash/fp'; import { extname } from 'path'; +import { validate } from '../../../../../common/validate'; import { importRulesQuerySchema, ImportRulesQuerySchemaDecoded, @@ -39,7 +40,6 @@ import { } from '../utils'; import { patchRules } from '../../rules/patch_rules'; import { getTupleDuplicateErrorsAndUniqueRules } from './utils'; -import { validate } from './validate'; import { createRulesStreamFromNdJson } from '../../rules/create_rules_stream_from_ndjson'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; import { HapiReadableStream } from '../../rules/types'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts index 16f491547a9e6..b2a9fdd103a68 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { validate } from '../../../../../common/validate'; import { RuleAlertAction } from '../../../../../common/detection_engine/types'; import { patchRulesBulkSchema, @@ -18,7 +19,7 @@ import { buildMlAuthz } from '../../../machine_learning/authz'; import { throwHttpError } from '../../../machine_learning/validation'; import { transformBulkError, buildSiemResponse } from '../utils'; import { getIdBulkError } from './utils'; -import { transformValidateBulkError, validate } from './validate'; +import { transformValidateBulkError } from './validate'; import { patchRules } from '../../rules/patch_rules'; import { updateRulesNotifications } from '../../rules/update_rules_notifications'; import { ruleStatusSavedObjectsClientFactory } from '../../signals/rule_status_saved_objects_client'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts index 3ca4a28dd93ee..1e6815a357154 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { validate } from '../../../../../common/validate'; import { updateRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/update_rules_type_dependents'; import { RuleAlertAction } from '../../../../../common/detection_engine/types'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; @@ -18,7 +19,7 @@ import { SetupPlugins } from '../../../../plugin'; import { buildMlAuthz } from '../../../machine_learning/authz'; import { throwHttpError } from '../../../machine_learning/validation'; import { getIdBulkError } from './utils'; -import { transformValidateBulkError, validate } from './validate'; +import { transformValidateBulkError } from './validate'; import { transformBulkError, buildSiemResponse, createBulkErrorObject } from '../utils'; import { updateRules } from '../../rules/update_rules'; import { updateRulesNotifications } from '../../rules/update_rules_notifications'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts index 07b891e2bf021..1f5442e23d884 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts @@ -4,10 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import * as t from 'io-ts'; - import { - validate, transformValidate, transformValidateFindAlerts, transformValidateBulkError, @@ -121,26 +118,6 @@ describe('validate', () => { unSetFeatureFlagsForTestsOnly(); }); - describe('validate', () => { - test('it should do a validation correctly', () => { - const schema = t.exact(t.type({ a: t.number })); - const payload = { a: 1 }; - const [validated, errors] = validate(payload, schema); - - expect(validated).toEqual(payload); - expect(errors).toEqual(null); - }); - - test('it should do an in-validation correctly', () => { - const schema = t.exact(t.type({ a: t.number })); - const payload = { a: 'some other value' }; - const [validated, errors] = validate(payload, schema); - - expect(validated).toEqual(null); - expect(errors).toEqual('Invalid value "some other value" supplied to "a"'); - }); - }); - describe('transformValidate', () => { test('it should do a validation correctly of a partial alert', () => { const ruleAlert = getResult(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts index 7b0bf5997d12f..983382b28ab38 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts @@ -9,6 +9,7 @@ import { fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; import * as t from 'io-ts'; +import { validate } from '../../../../../common/validate'; import { findRulesSchema } from '../../../../../common/detection_engine/schemas/response/find_rules_schema'; import { RulesSchema, @@ -113,17 +114,3 @@ export const transformValidateBulkError = ( }); } }; - -export const validate = ( - obj: object, - schema: T -): [t.TypeOf | null, string | null] => { - const decoded = schema.decode(obj); - const checked = exactCheck(obj, decoded); - const left = (errors: t.Errors): [T | null, string | null] => [ - null, - formatErrors(errors).join(','), - ]; - const right = (output: T): [T | null, string | null] => [output, null]; - return pipe(checked, fold(left, right)); -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts index 18c3440ab93e1..ac5132d93a069 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts @@ -55,6 +55,7 @@ export const setSignalsStatusRoute = (router: IRouter) => { try { const result = await clusterClient.callAsCurrentUser('updateByQuery', { index: siemClient.getSignalsIndex(), + refresh: 'wait_for', body: { script: { source: `ctx._source.signal.status = '${status}'`, diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.ts index 0f9e97cfc2106..5080142f22b15 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.ts @@ -7,6 +7,7 @@ import { extname } from 'path'; import { chunk, omit } from 'lodash/fp'; +import { validate } from '../../../../common/validate'; import { importRulesSchema } from '../../../../common/detection_engine/schemas/response/import_rules_schema'; import { createPromiseFromStreams } from '../../../../../../../src/legacy/utils'; import { IRouter } from '../../../../../../../src/core/server'; @@ -17,7 +18,6 @@ import { SetupPlugins } from '../../../plugin'; import { ConfigType } from '../../../config'; import { buildRouteValidation } from '../../../utils/build_validation/route_validation'; -import { validate } from '../../detection_engine/routes/rules/validate'; import { buildSiemResponse, createBulkErrorObject, diff --git a/x-pack/plugins/spaces/kibana.json b/x-pack/plugins/spaces/kibana.json index 7e6d0425f2ae0..9483cb67392c4 100644 --- a/x-pack/plugins/spaces/kibana.json +++ b/x-pack/plugins/spaces/kibana.json @@ -13,5 +13,6 @@ "savedObjectsManagement" ], "server": true, - "ui": true + "ui": true, + "extraPublicDirs": ["common"] } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 8888120d59f88..7b887867b43b8 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -8521,28 +8521,18 @@ "xpack.ingestPipelines.form.nameDescription": "このパイプラインの固有の識別子です。", "xpack.ingestPipelines.form.nameFieldLabel": "名前", "xpack.ingestPipelines.form.nameTitle": "名前", - "xpack.ingestPipelines.form.onFailureDescription": "プロセッサーが失敗した後に実行する代替プロセッサー。{learnMoreLink}", - "xpack.ingestPipelines.form.onFailureDocumentionLink": "詳細", - "xpack.ingestPipelines.form.onFailureFieldAriaLabel": "障害プロセッサーJSONエディター", "xpack.ingestPipelines.form.onFailureFieldHelpText": "JSONフォーマットを使用:{code}", "xpack.ingestPipelines.form.onFailureFieldLabel": "障害プロセッサー(任意)", "xpack.ingestPipelines.form.onFailureProcessorsJsonError": "入力が無効です。", - "xpack.ingestPipelines.form.onFailureTitle": "障害プロセッサー", - "xpack.ingestPipelines.form.onFailureToggleDescription": "障害プロセッサーを追加", "xpack.ingestPipelines.form.pipelineNameRequiredError": "名前が必要です。", - "xpack.ingestPipelines.form.processorsDocumentionLink": "詳細", - "xpack.ingestPipelines.form.processorsFieldAriaLabel": "プロセッサーJSONエディター", - "xpack.ingestPipelines.form.processorsFieldDescription": "インデックスの前にドキュメントを変換するために使用するプロセッサー。{learnMoreLink}", "xpack.ingestPipelines.form.processorsFieldHelpText": "JSONフォーマットを使用:{code}", "xpack.ingestPipelines.form.processorsFieldLabel": "プロセッサー", - "xpack.ingestPipelines.form.processorsFieldTitle": "プロセッサー", "xpack.ingestPipelines.form.processorsJsonError": "入力が無効です。", "xpack.ingestPipelines.form.processorsRequiredError": "プロセッサーが必要です。", "xpack.ingestPipelines.form.saveButtonLabel": "パイプラインを保存", "xpack.ingestPipelines.form.savePipelineError": "パイプラインを作成できません", "xpack.ingestPipelines.form.savingButtonLabel": "保存中…", "xpack.ingestPipelines.form.showRequestButtonLabel": "リクエストを表示", - "xpack.ingestPipelines.form.testPipelineButtonLabel": "パイプラインをテスト", "xpack.ingestPipelines.form.versionFieldLabel": "バージョン(任意)", "xpack.ingestPipelines.form.versionToggleDescription": "バージョン番号を追加", "xpack.ingestPipelines.licenseCheckErrorMessage": "ライセンス確認失敗", @@ -10455,7 +10445,6 @@ "xpack.ml.navMenu.dataVisualizerTabLinkText": "データビジュアライザー", "xpack.ml.navMenu.overviewTabLinkText": "概要", "xpack.ml.navMenu.settingsTabLinkText": "設定", - "xpack.ml.newJi18n(ob.recognize.jobsCreationFailed.resetButtonAriaLabel": "リセット", "xpack.ml.newJob.page.createJob": "ジョブを作成", "xpack.ml.newJob.recognize.advancedLabel": "高度な設定", "xpack.ml.newJob.recognize.advancedSettingsAriaLabel": "高度な設定", @@ -16617,7 +16606,6 @@ "xpack.uptime.locationMap.locations.missing.message": "重要な位置情報構成がありません。{codeBlock}フィールドを使用して、アップタイムチェック用に一意の地域を作成できます。", "xpack.uptime.locationMap.locations.missing.message1": "詳細については、ドキュメンテーションを参照してください。", "xpack.uptime.locationMap.locations.missing.title": "地理情報の欠測", - "xpack.uptime.locationMap.locations.tags.others": "{otherLoc}その他 ...", "xpack.uptime.locationName.helpLinkAnnotation": "場所を追加", "xpack.uptime.ml.durationChart.exploreInMlApp": "ML アプリで探索", "xpack.uptime.ml.enableAnomalyDetectionPanel.anomalyDetectionTitle": "異常検知", @@ -16701,12 +16689,7 @@ "xpack.uptime.monitorStatusBar.locations.oneLocStatus": "{loc}場所での{status}", "xpack.uptime.monitorStatusBar.locations.upStatus": "{loc}場所での{status}", "xpack.uptime.monitorStatusBar.monitorUrlLinkAriaLabel": "監視 URL リンク", - "xpack.uptime.monitorStatusBar.sslCertificate.overview": "証明書概要", "xpack.uptime.monitorStatusBar.sslCertificate.title": "証明書", - "xpack.uptime.monitorStatusBar.sslCertificateExpired.badgeContent": "{emphasizedText}が期限切れになりました", - "xpack.uptime.monitorStatusBar.sslCertificateExpired.label.ariaLabel": "{validityDate}に期限切れになりました", - "xpack.uptime.monitorStatusBar.sslCertificateExpiry.badgeContent": "{emphasizedText}が期限切れになります", - "xpack.uptime.monitorStatusBar.sslCertificateExpiry.label.ariaLabel": "{validityDate}に期限切れになります", "xpack.uptime.monitorStatusBar.timestampFromNowTextAriaLabel": "最終確認からの経過時間", "xpack.uptime.navigateToAlertingButton.content": "アラートを管理", "xpack.uptime.navigateToAlertingUi": "Uptime を離れてアラート管理ページに移動します", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 3109bab4b9426..e821c4fb22899 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -8525,28 +8525,18 @@ "xpack.ingestPipelines.form.nameDescription": "此管道的唯一标识符。", "xpack.ingestPipelines.form.nameFieldLabel": "名称", "xpack.ingestPipelines.form.nameTitle": "名称", - "xpack.ingestPipelines.form.onFailureDescription": "处理器失败后要执行的备用处理器。{learnMoreLink}", - "xpack.ingestPipelines.form.onFailureDocumentionLink": "了解详情", - "xpack.ingestPipelines.form.onFailureFieldAriaLabel": "失败处理器 JSON 编辑器", "xpack.ingestPipelines.form.onFailureFieldHelpText": "使用 JSON 格式:{code}", "xpack.ingestPipelines.form.onFailureFieldLabel": "失败处理器(可选)", "xpack.ingestPipelines.form.onFailureProcessorsJsonError": "输入无效。", - "xpack.ingestPipelines.form.onFailureTitle": "失败处理器", - "xpack.ingestPipelines.form.onFailureToggleDescription": "添加失败处理器", "xpack.ingestPipelines.form.pipelineNameRequiredError": "“名称”必填。", - "xpack.ingestPipelines.form.processorsDocumentionLink": "了解详情", - "xpack.ingestPipelines.form.processorsFieldAriaLabel": "处理器 JSON 编辑器", - "xpack.ingestPipelines.form.processorsFieldDescription": "用于在索引之前转换文档的处理器。{learnMoreLink}", "xpack.ingestPipelines.form.processorsFieldHelpText": "使用 JSON 格式:{code}", "xpack.ingestPipelines.form.processorsFieldLabel": "处理器", - "xpack.ingestPipelines.form.processorsFieldTitle": "处理器", "xpack.ingestPipelines.form.processorsJsonError": "输入无效。", "xpack.ingestPipelines.form.processorsRequiredError": "需要指定处理器。", "xpack.ingestPipelines.form.saveButtonLabel": "保存管道", "xpack.ingestPipelines.form.savePipelineError": "无法创建管道", "xpack.ingestPipelines.form.savingButtonLabel": "正在保存......", "xpack.ingestPipelines.form.showRequestButtonLabel": "显示请求", - "xpack.ingestPipelines.form.testPipelineButtonLabel": "测试管道", "xpack.ingestPipelines.form.versionFieldLabel": "版本(可选)", "xpack.ingestPipelines.form.versionToggleDescription": "添加版本号", "xpack.ingestPipelines.licenseCheckErrorMessage": "许可证检查失败", @@ -10459,7 +10449,6 @@ "xpack.ml.navMenu.dataVisualizerTabLinkText": "数据可视化工具", "xpack.ml.navMenu.overviewTabLinkText": "概览", "xpack.ml.navMenu.settingsTabLinkText": "设置", - "xpack.ml.newJi18n(ob.recognize.jobsCreationFailed.resetButtonAriaLabel": "重置", "xpack.ml.newJob.page.createJob": "创建作业", "xpack.ml.newJob.recognize.advancedLabel": "高级", "xpack.ml.newJob.recognize.advancedSettingsAriaLabel": "高级设置", @@ -16623,7 +16612,6 @@ "xpack.uptime.locationMap.locations.missing.message": "重要的地理位置配置缺失。您可以使用 {codeBlock} 字段为您的运行时间检查创建独特的地理区域。", "xpack.uptime.locationMap.locations.missing.message1": "在我们的文档中获取更多的信息。", "xpack.uptime.locationMap.locations.missing.title": "地理信息缺失", - "xpack.uptime.locationMap.locations.tags.others": "{otherLoc} 其他......", "xpack.uptime.locationName.helpLinkAnnotation": "添加位置", "xpack.uptime.ml.durationChart.exploreInMlApp": "在 ML 应用中浏览", "xpack.uptime.ml.enableAnomalyDetectionPanel.anomalyDetectionTitle": "异常检测", @@ -16707,12 +16695,7 @@ "xpack.uptime.monitorStatusBar.locations.oneLocStatus": "在 {loc} 位置{status}", "xpack.uptime.monitorStatusBar.locations.upStatus": "在 {loc} 位置{status}", "xpack.uptime.monitorStatusBar.monitorUrlLinkAriaLabel": "监测 URL 链接", - "xpack.uptime.monitorStatusBar.sslCertificate.overview": "证书概览", "xpack.uptime.monitorStatusBar.sslCertificate.title": "证书", - "xpack.uptime.monitorStatusBar.sslCertificateExpired.badgeContent": "{emphasizedText}过期", - "xpack.uptime.monitorStatusBar.sslCertificateExpired.label.ariaLabel": "已于 {validityDate}过期", - "xpack.uptime.monitorStatusBar.sslCertificateExpiry.badgeContent": "{emphasizedText}过期", - "xpack.uptime.monitorStatusBar.sslCertificateExpiry.label.ariaLabel": "将于 {validityDate}过期", "xpack.uptime.monitorStatusBar.timestampFromNowTextAriaLabel": "自上次检查以来经过的时间", "xpack.uptime.navigateToAlertingButton.content": "管理告警", "xpack.uptime.navigateToAlertingUi": "离开 Uptime 并前往“Alerting 管理”页面", diff --git a/x-pack/plugins/triggers_actions_ui/kibana.json b/x-pack/plugins/triggers_actions_ui/kibana.json index d8f5055368831..158cfa100d546 100644 --- a/x-pack/plugins/triggers_actions_ui/kibana.json +++ b/x-pack/plugins/triggers_actions_ui/kibana.json @@ -5,5 +5,6 @@ "ui": true, "optionalPlugins": ["alerts", "alertingBuiltins"], "requiredPlugins": ["management", "charts", "data"], - "configPath": ["xpack", "trigger_actions_ui"] + "configPath": ["xpack", "trigger_actions_ui"], + "extraPublicDirs": ["public/common", "public/common/constants"] } diff --git a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/dynamic_action_manager.ts b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/dynamic_action_manager.ts index 3e37bac0a17af..58344026079e7 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/dynamic_action_manager.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/dynamic_action_manager.ts @@ -12,7 +12,10 @@ import { UiActionsActionDefinition as ActionDefinition, } from '../../../../../src/plugins/ui_actions/public'; import { defaultState, transitions, selectors, State } from './dynamic_action_manager_state'; -import { StateContainer, createStateContainer } from '../../../../../src/plugins/kibana_utils'; +import { + StateContainer, + createStateContainer, +} from '../../../../../src/plugins/kibana_utils/common'; import { StartContract } from '../plugin'; import { SerializedAction, SerializedEvent } from './types'; diff --git a/x-pack/plugins/uptime/common/runtime_types/common.ts b/x-pack/plugins/uptime/common/runtime_types/common.ts index e07c46fa01cfe..603d242d4dcd0 100644 --- a/x-pack/plugins/uptime/common/runtime_types/common.ts +++ b/x-pack/plugins/uptime/common/runtime_types/common.ts @@ -6,15 +6,19 @@ import * as t from 'io-ts'; -export const LocationType = t.partial({ +export const LocationType = t.type({ lat: t.string, lon: t.string, }); -export const CheckGeoType = t.partial({ - name: t.string, - location: LocationType, -}); +export const CheckGeoType = t.intersection([ + t.type({ + name: t.string, + }), + t.partial({ + location: LocationType, + }), +]); export const SummaryType = t.partial({ up: t.number, @@ -34,5 +38,6 @@ export const DateRangeType = t.type({ export type Summary = t.TypeOf; export type Location = t.TypeOf; +export type GeoPoint = t.TypeOf; export type StatesIndexStatus = t.TypeOf; export type DateRange = t.TypeOf; diff --git a/x-pack/plugins/uptime/common/runtime_types/monitor/locations.ts b/x-pack/plugins/uptime/common/runtime_types/monitor/locations.ts index ea3cfe677ca99..00ed1dc407e98 100644 --- a/x-pack/plugins/uptime/common/runtime_types/monitor/locations.ts +++ b/x-pack/plugins/uptime/common/runtime_types/monitor/locations.ts @@ -7,17 +7,23 @@ import * as t from 'io-ts'; import { CheckGeoType, SummaryType } from '../common'; // IO type for validation -export const MonitorLocationType = t.partial({ +export const MonitorLocationType = t.type({ + up_history: t.number, + down_history: t.number, + timestamp: t.string, summary: SummaryType, geo: CheckGeoType, - timestamp: t.string, }); // Typescript type for type checking export type MonitorLocation = t.TypeOf; export const MonitorLocationsType = t.intersection([ - t.type({ monitorId: t.string }), + t.type({ + monitorId: t.string, + up_history: t.number, + down_history: t.number, + }), t.partial({ locations: t.array(MonitorLocationType) }), ]); export type MonitorLocations = t.TypeOf; diff --git a/x-pack/plugins/uptime/public/components/certificates/translations.ts b/x-pack/plugins/uptime/public/components/certificates/translations.ts index 518eddf1211a4..176625d647ca0 100644 --- a/x-pack/plugins/uptime/public/components/certificates/translations.ts +++ b/x-pack/plugins/uptime/public/components/certificates/translations.ts @@ -18,6 +18,10 @@ export const EXPIRES_SOON = i18n.translate('xpack.uptime.certs.expireSoon', { defaultMessage: 'Expires soon', }); +export const EXPIRES = i18n.translate('xpack.uptime.certs.expires', { + defaultMessage: 'Expires', +}); + export const SEARCH_CERTS = i18n.translate('xpack.uptime.certs.searchCerts', { defaultMessage: 'Search certificates', }); diff --git a/x-pack/plugins/uptime/public/components/common/translations.ts b/x-pack/plugins/uptime/public/components/common/translations.ts new file mode 100644 index 0000000000000..d2c466ddf0c83 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/common/translations.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const URL_LABEL = i18n.translate('xpack.uptime.monitorList.table.url.name', { + defaultMessage: 'Url', +}); diff --git a/x-pack/plugins/uptime/public/components/monitor/index.ts b/x-pack/plugins/uptime/public/components/monitor/index.ts index cb7b27afded02..fd9a9a2c897d7 100644 --- a/x-pack/plugins/uptime/public/components/monitor/index.ts +++ b/x-pack/plugins/uptime/public/components/monitor/index.ts @@ -6,7 +6,7 @@ export * from './ml'; export * from './ping_list'; -export * from './location_map'; -export * from './monitor_status_details'; +export * from './status_details/location_map'; +export * from './status_details'; export * from './ping_histogram'; export * from './monitor_charts'; diff --git a/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_map.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_map.test.tsx.snap deleted file mode 100644 index 7b847782fe1ac..0000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_map.test.tsx.snap +++ /dev/null @@ -1,282 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`LocationMap component doesnt shows warning if geo is provided 1`] = ` - - - - - - - - - - - - - - -`; - -exports[`LocationMap component renders correctly against snapshot 1`] = ` - - - - - - - - - - - - - - - -`; - -exports[`LocationMap component renders named locations that have missing geo data 1`] = ` - - - - - - - - - - - - - - - -`; - -exports[`LocationMap component shows warning if geo information is missing 1`] = ` - - - - - - - - - - - - - - - -`; diff --git a/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_status_tags.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_status_tags.test.tsx.snap deleted file mode 100644 index f2a390792918a..0000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_status_tags.test.tsx.snap +++ /dev/null @@ -1,682 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`LocationStatusTags component renders properly against props 1`] = ` - - - - - - - - Berlin - - - - - - 1 Mon ago - - - - - - - - - - Berlin - - - - - - 1 Mon ago - - - - - - - - Islamabad - - - - - - 1 Mon ago - - - - - - -`; - -exports[`LocationStatusTags component renders when all locations are down 1`] = ` -.c3 { - display: inline-block; - margin-left: 4px; -} - -.c2 { - font-weight: 600; -} - -.c1 { - margin-bottom: 5px; - white-space: nowrap; -} - -.c0 { - max-height: 229px; - overflow: hidden; - margin-top: auto; -} - -@media (max-width:1042px) { - .c1 { - display: inline-block; - margin-right: 16px; - } -} - -
- -
- - - -
-
- Islamabad -
-
-
-
-
- -
-
- 5s ago -
-
-
-
-
- - - -
-
- Berlin -
-
-
-
-
- -
-
- 5m ago -
-
-
-
-
- -
-`; - -exports[`LocationStatusTags component renders when all locations are up 1`] = ` -.c3 { - display: inline-block; - margin-left: 4px; -} - -.c2 { - font-weight: 600; -} - -.c1 { - margin-bottom: 5px; - white-space: nowrap; -} - -.c0 { - max-height: 229px; - overflow: hidden; - margin-top: auto; -} - -@media (max-width:1042px) { - .c1 { - display: inline-block; - margin-right: 16px; - } -} - -
- - -
- - - -
-
- Berlin -
-
-
-
-
- -
-
- 5d ago -
-
-
-
-
- - - -
-
- Islamabad -
-
-
-
-
- -
-
- 5s ago -
-
-
-
-
-
-`; - -exports[`LocationStatusTags component renders when there are many location 1`] = ` -Array [ - .c3 { - display: inline-block; - margin-left: 4px; -} - -.c2 { - font-weight: 600; -} - -.c1 { - margin-bottom: 5px; - white-space: nowrap; -} - -.c0 { - max-height: 229px; - overflow: hidden; - margin-top: auto; -} - -@media (max-width:1042px) { - .c1 { - display: inline-block; - margin-right: 16px; - } -} - -
- -
- - - -
-
- Islamabad -
-
-
-
-
- -
-
- 5s ago -
-
-
-
-
- - - -
-
- Berlin -
-
-
-
-
- -
-
- 5m ago -
-
-
-
-
- - - -
-
- st-paul -
-
-
-
-
- -
-
- 5h ago -
-
-
-
-
- - - -
-
- Tokyo -
-
-
-
-
- -
-
- 5d ago -
-
-
-
-
- - - -
-
- New York -
-
-
-
-
- -
-
- 1 Mon ago -
-
-
-
-
- - - -
-
- Toronto -
-
-
-
-
- -
-
- 5 Mon ago -
-
-
-
-
- - - -
-
- Sydney -
-
-
-
-
- -
-
- 5 Yr ago -
-
-
-
-
- - - -
-
- Paris -
-
-
-
-
- -
-
- 5 Yr ago -
-
-
-
-
- -
, - .c0 { - padding-left: 18px; -} - -@media (max-width:1042px) { - -} - -
-
-
-

- 1 Others ... -

-
-
-
, -] -`; diff --git a/x-pack/plugins/uptime/public/components/monitor/location_map/location_map.tsx b/x-pack/plugins/uptime/public/components/monitor/location_map/location_map.tsx deleted file mode 100644 index 916f1cbb63e53..0000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/location_map/location_map.tsx +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import styled from 'styled-components'; -import { EuiFlexGroup, EuiFlexItem, EuiErrorBoundary, EuiHideFor } from '@elastic/eui'; -import { LocationStatusTags } from './location_status_tags'; -import { EmbeddedMap, LocationPoint } from './embeddables/embedded_map'; -import { MonitorLocations, MonitorLocation } from '../../../../common/runtime_types'; -import { UNNAMED_LOCATION } from '../../../../common/constants'; -import { LocationMissingWarning } from './location_missing'; - -// These height/width values are used to make sure map is in center of panel -// And to make sure, it doesn't take too much space -const MapPanel = styled.div` - height: 240px; - width: 520px; - @media (min-width: 1300px) { - margin-right: 20px; - } - @media (max-width: 574px) { - height: 250px; - width: 100%; - margin-right: 0; - } -`; - -const EuiFlexItemTags = styled(EuiFlexItem)` - padding-top: 5px; - @media (max-width: 1042px) { - flex-basis: 80% !important; - flex-grow: 0 !important; - order: 1; - } -`; - -const FlexGroup = styled(EuiFlexGroup)` - @media (max-width: 850px) { - justify-content: center; - } -`; - -interface LocationMapProps { - monitorLocations: MonitorLocations; -} - -export const LocationMap = ({ monitorLocations }: LocationMapProps) => { - const upPoints: LocationPoint[] = []; - const downPoints: LocationPoint[] = []; - - let isGeoInfoMissing = false; - - if (monitorLocations?.locations) { - monitorLocations.locations.forEach((item: MonitorLocation) => { - if (item.geo?.name === UNNAMED_LOCATION || !item.geo?.location) { - isGeoInfoMissing = true; - } else if ( - item.geo?.name !== UNNAMED_LOCATION && - !!item.geo.location.lat && - !!item.geo.location.lon - ) { - // TypeScript doesn't infer that the above checks in this block's condition - // ensure that lat and lon are defined when we try to pass the location object directly, - // but if we destructure the values it does. Improvement to this block is welcome. - const { lat, lon } = item.geo.location; - if (item?.summary?.down === 0) { - upPoints.push({ lat, lon }); - } else { - downPoints.push({ lat, lon }); - } - } - }); - } - - return ( - - - - - - - - {isGeoInfoMissing && } - - - - - - - - ); -}; diff --git a/x-pack/plugins/uptime/public/components/monitor/location_map/location_status_tags.tsx b/x-pack/plugins/uptime/public/components/monitor/location_map/location_status_tags.tsx deleted file mode 100644 index db84fd5e2ca42..0000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/location_map/location_status_tags.tsx +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useContext } from 'react'; -import moment from 'moment'; -import styled from 'styled-components'; -import { EuiBadge, EuiText } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { UptimeThemeContext } from '../../../contexts'; -import { MonitorLocation } from '../../../../common/runtime_types'; -import { SHORT_TIMESPAN_LOCALE, SHORT_TS_LOCALE } from '../../../../common/constants'; - -const TimeStampSpan = styled.span` - display: inline-block; - margin-left: 4px; -`; - -const TextStyle = styled.div` - font-weight: 600; -`; - -const BadgeItem = styled.div` - margin-bottom: 5px; - white-space: nowrap; - @media (max-width: 1042px) { - display: inline-block; - margin-right: 16px; - } -`; - -// Set height so that it remains within panel, enough height to display 7 locations tags -const TagContainer = styled.div` - max-height: 229px; - overflow: hidden; - margin-top: auto; -`; - -const OtherLocationsDiv = styled.div` - padding-left: 18px; -`; - -interface Props { - locations: MonitorLocation[]; -} - -interface StatusTag { - label: string; - timestamp: number; -} - -export const LocationStatusTags = ({ locations }: Props) => { - const { - colors: { gray, danger }, - } = useContext(UptimeThemeContext); - - const upLocations: StatusTag[] = []; - const downLocations: StatusTag[] = []; - - locations.forEach((item: any) => { - if (item.summary.down === 0) { - upLocations.push({ label: item.geo.name, timestamp: new Date(item.timestamp).valueOf() }); - } else { - downLocations.push({ label: item.geo.name, timestamp: new Date(item.timestamp).valueOf() }); - } - }); - - // Sort lexicographically by label - upLocations.sort((a, b) => { - return a.label > b.label ? 1 : b.label > a.label ? -1 : 0; - }); - - const tagLabel = (item: StatusTag, ind: number, color: string) => { - return ( - - - - {item.label} - - - - {moment(item.timestamp).fromNow()} - - - ); - }; - - const prevLocal: string = moment.locale() ?? 'en'; - - const renderTags = () => { - const shortLocale = moment.locale(SHORT_TS_LOCALE) === SHORT_TS_LOCALE; - if (!shortLocale) { - moment.defineLocale(SHORT_TS_LOCALE, SHORT_TIMESPAN_LOCALE); - } - - const tags = ( - - {downLocations.map((item, ind) => tagLabel(item, ind, danger))} - {upLocations.map((item, ind) => tagLabel(item, ind, gray))} - - ); - - // Need to reset locale so it doesn't effect other parts of the app - moment.locale(prevLocal); - return tags; - }; - - return ( - <> - {renderTags()} - {locations.length > 7 && ( - - -

- -

-
-
- )} - - ); -}; diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/monitor_status.bar.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/monitor_status.bar.test.tsx.snap deleted file mode 100644 index ff63b3695fb8d..0000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/monitor_status.bar.test.tsx.snap +++ /dev/null @@ -1,55 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`MonitorStatusBar component renders duration in ms, not us 1`] = ` -
-
-
-

- Up in 2 Locations -

-
-
- -
- -

- id1 -

-
-
-
-
-
-`; diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/ssl_certificate.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/ssl_certificate.tsx deleted file mode 100644 index 73b58e8a33f6b..0000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/ssl_certificate.tsx +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import moment from 'moment'; -import { i18n } from '@kbn/i18n'; -import { Link } from 'react-router-dom'; -import { EuiSpacer, EuiText, EuiBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { Tls } from '../../../../../common/runtime_types'; -import { useCertStatus } from '../../../../hooks'; -import { CERT_STATUS, CERTIFICATES_ROUTE } from '../../../../../common/constants'; - -interface Props { - /** - * TLS information coming from monitor in ES heartbeat index - */ - tls: Tls | null | undefined; -} - -export const MonitorSSLCertificate = ({ tls }: Props) => { - const certStatus = useCertStatus(tls?.not_after); - - const isExpiringSoon = certStatus === CERT_STATUS.EXPIRING_SOON; - - const isExpired = certStatus === CERT_STATUS.EXPIRED; - - const relativeDate = moment(tls?.not_after).fromNow(); - - return certStatus ? ( - <> - - {i18n.translate('xpack.uptime.monitorStatusBar.sslCertificate.title', { - defaultMessage: 'Certificate:', - })} - - - - - - {isExpired ? ( - {relativeDate}, - }} - /> - ) : ( - - {relativeDate} - - ), - }} - /> - )} - - - - - - {i18n.translate('xpack.uptime.monitorStatusBar.sslCertificate.overview', { - defaultMessage: 'Certificate overview', - })} - - - - - - ) : null; -}; diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar.tsx deleted file mode 100644 index 36159dc29eccd..0000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar.tsx +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { - EuiLink, - EuiTitle, - EuiTextColor, - EuiSpacer, - EuiText, - EuiFlexGroup, - EuiFlexItem, -} from '@elastic/eui'; -import { MonitorSSLCertificate } from './ssl_certificate'; -import * as labels from './translations'; -import { StatusByLocations } from './status_by_location'; -import { Ping } from '../../../../../common/runtime_types'; -import { MonitorLocations } from '../../../../../common/runtime_types'; - -interface MonitorStatusBarProps { - monitorId: string; - monitorStatus: Ping | null; - monitorLocations: MonitorLocations; -} - -export const MonitorStatusBarComponent: React.FC = ({ - monitorId, - monitorStatus, - monitorLocations, -}) => { - const full = monitorStatus?.url?.full ?? ''; - - return ( - - - - - - - - {full} - - - - - - -

{monitorId}

-
-
-
- - - - -
- ); -}; diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/translations.ts b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/translations.ts deleted file mode 100644 index f60a1ceeaafb8..0000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/translations.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; - -export const healthStatusMessageAriaLabel = i18n.translate( - 'xpack.uptime.monitorStatusBar.healthStatusMessageAriaLabel', - { - defaultMessage: 'Monitor status', - } -); - -export const upLabel = i18n.translate('xpack.uptime.monitorStatusBar.healthStatusMessage.upLabel', { - defaultMessage: 'Up', -}); - -export const downLabel = i18n.translate( - 'xpack.uptime.monitorStatusBar.healthStatusMessage.downLabel', - { - defaultMessage: 'Down', - } -); - -export const monitorUrlLinkAriaLabel = i18n.translate( - 'xpack.uptime.monitorStatusBar.monitorUrlLinkAriaLabel', - { - defaultMessage: 'Monitor URL link', - } -); - -export const durationTextAriaLabel = i18n.translate( - 'xpack.uptime.monitorStatusBar.durationTextAriaLabel', - { - defaultMessage: 'Monitor duration in milliseconds', - } -); - -export const timestampFromNowTextAriaLabel = i18n.translate( - 'xpack.uptime.monitorStatusBar.timestampFromNowTextAriaLabel', - { - defaultMessage: 'Time since last check', - } -); - -export const loadingMessage = i18n.translate('xpack.uptime.monitorStatusBar.loadingMessage', { - defaultMessage: 'Loading…', -}); diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/__test__/__snapshots__/monitor_status.bar.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/status_details/__test__/__snapshots__/monitor_status.bar.test.tsx.snap new file mode 100644 index 0000000000000..d53f338b60aed --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/__test__/__snapshots__/monitor_status.bar.test.tsx.snap @@ -0,0 +1,73 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MonitorStatusBar component renders 1`] = ` +Array [ +
+
+

+ Up in 2 Locations +

+
+
, +
, + .c0.c0.c0 { + width: 35%; +} + +.c1.c1.c1 { + width: 65%; + overflow-wrap: anywhere; +} + +
+
+ Overall availability +
+
+ 0.00 % +
+
+ Url +
+
+ + +
+ +
+
+ Monitor ID +
+
+
, +] +`; diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/ssl_certificate.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/status_details/__test__/__snapshots__/ssl_certificate.test.tsx.snap similarity index 61% rename from x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/ssl_certificate.test.tsx.snap rename to x-pack/plugins/uptime/public/components/monitor/status_details/__test__/__snapshots__/ssl_certificate.test.tsx.snap index 628e1d576181e..5b63a09d4f7c4 100644 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/ssl_certificate.test.tsx.snap +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/__test__/__snapshots__/ssl_certificate.test.tsx.snap @@ -2,61 +2,91 @@ exports[`SSL Certificate component renders 1`] = ` Array [ -
- Certificate: -
, + TLS Certificate + ,
, -
-
-
- Expires - - - - in 2 months - - - -
-
- , + + + , ] `; -exports[`SSL Certificate component renders null if invalid date 1`] = `null`; +exports[`SSL Certificate component renders null if invalid date 1`] = ` +Array [ + .c0.c0.c0 { + width: 35%; +} + +
+ TLS Certificate +
, +
, + .c0.c0.c0 { + width: 65%; + overflow-wrap: anywhere; +} + +
+ + + -- + + +
, +] +`; exports[`SSL Certificate component shallow renders 1`] = ` { let monitorStatus: Ping; @@ -49,18 +50,21 @@ describe('MonitorStatusBar component', () => { const spy = jest.spyOn(redux, 'useDispatch'); spy.mockReturnValue(jest.fn()); - const spy1 = jest.spyOn(redux, 'useSelector'); - spy1.mockReturnValue(true); + jest.spyOn(redux, 'useSelector').mockImplementation((fn, d) => { + if (fn.name === ' monitorStatusSelector') { + return monitorStatus; + } else { + return monitorLocations; + } + }); }); - it('renders duration in ms, not us', () => { - const component = renderWithIntl( - - ); + it('renders', () => { + const history = createMemoryHistory({ + initialEntries: ['/aWQx/'], + }); + history.location.key = 'test'; + const component = renderWithRouter(, history); expect(component).toMatchSnapshot(); }); }); diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/ssl_certificate.test.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/__test__/ssl_certificate.test.tsx similarity index 75% rename from x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/ssl_certificate.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/status_details/__test__/ssl_certificate.test.tsx index e8ffc3bf26c42..a4b360ea690d0 100644 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/ssl_certificate.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/__test__/ssl_certificate.test.tsx @@ -6,9 +6,9 @@ import React from 'react'; import moment from 'moment'; -import { EuiBadge } from '@elastic/eui'; +import { EuiIcon } from '@elastic/eui'; import { Tls } from '../../../../../common/runtime_types'; -import { MonitorSSLCertificate } from '../monitor_status_bar'; +import { MonitorSSLCertificate } from '../status_bar'; import * as redux from 'react-redux'; import { mountWithRouter, renderWithRouter, shallowWithRouter } from '../../../../lib'; import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../../common/constants'; @@ -58,14 +58,12 @@ describe('SSL Certificate component', () => { }; const component = mountWithRouter(); - const badgeComponent = component.find(EuiBadge); + const lockIcon = component.find(EuiIcon); - expect(badgeComponent.props().color).toBe('warning'); + expect(lockIcon.props().color).toBe('warning'); - const badgeComponentText = component.find('.euiBadge__text'); - expect(badgeComponentText.text()).toBe(moment(dateIn5Days).fromNow()); - - expect(badgeComponent.find('span.euiBadge--warning')).toBeTruthy(); + const componentText = component.find('h4'); + expect(componentText.text()).toBe('Expires soon ' + moment(dateIn5Days).fromNow()); }); it('does not render the expiration date with a warning state if expiry date is greater than a month', () => { @@ -75,12 +73,10 @@ describe('SSL Certificate component', () => { }; const component = mountWithRouter(); - const badgeComponent = component.find(EuiBadge); - expect(badgeComponent.props().color).toBe('default'); - - const badgeComponentText = component.find('.euiBadge__text'); - expect(badgeComponentText.text()).toBe(moment(dateIn40Days).fromNow()); + const lockIcon = component.find(EuiIcon); + expect(lockIcon.props().color).toBe('success'); - expect(badgeComponent.find('span.euiBadge--warning')).toHaveLength(0); + const componentText = component.find('h4'); + expect(componentText.text()).toBe('Expires ' + moment(dateIn40Days).fromNow()); }); }); diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/status_by_location.test.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/__test__/status_by_location.test.tsx similarity index 77% rename from x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/status_by_location.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/status_details/__test__/status_by_location.test.tsx index b2619825311d7..b171a8bedb8a7 100644 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/status_by_location.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/__test__/status_by_location.test.tsx @@ -17,10 +17,16 @@ describe('StatusByLocation component', () => { { summary: { up: 4, down: 0 }, geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } }, + up_history: 4, + down_history: 0, + timestamp: '2020-01-13T22:50:06.536Z', }, { summary: { up: 4, down: 0 }, geo: { name: 'st-paul', location: { lat: '52.487448', lon: ' 13.394798' } }, + up_history: 4, + down_history: 0, + timestamp: '2020-01-13T22:50:06.536Z', }, ]; const component = shallowWithIntl(); @@ -32,10 +38,16 @@ describe('StatusByLocation component', () => { { summary: { up: 4, down: 0 }, geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } }, + up_history: 4, + down_history: 0, + timestamp: '2020-01-13T22:50:06.536Z', }, { summary: { up: 4, down: 0 }, geo: { name: 'st-paul', location: { lat: '52.487448', lon: ' 13.394798' } }, + up_history: 4, + down_history: 0, + timestamp: '2020-01-13T22:50:06.536Z', }, ]; const component = renderWithIntl(); @@ -47,6 +59,9 @@ describe('StatusByLocation component', () => { { summary: { up: 4, down: 0 }, geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } }, + up_history: 4, + down_history: 0, + timestamp: '2020-01-13T22:50:06.536Z', }, ]; const component = renderWithIntl(); @@ -58,6 +73,9 @@ describe('StatusByLocation component', () => { { summary: { up: 0, down: 4 }, geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } }, + up_history: 4, + down_history: 0, + timestamp: '2020-01-13T22:50:06.536Z', }, ]; const component = renderWithIntl(); @@ -69,10 +87,16 @@ describe('StatusByLocation component', () => { { summary: { up: 0, down: 4 }, geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } }, + up_history: 4, + down_history: 0, + timestamp: '2020-01-13T22:50:06.536Z', }, { summary: { up: 0, down: 4 }, geo: { name: 'st-paul', location: { lat: '52.487448', lon: ' 13.394798' } }, + up_history: 4, + down_history: 0, + timestamp: '2020-01-13T22:50:06.536Z', }, ]; const component = renderWithIntl(); @@ -84,10 +108,16 @@ describe('StatusByLocation component', () => { { summary: { up: 0, down: 4 }, geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } }, + up_history: 4, + down_history: 0, + timestamp: '2020-01-13T22:50:06.536Z', }, { summary: { up: 4, down: 0 }, geo: { name: 'st-paul', location: { lat: '52.487448', lon: ' 13.394798' } }, + up_history: 4, + down_history: 0, + timestamp: '2020-01-13T22:50:06.536Z', }, ]; const component = renderWithIntl(); diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/__tests__/__snapshots__/availability_reporting.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/__tests__/__snapshots__/availability_reporting.test.tsx.snap new file mode 100644 index 0000000000000..9496274a69171 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/__tests__/__snapshots__/availability_reporting.test.tsx.snap @@ -0,0 +1,381 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AvailabilityReporting component renders correctly against snapshot 1`] = ` +Array [ + @media (max-width:1042px) { + +} + +
, + .c0 { + white-space: nowrap; + display: inline-block; +} + +@media (max-width:1042px) { + .c0 { + display: inline-block; + margin-right: 16px; + } +} + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + Location + +
+
+
+ + Availability + +
+
+
+ + Last check + +
+
+
+ Location +
+
+
+ + + +
+

+ au-heartbeat +

+
+
+
+
+
+
+
+
+ Availability +
+
+ + 100.00 % + +
+
+
+ Last check +
+
+ + 36m ago + +
+
+
+ Location +
+
+
+ + + +
+

+ nyc-heartbeat +

+
+
+
+
+
+
+
+
+ Availability +
+
+ + 100.00 % + +
+
+
+ Last check +
+
+ + 36m ago + +
+
+
+ Location +
+
+
+ + + +
+

+ spa-heartbeat +

+
+
+
+
+
+
+
+
+ Availability +
+
+ + 100.00 % + +
+
+
+ Last check +
+
+ + 36m ago + +
+
+
+
, +] +`; + +exports[`AvailabilityReporting component shallow renders correctly against snapshot 1`] = ` + + + + +`; diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/__tests__/__snapshots__/location_status_tags.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/__tests__/__snapshots__/location_status_tags.test.tsx.snap new file mode 100644 index 0000000000000..05e0b50a86f35 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/__tests__/__snapshots__/location_status_tags.test.tsx.snap @@ -0,0 +1,1085 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`LocationStatusTags component renders properly against props 1`] = ` + + + + + +`; + +exports[`LocationStatusTags component renders when all locations are down 1`] = ` +.c1 { + white-space: nowrap; + display: inline-block; +} + +.c0 { + max-height: 246px; + overflow: hidden; +} + +@media (max-width:1042px) { + .c1 { + display: inline-block; + margin-right: 16px; + } +} + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + +
+
+
+ + Location + +
+
+
+ + Availability + +
+
+
+ + Last check + +
+
+
+ Location +
+
+
+ + + +
+

+ Berlin +

+
+
+
+
+
+
+
+
+ Availability +
+
+ + 100.00 % + +
+
+
+ Last check +
+
+ + 5m ago + +
+
+
+ Location +
+
+
+ + + +
+

+ Islamabad +

+
+
+
+
+
+
+
+
+ Availability +
+
+ + 100.00 % + +
+
+
+ Last check +
+
+ + 5s ago + +
+
+
+
+
+`; + +exports[`LocationStatusTags component renders when all locations are up 1`] = ` +.c1 { + white-space: nowrap; + display: inline-block; +} + +.c0 { + max-height: 246px; + overflow: hidden; +} + +@media (max-width:1042px) { + .c1 { + display: inline-block; + margin-right: 16px; + } +} + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + +
+
+
+ + Location + +
+
+
+ + Availability + +
+
+
+ + Last check + +
+
+
+ Location +
+
+
+ + + +
+

+ Berlin +

+
+
+
+
+
+
+
+
+ Availability +
+
+ + 100.00 % + +
+
+
+ Last check +
+
+ + 5d ago + +
+
+
+ Location +
+
+
+ + + +
+

+ Islamabad +

+
+
+
+
+
+
+
+
+ Availability +
+
+ + 100.00 % + +
+
+
+ Last check +
+
+ + 5s ago + +
+
+
+
+
+`; + +exports[`LocationStatusTags component renders when there are many location 1`] = ` +.c1 { + white-space: nowrap; + display: inline-block; +} + +.c0 { + max-height: 246px; + overflow: hidden; +} + +@media (max-width:1042px) { + .c1 { + display: inline-block; + margin-right: 16px; + } +} + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + Location + +
+
+
+ + Availability + +
+
+
+ + Last check + +
+
+
+ Location +
+
+
+ + + +
+

+ Berlin +

+
+
+
+
+
+
+
+
+ Availability +
+
+ + 100.00 % + +
+
+
+ Last check +
+
+ + 5m ago + +
+
+
+ Location +
+
+
+ + + +
+

+ Islamabad +

+
+
+
+
+
+
+
+
+ Availability +
+
+ + 100.00 % + +
+
+
+ Last check +
+
+ + 5s ago + +
+
+
+ Location +
+
+
+ + + +
+

+ New York +

+
+
+
+
+
+
+
+
+ Availability +
+
+ + 100.00 % + +
+
+
+ Last check +
+
+ + 1 Mon ago + +
+
+
+ Location +
+
+
+ + + +
+

+ Paris +

+
+
+
+
+
+
+
+
+ Availability +
+
+ + 100.00 % + +
+
+
+ Last check +
+
+ + 5 Yr ago + +
+
+
+ Location +
+
+
+ + + +
+

+ Sydney +

+
+
+
+
+
+
+
+
+ Availability +
+
+ + 100.00 % + +
+
+
+ Last check +
+
+ + 5 Yr ago + +
+
+
+
+
+
+
+
+
+ + + + +
+
+
+
+
+
+`; diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/__tests__/__snapshots__/tag_label.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/__tests__/__snapshots__/tag_label.test.tsx.snap new file mode 100644 index 0000000000000..3381efa62286b --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/__tests__/__snapshots__/tag_label.test.tsx.snap @@ -0,0 +1,56 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TagLabel component renders correctly against snapshot 1`] = ` +.c0 { + white-space: nowrap; + display: inline-block; +} + +@media (max-width:1042px) { + .c0 { + display: inline-block; + margin-right: 16px; + } +} + +
+ + + +
+

+ US-East +

+
+
+
+
+
+`; + +exports[`TagLabel component shallow render correctly against snapshot 1`] = ` + + + +

+ US-East +

+
+
+
+`; diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/__tests__/availability_reporting.test.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/__tests__/availability_reporting.test.tsx new file mode 100644 index 0000000000000..de9f6b0d3b30f --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/__tests__/availability_reporting.test.tsx @@ -0,0 +1,42 @@ +/* + * 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 { renderWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; +import { AvailabilityReporting } from '../availability_reporting'; +import { StatusTag } from '../location_status_tags'; + +describe('AvailabilityReporting component', () => { + let allLocations: StatusTag[]; + + beforeEach(() => { + allLocations = [ + { + label: 'au-heartbeat', + timestamp: '36m ago', + color: '#d3dae6', + availability: 100, + }, + { + label: 'nyc-heartbeat', + timestamp: '36m ago', + color: '#d3dae6', + availability: 100, + }, + { label: 'spa-heartbeat', timestamp: '36m ago', color: '#d3dae6', availability: 100 }, + ]; + }); + + it('shallow renders correctly against snapshot', () => { + const component = shallowWithIntl(); + expect(component).toMatchSnapshot(); + }); + + it('renders correctly against snapshot', () => { + const component = renderWithIntl(); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/location_status_tags.test.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/__tests__/location_status_tags.test.tsx similarity index 84% rename from x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/location_status_tags.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/__tests__/location_status_tags.test.tsx index 28b4482401793..bfeaa6085e998 100644 --- a/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/location_status_tags.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/__tests__/location_status_tags.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import moment from 'moment'; import { renderWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { MonitorLocation } from '../../../../../common/runtime_types/monitor'; +import { MonitorLocation } from '../../../../../../common/runtime_types/monitor'; import { LocationStatusTags } from '../index'; describe('LocationStatusTags component', () => { @@ -19,16 +19,22 @@ describe('LocationStatusTags component', () => { summary: { up: 4, down: 0 }, geo: { name: 'Islamabad', location: { lat: '52.487448', lon: ' 13.394798' } }, timestamp: moment().subtract('5', 'w').toISOString(), + up_history: 4, + down_history: 0, }, { summary: { up: 4, down: 0 }, geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } }, timestamp: moment().subtract('5', 'w').toISOString(), + up_history: 4, + down_history: 0, }, { summary: { up: 0, down: 2 }, geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } }, timestamp: moment().subtract('5', 'w').toISOString(), + up_history: 4, + down_history: 0, }, ]; const component = shallowWithIntl(); @@ -41,41 +47,57 @@ describe('LocationStatusTags component', () => { summary: { up: 0, down: 1 }, geo: { name: 'Islamabad', location: { lat: '52.487448', lon: ' 13.394798' } }, timestamp: moment().subtract('5', 's').toISOString(), + up_history: 4, + down_history: 0, }, { summary: { up: 0, down: 1 }, geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } }, timestamp: moment().subtract('5', 'm').toISOString(), + up_history: 4, + down_history: 0, }, { summary: { up: 0, down: 1 }, geo: { name: 'st-paul', location: { lat: '52.487448', lon: ' 13.394798' } }, timestamp: moment().subtract('5', 'h').toISOString(), + up_history: 4, + down_history: 0, }, { summary: { up: 0, down: 1 }, geo: { name: 'Tokyo', location: { lat: '52.487448', lon: ' 13.394798' } }, timestamp: moment().subtract('5', 'd').toISOString(), + up_history: 4, + down_history: 0, }, { summary: { up: 0, down: 1 }, geo: { name: 'New York', location: { lat: '52.487448', lon: ' 13.394798' } }, timestamp: moment().subtract('5', 'w').toISOString(), + up_history: 4, + down_history: 0, }, { summary: { up: 0, down: 1 }, geo: { name: 'Toronto', location: { lat: '52.487448', lon: ' 13.394798' } }, timestamp: moment().subtract('5', 'M').toISOString(), + up_history: 4, + down_history: 0, }, { summary: { up: 0, down: 1 }, geo: { name: 'Sydney', location: { lat: '52.487448', lon: ' 13.394798' } }, timestamp: moment().subtract('5', 'y').toISOString(), + up_history: 4, + down_history: 0, }, { summary: { up: 0, down: 1 }, geo: { name: 'Paris', location: { lat: '52.487448', lon: ' 13.394798' } }, timestamp: moment().subtract('5', 'y').toISOString(), + up_history: 4, + down_history: 0, }, ]; const component = renderWithIntl(); @@ -88,11 +110,15 @@ describe('LocationStatusTags component', () => { summary: { up: 4, down: 0 }, geo: { name: 'Islamabad', location: { lat: '52.487448', lon: ' 13.394798' } }, timestamp: moment().subtract('5', 's').toISOString(), + up_history: 4, + down_history: 0, }, { summary: { up: 4, down: 0 }, geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } }, timestamp: moment().subtract('5', 'd').toISOString(), + up_history: 4, + down_history: 0, }, ]; const component = renderWithIntl(); @@ -105,11 +131,15 @@ describe('LocationStatusTags component', () => { summary: { up: 0, down: 2 }, geo: { name: 'Islamabad', location: { lat: '52.487448', lon: ' 13.394798' } }, timestamp: moment().subtract('5', 's').toISOString(), + up_history: 4, + down_history: 0, }, { summary: { up: 0, down: 2 }, geo: { name: 'Berlin', location: { lat: '52.487448', lon: ' 13.394798' } }, timestamp: moment().subtract('5', 'm').toISOString(), + up_history: 4, + down_history: 0, }, ]; const component = renderWithIntl(); diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/__tests__/tag_label.test.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/__tests__/tag_label.test.tsx new file mode 100644 index 0000000000000..3560784122298 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/__tests__/tag_label.test.tsx @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { renderWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; +import { TagLabel } from '../tag_label'; + +describe('TagLabel component', () => { + it('shallow render correctly against snapshot', () => { + const component = shallowWithIntl(); + expect(component).toMatchSnapshot(); + }); + + it('renders correctly against snapshot', () => { + const component = renderWithIntl(); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/availability_reporting.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/availability_reporting.tsx new file mode 100644 index 0000000000000..8fed5db5e0271 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/availability_reporting.tsx @@ -0,0 +1,87 @@ +/* + * 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, { useState } from 'react'; +import { EuiBasicTable, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { Pagination } from '@elastic/eui/src/components/basic_table/pagination_bar'; +import { StatusTag } from './location_status_tags'; +import { TagLabel } from './tag_label'; +import { AvailabilityLabel, LastCheckLabel, LocationLabel } from '../translations'; + +interface Props { + allLocations: StatusTag[]; +} + +export const formatAvailabilityValue = (val: number) => { + const result = Math.round(val * 100) / 100; + return result.toFixed(2); +}; + +export const AvailabilityReporting: React.FC = ({ allLocations }) => { + const [pageIndex, setPageIndex] = useState(0); + + const cols = [ + { + field: 'label', + name: LocationLabel, + truncateText: true, + render: (val: string, item: StatusTag) => { + return ; + }, + }, + { + field: 'availability', + name: AvailabilityLabel, + align: 'right' as const, + render: (val: number) => { + return ( + + + + ); + }, + }, + { + name: LastCheckLabel, + field: 'timestamp', + align: 'right' as const, + }, + ]; + const pageSize = 5; + + const pagination: Pagination = { + pageIndex, + pageSize, + totalItemCount: allLocations.length, + hidePerPageOptions: true, + }; + + const onTableChange = ({ page }: any) => { + setPageIndex(page.index); + }; + + const paginationProps = allLocations.length > pageSize ? { pagination } : {}; + + return ( + <> + + + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/index.ts b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/index.ts new file mode 100644 index 0000000000000..fb42a162522a8 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/index.ts @@ -0,0 +1,9 @@ +/* + * 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 { AvailabilityReporting } from './availability_reporting'; +export { LocationStatusTags } from './location_status_tags'; +export { TagLabel } from './tag_label'; diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/location_status_tags.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/location_status_tags.tsx new file mode 100644 index 0000000000000..6096499213a10 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/location_status_tags.tsx @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useContext } from 'react'; +import moment from 'moment'; +import styled from 'styled-components'; +import { UptimeThemeContext } from '../../../../contexts'; +import { MonitorLocation } from '../../../../../common/runtime_types'; +import { SHORT_TIMESPAN_LOCALE, SHORT_TS_LOCALE } from '../../../../../common/constants'; +import { AvailabilityReporting } from '../index'; + +// Set height so that it remains within panel, enough height to display 7 locations tags +const TagContainer = styled.div` + max-height: 246px; + overflow: hidden; +`; + +interface Props { + locations: MonitorLocation[]; +} + +export interface StatusTag { + label: string; + timestamp: string; + color: string; + availability: number; +} + +export const LocationStatusTags = ({ locations }: Props) => { + const { + colors: { gray, danger }, + } = useContext(UptimeThemeContext); + + const allLocations: StatusTag[] = []; + const prevLocal: string = moment.locale() ?? 'en'; + + const shortLocale = moment.locale(SHORT_TS_LOCALE) === SHORT_TS_LOCALE; + if (!shortLocale) { + moment.defineLocale(SHORT_TS_LOCALE, SHORT_TIMESPAN_LOCALE); + } + + locations.forEach((item: MonitorLocation) => { + allLocations.push({ + label: item.geo.name!, + timestamp: moment(new Date(item.timestamp).valueOf()).fromNow(), + color: item.summary.down === 0 ? gray : danger, + availability: (item.up_history / (item.up_history + item.down_history)) * 100, + }); + }); + + // Need to reset locale so it doesn't effect other parts of the app + moment.locale(prevLocal); + + // Sort lexicographically by label + allLocations.sort((a, b) => { + return a.label > b.label ? 1 : b.label > a.label ? -1 : 0; + }); + + if (allLocations.length === 0) { + return null; + } + + return ( + <> + + + + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/tag_label.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/tag_label.tsx new file mode 100644 index 0000000000000..dbd73fc7d440b --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/availability_reporting/tag_label.tsx @@ -0,0 +1,35 @@ +/* + * 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 styled from 'styled-components'; +import { EuiBadge, EuiText } from '@elastic/eui'; + +const BadgeItem = styled.div` + white-space: nowrap; + display: inline-block; + @media (max-width: 1042px) { + display: inline-block; + margin-right: 16px; + } +`; + +interface Props { + color: string; + label: string; +} + +export const TagLabel: React.FC = ({ color, label }) => { + return ( + + + +

{label}

+
+
+
+ ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/index.ts b/x-pack/plugins/uptime/public/components/monitor/status_details/index.ts similarity index 63% rename from x-pack/plugins/uptime/public/components/monitor/monitor_status_details/index.ts rename to x-pack/plugins/uptime/public/components/monitor/status_details/index.ts index e95f14472e9e8..ae3a07d231da3 100644 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/index.ts +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/index.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -export { MonitorStatusBarComponent } from './monitor_status_bar'; export { MonitorStatusDetailsComponent } from './status_details'; -export { StatusByLocations } from './monitor_status_bar/status_by_location'; +export { StatusByLocations } from './status_bar/status_by_location'; export { MonitorStatusDetails } from './status_details_container'; -export { MonitorStatusBar } from './monitor_status_bar/status_bar_container'; +export { MonitorStatusBar } from './status_bar/status_bar'; +export { AvailabilityReporting } from './availability_reporting/availability_reporting'; diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/location_availability/__tests__/__snapshots__/location_availability.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/status_details/location_availability/__tests__/__snapshots__/location_availability.test.tsx.snap new file mode 100644 index 0000000000000..94cbeb49a32cf --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_availability/__tests__/__snapshots__/location_availability.test.tsx.snap @@ -0,0 +1,234 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`LocationAvailability component doesnt shows warning if geo is provided 1`] = ` + + + + + + + + + + + + + +`; + +exports[`LocationAvailability component renders correctly against snapshot 1`] = ` + + + + +

+ Monitoring from +

+
+
+ + + +
+ + + + + +
+`; + +exports[`LocationAvailability component renders named locations that have missing geo data 1`] = ` + + + + + + + + + + + + + + + +`; + +exports[`LocationAvailability component shows warning if geo information is missing 1`] = ` + + + + + + + + + + + + + + + +`; diff --git a/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/location_map.test.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/location_availability/__tests__/location_availability.test.tsx similarity index 64% rename from x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/location_map.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/status_details/location_availability/__tests__/location_availability.test.tsx index 1913e677dd674..d00cb1f24def8 100644 --- a/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/location_map.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_availability/__tests__/location_availability.test.tsx @@ -6,59 +6,85 @@ import React from 'react'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { LocationMap } from '../location_map'; -import { MonitorLocations } from '../../../../../common/runtime_types'; -import { LocationMissingWarning } from '../location_missing'; +import { LocationAvailability } from '../location_availability'; +import { MonitorLocations } from '../../../../../../common/runtime_types'; +import { LocationMissingWarning } from '../../location_map/location_missing'; // Note For shallow test, we need absolute time strings -describe('LocationMap component', () => { +describe('LocationAvailability component', () => { let monitorLocations: MonitorLocations; + let localStorageMock: any; + + let selectedView = 'list'; beforeEach(() => { + localStorageMock = { + getItem: jest.fn().mockImplementation(() => selectedView), + setItem: jest.fn(), + }; + + // @ts-ignore replacing a call to localStorage we use for monitor list size + global.localStorage = localStorageMock; + monitorLocations = { monitorId: 'wapo', + up_history: 12, + down_history: 0, locations: [ { summary: { up: 4, down: 0 }, geo: { name: 'New York', location: { lat: '40.730610', lon: ' -73.935242' } }, timestamp: '2020-01-13T22:50:06.536Z', + up_history: 4, + down_history: 0, }, { summary: { up: 4, down: 0 }, geo: { name: 'Tokyo', location: { lat: '52.487448', lon: ' 13.394798' } }, timestamp: '2020-01-13T22:50:04.354Z', + up_history: 4, + down_history: 0, }, { summary: { up: 4, down: 0 }, geo: { name: 'Unnamed-location' }, timestamp: '2020-01-13T22:50:02.753Z', + up_history: 4, + down_history: 0, }, ], }; }); it('renders correctly against snapshot', () => { - const component = shallowWithIntl(); + const component = shallowWithIntl(); expect(component).toMatchSnapshot(); }); it('shows warning if geo information is missing', () => { + selectedView = 'map'; monitorLocations = { monitorId: 'wapo', + up_history: 8, + down_history: 0, locations: [ { summary: { up: 4, down: 0 }, geo: { name: 'Tokyo', location: { lat: '52.487448', lon: ' 13.394798' } }, timestamp: '2020-01-13T22:50:04.354Z', + up_history: 4, + down_history: 0, }, { summary: { up: 4, down: 0 }, geo: { name: 'Unnamed-location' }, timestamp: '2020-01-13T22:50:02.753Z', + up_history: 4, + down_history: 0, }, ], }; - const component = shallowWithIntl(); + const component = shallowWithIntl(); expect(component).toMatchSnapshot(); const warningComponent = component.find(LocationMissingWarning); @@ -68,20 +94,26 @@ describe('LocationMap component', () => { it('doesnt shows warning if geo is provided', () => { monitorLocations = { monitorId: 'wapo', + up_history: 8, + down_history: 0, locations: [ { summary: { up: 4, down: 0 }, geo: { name: 'New York', location: { lat: '40.730610', lon: ' -73.935242' } }, timestamp: '2020-01-13T22:50:06.536Z', + up_history: 4, + down_history: 0, }, { summary: { up: 4, down: 0 }, geo: { name: 'Tokyo', location: { lat: '52.487448', lon: ' 13.394798' } }, timestamp: '2020-01-13T22:50:04.354Z', + up_history: 4, + down_history: 0, }, ], }; - const component = shallowWithIntl(); + const component = shallowWithIntl(); expect(component).toMatchSnapshot(); const warningComponent = component.find(LocationMissingWarning); @@ -91,16 +123,20 @@ describe('LocationMap component', () => { it('renders named locations that have missing geo data', () => { monitorLocations = { monitorId: 'wapo', + up_history: 4, + down_history: 0, locations: [ { summary: { up: 4, down: 0 }, geo: { name: 'New York', location: undefined }, timestamp: '2020-01-13T22:50:06.536Z', + up_history: 4, + down_history: 0, }, ], }; - const component = shallowWithIntl(); + const component = shallowWithIntl(); expect(component).toMatchSnapshot(); }); }); diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/location_availability/location_availability.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/location_availability/location_availability.tsx new file mode 100644 index 0000000000000..89b969fdcf691 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_availability/location_availability.tsx @@ -0,0 +1,90 @@ +/* + * 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, { useState } from 'react'; +import styled from 'styled-components'; +import { EuiFlexGroup, EuiFlexItem, EuiErrorBoundary, EuiTitle } from '@elastic/eui'; +import { LocationStatusTags } from '../availability_reporting'; +import { LocationPoint } from '../location_map/embeddables/embedded_map'; +import { MonitorLocations, MonitorLocation } from '../../../../../common/runtime_types'; +import { UNNAMED_LOCATION } from '../../../../../common/constants'; +import { LocationMissingWarning } from '../location_map/location_missing'; +import { useSelectedView } from './use_selected_view'; +import { LocationMap } from '../location_map'; +import { MonitoringFrom } from '../translations'; +import { ToggleViewBtn } from './toggle_view_btn'; + +const EuiFlexItemTags = styled(EuiFlexItem)` + width: 350px; + @media (max-width: 1042px) { + width: 100%; + } +`; + +interface LocationMapProps { + monitorLocations: MonitorLocations; +} + +export const LocationAvailability = ({ monitorLocations }: LocationMapProps) => { + const upPoints: LocationPoint[] = []; + const downPoints: LocationPoint[] = []; + + let isAnyGeoInfoMissing = false; + + if (monitorLocations?.locations) { + monitorLocations.locations.forEach(({ geo, summary }: MonitorLocation) => { + if (geo?.name === UNNAMED_LOCATION || !geo?.location) { + isAnyGeoInfoMissing = true; + } else if (!!geo.location.lat && !!geo.location.lon) { + if (summary?.down === 0) { + upPoints.push(geo as LocationPoint); + } else { + downPoints.push(geo as LocationPoint); + } + } + }); + } + const { selectedView: initialView } = useSelectedView(); + + const [selectedView, setSelectedView] = useState(initialView); + + return ( + + + {selectedView === 'list' && ( + + +

{MonitoringFrom}

+
+
+ )} + {selectedView === 'map' && ( + {isAnyGeoInfoMissing && } + )} + + { + setSelectedView(val); + }} + /> + +
+ + + {selectedView === 'list' && ( + + + + )} + {selectedView === 'map' && ( + + + + )} + +
+ ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/location_availability/toggle_view_btn.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/location_availability/toggle_view_btn.tsx new file mode 100644 index 0000000000000..dfce583945cce --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_availability/toggle_view_btn.tsx @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as React from 'react'; +import styled from 'styled-components'; +import { EuiButtonGroup } from '@elastic/eui'; +import { useSelectedView } from './use_selected_view'; +import { ChangeToListView, ChangeToMapView } from '../translations'; + +const ToggleViewButtons = styled.span` + margin-left: auto; +`; + +interface Props { + onChange: (val: string) => void; +} + +export const ToggleViewBtn = ({ onChange }: Props) => { + const toggleButtons = [ + { + id: `listBtn`, + label: ChangeToMapView, + name: 'listView', + iconType: 'list', + 'data-test-subj': 'uptimeMonitorToggleListBtn', + 'aria-label': ChangeToMapView, + }, + { + id: `mapBtn`, + label: ChangeToListView, + name: 'mapView', + iconType: 'mapMarker', + 'data-test-subj': 'uptimeMonitorToggleMapBtn', + 'aria-label': ChangeToListView, + }, + ]; + + const { selectedView, setSelectedView } = useSelectedView(); + + const onChangeView = (optionId: string) => { + const currView = optionId === 'listBtn' ? 'list' : 'map'; + setSelectedView(currView); + onChange(currView); + }; + + return ( + + onChangeView(id)} + type="multi" + isIconOnly + style={{ marginLeft: 'auto' }} + /> + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/location_availability/use_selected_view.ts b/x-pack/plugins/uptime/public/components/monitor/status_details/location_availability/use_selected_view.ts new file mode 100644 index 0000000000000..f132f502666f0 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_availability/use_selected_view.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useEffect, useState } from 'react'; + +const localKey = 'xpack.uptime.detailPage.selectedView'; + +interface Props { + selectedView: string; + setSelectedView: (val: string) => void; +} + +export const useSelectedView = (): Props => { + const getSelectedView = localStorage.getItem(localKey) ?? 'list'; + + const [selectedView, setSelectedView] = useState(getSelectedView); + + useEffect(() => { + localStorage.setItem(localKey, selectedView); + }, [selectedView]); + + return { selectedView, setSelectedView }; +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/__tests__/__snapshots__/location_map.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/__tests__/__snapshots__/location_map.test.tsx.snap new file mode 100644 index 0000000000000..6b3d157c23fee --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/__tests__/__snapshots__/location_map.test.tsx.snap @@ -0,0 +1,27 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`LocationMap component renders correctly against snapshot 1`] = ` + + + +`; diff --git a/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_missing.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/__tests__/__snapshots__/location_missing.test.tsx.snap similarity index 99% rename from x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_missing.test.tsx.snap rename to x-pack/plugins/uptime/public/components/monitor/status_details/location_map/__tests__/__snapshots__/location_missing.test.tsx.snap index 150c4581dfd13..cd282f916f46c 100644 --- a/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_missing.test.tsx.snap +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/__tests__/__snapshots__/location_missing.test.tsx.snap @@ -4,6 +4,7 @@ exports[`LocationMissingWarning component renders correctly against snapshot 1`] .c0 { margin-left: auto; margin-bottom: 3px; + margin-right: 5px; }
{ + let upPoints: LocationPoint[]; + + beforeEach(() => { + upPoints = [ + { + name: 'New York', + location: { lat: '40.730610', lon: ' -73.935242' }, + }, + { + name: 'Tokyo', + location: { lat: '52.487448', lon: ' 13.394798' }, + }, + ]; + }); + + it('renders correctly against snapshot', () => { + const component = shallowWithIntl(); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/location_missing.test.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/__tests__/location_missing.test.tsx similarity index 100% rename from x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/location_missing.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/status_details/location_map/__tests__/location_missing.test.tsx diff --git a/x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/__tests__/__mocks__/mock.ts b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/__tests__/__mocks__/mock.ts similarity index 96% rename from x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/__tests__/__mocks__/mock.ts rename to x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/__tests__/__mocks__/mock.ts index 291ab555fbdc6..626011e3d09c9 100644 --- a/x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/__tests__/__mocks__/mock.ts +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/__tests__/__mocks__/mock.ts @@ -14,6 +14,7 @@ export const mockDownPointsLayer = { __featureCollection: { features: [ { + id: 'Asia', type: 'feature', geometry: { type: 'Point', @@ -21,6 +22,7 @@ export const mockDownPointsLayer = { }, }, { + id: 'APJ', type: 'feature', geometry: { type: 'Point', @@ -28,6 +30,7 @@ export const mockDownPointsLayer = { }, }, { + id: 'Canada', type: 'feature', geometry: { type: 'Point', @@ -79,6 +82,7 @@ export const mockUpPointsLayer = { __featureCollection: { features: [ { + id: 'US-EAST', type: 'feature', geometry: { type: 'Point', @@ -86,6 +90,7 @@ export const mockUpPointsLayer = { }, }, { + id: 'US-WEST', type: 'feature', geometry: { type: 'Point', @@ -93,6 +98,7 @@ export const mockUpPointsLayer = { }, }, { + id: 'Europe', type: 'feature', geometry: { type: 'Point', diff --git a/x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/__tests__/map_config.test.ts b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/__tests__/map_config.test.ts similarity index 65% rename from x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/__tests__/map_config.test.ts rename to x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/__tests__/map_config.test.ts index 7d53d784ff338..09a41bd9eb4b9 100644 --- a/x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/__tests__/map_config.test.ts +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/__tests__/map_config.test.ts @@ -7,7 +7,7 @@ import { getLayerList } from '../map_config'; import { mockLayerList } from './__mocks__/mock'; import { LocationPoint } from '../embedded_map'; -import { UptimeAppColors } from '../../../../../uptime_app'; +import { UptimeAppColors } from '../../../../../../uptime_app'; jest.mock('uuid', () => { return { @@ -22,14 +22,14 @@ describe('map_config', () => { beforeEach(() => { upPoints = [ - { lat: '52.487239', lon: '13.399262' }, - { lat: '55.487239', lon: '13.399262' }, - { lat: '54.487239', lon: '14.399262' }, + { name: 'US-EAST', location: { lat: '52.487239', lon: '13.399262' } }, + { location: { lat: '55.487239', lon: '13.399262' }, name: 'US-WEST' }, + { location: { lat: '54.487239', lon: '14.399262' }, name: 'Europe' }, ]; downPoints = [ - { lat: '52.487239', lon: '13.399262' }, - { lat: '55.487239', lon: '13.399262' }, - { lat: '54.487239', lon: '14.399262' }, + { location: { lat: '52.487239', lon: '13.399262' }, name: 'Asia' }, + { location: { lat: '55.487239', lon: '13.399262' }, name: 'APJ' }, + { location: { lat: '54.487239', lon: '14.399262' }, name: 'Canada' }, ]; colors = { danger: '#BC261E', diff --git a/x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/embedded_map.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/embedded_map.tsx similarity index 68% rename from x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/embedded_map.tsx rename to x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/embedded_map.tsx index 06cdb07bd8bcd..648418c02489a 100644 --- a/x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/embedded_map.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/embedded_map.tsx @@ -7,24 +7,30 @@ import React, { useEffect, useState, useContext, useRef } from 'react'; import uuid from 'uuid'; import styled from 'styled-components'; -import { MapEmbeddable, MapEmbeddableInput } from '../../../../../../../legacy/plugins/maps/public'; +import { createPortalNode, InPortal, OutPortal } from 'react-reverse-portal'; +import { + MapEmbeddable, + MapEmbeddableInput, +} from '../../../../../../../../legacy/plugins/maps/public'; import * as i18n from './translations'; -import { Location } from '../../../../../common/runtime_types'; +import { GeoPoint } from '../../../../../../common/runtime_types'; import { getLayerList } from './map_config'; -import { UptimeThemeContext, UptimeStartupPluginsContext } from '../../../../contexts'; +import { UptimeThemeContext, UptimeStartupPluginsContext } from '../../../../../contexts'; import { isErrorEmbeddable, ViewMode, ErrorEmbeddable, -} from '../../../../../../../../src/plugins/embeddable/public'; -import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../maps/public'; +} from '../../../../../../../../../src/plugins/embeddable/public'; +import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../../maps/public'; +import { MapToolTipComponent } from './map_tool_tip'; +import { RenderTooltipContentParams } from '../../../../../../../../legacy/plugins/maps/public'; export interface EmbeddedMapProps { upPoints: LocationPoint[]; downPoints: LocationPoint[]; } -export type LocationPoint = Required; +export type LocationPoint = Required; const EmbeddedPanel = styled.div` z-index: auto; @@ -54,6 +60,8 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp } const factory: any = embeddablePlugin.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE); + const portalNode = React.useMemo(() => createPortalNode(), []); + const input: MapEmbeddableInput = { id: uuid.v4(), filters: [], @@ -73,12 +81,38 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp zoom: 0, }, disableInteractive: true, - disableTooltipControl: true, hideToolbarOverlay: true, hideLayerControl: true, hideViewControl: true, }; + const renderTooltipContent = ({ + addFilters, + closeTooltip, + features, + isLocked, + getLayerName, + loadFeatureProperties, + loadFeatureGeometry, + }: RenderTooltipContentParams) => { + const props = { + addFilters, + closeTooltip, + isLocked, + getLayerName, + loadFeatureProperties, + loadFeatureGeometry, + }; + const relevantFeatures = features.filter( + (item: any) => item.layerId === 'up_points' || item.layerId === 'down_points' + ); + if (relevantFeatures.length > 0) { + return ; + } + closeTooltip(); + return null; + }; + useEffect(() => { async function setupEmbeddable() { if (!factory) { @@ -90,11 +124,13 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp }); if (embeddableObject && !isErrorEmbeddable(embeddableObject)) { - embeddableObject.setLayerList(getLayerList(upPoints, downPoints, colors)); + embeddableObject.setRenderTooltipContent(renderTooltipContent); + await embeddableObject.setLayerList(getLayerList(upPoints, downPoints, colors)); } setEmbeddable(embeddableObject); } + setupEmbeddable(); // we want this effect to execute exactly once after the component mounts @@ -122,6 +158,9 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp className="embPanel__content" ref={embeddableRoot} /> + + + ); }); diff --git a/x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/low_poly_layer.json b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/low_poly_layer.json similarity index 100% rename from x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/low_poly_layer.json rename to x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/low_poly_layer.json diff --git a/x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/map_config.ts b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/map_config.ts similarity index 94% rename from x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/map_config.ts rename to x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/map_config.ts index f1b530c767f1f..e766641102a24 100644 --- a/x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/map_config.ts +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/map_config.ts @@ -6,7 +6,7 @@ import lowPolyLayerFeatures from './low_poly_layer.json'; import { LocationPoint } from './embedded_map'; -import { UptimeAppColors } from '../../../../uptime_app'; +import { UptimeAppColors } from '../../../../../uptime_app'; /** * Returns `Source/Destination Point-to-point` Map LayerList configuration, with a source, @@ -70,9 +70,10 @@ export const getLowPolyLayer = () => { export const getDownPointsLayer = (downPoints: LocationPoint[], dangerColor: string) => { const features = downPoints?.map((point) => ({ type: 'feature', + id: point.name, geometry: { type: 'Point', - coordinates: [+point.lon, +point.lat], + coordinates: [+point.location.lon, +point.location.lat], }, })); return { @@ -122,9 +123,10 @@ export const getDownPointsLayer = (downPoints: LocationPoint[], dangerColor: str export const getUpPointsLayer = (upPoints: LocationPoint[]) => { const features = upPoints?.map((point) => ({ type: 'feature', + id: point.name, geometry: { type: 'Point', - coordinates: [+point.lon, +point.lat], + coordinates: [+point.location.lon, +point.location.lat], }, })); return { diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/map_tool_tip.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/map_tool_tip.tsx new file mode 100644 index 0000000000000..0d54d91007a8d --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/map_tool_tip.tsx @@ -0,0 +1,90 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import React, { useContext } from 'react'; +import { useSelector } from 'react-redux'; +import { + EuiDescriptionList, + EuiDescriptionListDescription, + EuiDescriptionListTitle, + EuiOutsideClickDetector, + EuiPopoverTitle, +} from '@elastic/eui'; +import { TagLabel } from '../../availability_reporting'; +import { UptimeThemeContext } from '../../../../../contexts'; +import { AppState } from '../../../../../state'; +import { monitorLocationsSelector } from '../../../../../state/selectors'; +import { useMonitorId } from '../../../../../hooks'; +import { MonitorLocation } from '../../../../../../common/runtime_types/monitor'; +import { RenderTooltipContentParams } from '../../../../../../../../legacy/plugins/maps/public'; +import { formatAvailabilityValue } from '../../availability_reporting/availability_reporting'; +import { LastCheckLabel } from '../../translations'; + +type MapToolTipProps = Partial; + +export const MapToolTipComponent = ({ closeTooltip, features = [] }: MapToolTipProps) => { + const { id: featureId, layerId } = features[0] ?? {}; + const locationName = featureId?.toString(); + const { + colors: { gray, danger }, + } = useContext(UptimeThemeContext); + + const monitorId = useMonitorId(); + + const monitorLocations = useSelector((state: AppState) => + monitorLocationsSelector(state, monitorId) + ); + if (!locationName || !monitorLocations?.locations) { + return null; + } + const { + timestamp, + up_history: ups, + down_history: downs, + }: MonitorLocation = monitorLocations.locations!.find( + ({ geo }: MonitorLocation) => geo.name === locationName + )!; + + const availability = (ups / (ups + downs)) * 100; + + return ( + { + if (closeTooltip != null) { + closeTooltip(); + } + }} + > + <> + + {layerId === 'up_points' ? ( + + ) : ( + + )} + + + Availability + + {i18n.translate('xpack.uptime.mapToolTip.AvailabilityStat.title', { + defaultMessage: '{value} %', + values: { value: formatAvailabilityValue(availability) }, + description: 'A percentage value like 23.5%', + })} + + {LastCheckLabel} + + {moment(timestamp).fromNow()} + + + + + ); +}; + +export const MapToolTip = React.memo(MapToolTipComponent); diff --git a/x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/translations.ts b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/translations.ts similarity index 100% rename from x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/translations.ts rename to x-pack/plugins/uptime/public/components/monitor/status_details/location_map/embeddables/translations.ts diff --git a/x-pack/plugins/uptime/public/components/monitor/location_map/index.ts b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/index.ts similarity index 81% rename from x-pack/plugins/uptime/public/components/monitor/location_map/index.ts rename to x-pack/plugins/uptime/public/components/monitor/status_details/location_map/index.ts index 140d33bbeef66..4261400bebc6f 100644 --- a/x-pack/plugins/uptime/public/components/monitor/location_map/index.ts +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/index.ts @@ -5,4 +5,4 @@ */ export * from './location_map'; -export * from './location_status_tags'; +export * from '../availability_reporting/location_status_tags'; diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/location_map.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/location_map.tsx new file mode 100644 index 0000000000000..3d0a097d20d70 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/location_map.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import styled from 'styled-components'; +import { EmbeddedMap, LocationPoint } from './embeddables/embedded_map'; + +// These height/width values are used to make sure map is in center of panel +// And to make sure, it doesn't take too much space +const MapPanel = styled.div` + height: 240px; + width: 520px; + margin-right: 65px; + @media (max-width: 574px) { + height: 250px; + width: 100%; + } +`; + +interface Props { + upPoints: LocationPoint[]; + downPoints: LocationPoint[]; +} + +export const LocationMap = ({ upPoints, downPoints }: Props) => { + return ( + + + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/location_map/location_missing.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/location_missing.tsx similarity index 96% rename from x-pack/plugins/uptime/public/components/monitor/location_map/location_missing.tsx rename to x-pack/plugins/uptime/public/components/monitor/status_details/location_map/location_missing.tsx index 6ce31e4cc8243..e364b6b8940b3 100644 --- a/x-pack/plugins/uptime/public/components/monitor/location_map/location_missing.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/location_map/location_missing.tsx @@ -16,11 +16,12 @@ import { } from '@elastic/eui'; import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n/react'; -import { LocationLink } from '../../common/location_link'; +import { LocationLink } from '../../../common/location_link'; const EuiPopoverRight = styled(EuiFlexItem)` margin-left: auto; margin-bottom: 3px; + margin-right: 5px; `; export const LocationMissingWarning = () => { diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/index.ts b/x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/index.ts similarity index 72% rename from x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/index.ts rename to x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/index.ts index 3c861412a39e9..22a059d603778 100644 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/index.ts +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/index.ts @@ -5,5 +5,4 @@ */ export { MonitorSSLCertificate } from './ssl_certificate'; -export { MonitorStatusBarComponent } from './status_bar'; -export { MonitorStatusBar } from './status_bar_container'; +export { MonitorStatusBar } from './status_bar'; diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/ssl_certificate.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/ssl_certificate.tsx new file mode 100644 index 0000000000000..93720ab313ee3 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/ssl_certificate.tsx @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { Link } from 'react-router-dom'; +import { EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { Tls } from '../../../../../common/runtime_types'; +import { CERTIFICATES_ROUTE } from '../../../../../common/constants'; +import { MonListDescription, MonListTitle } from './status_bar'; +import { CertStatusColumn } from '../../../overview/monitor_list/cert_status_column'; + +interface Props { + /** + * TLS information coming from monitor in ES heartbeat index + */ + tls: Tls | undefined; +} + +export const MonitorSSLCertificate = ({ tls }: Props) => { + return tls?.not_after ? ( + <> + + + + + + + + + + + + ) : null; +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/status_bar.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/status_bar.tsx new file mode 100644 index 0000000000000..afcc8fae7a8ac --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/status_bar.tsx @@ -0,0 +1,82 @@ +/* + * 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 styled from 'styled-components'; +import { + EuiLink, + EuiIcon, + EuiSpacer, + EuiDescriptionList, + EuiDescriptionListTitle, + EuiDescriptionListDescription, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { MonitorSSLCertificate } from './ssl_certificate'; +import * as labels from '../translations'; +import { StatusByLocations } from './status_by_location'; +import { useStatusBar } from './use_status_bar'; +import { MonitorIDLabel, OverallAvailability } from '../translations'; +import { URL_LABEL } from '../../../common/translations'; +import { MonitorLocations } from '../../../../../common/runtime_types/monitor'; +import { formatAvailabilityValue } from '../availability_reporting/availability_reporting'; + +export const MonListTitle = styled(EuiDescriptionListTitle)` + &&& { + width: 35%; + } +`; + +export const MonListDescription = styled(EuiDescriptionListDescription)` + &&& { + width: 65%; + overflow-wrap: anywhere; + } +`; + +export const MonitorStatusBar: React.FC = () => { + const { monitorId, monitorStatus, monitorLocations = {} } = useStatusBar(); + + const { locations, up_history: ups, down_history: downs } = monitorLocations as MonitorLocations; + + const full = monitorStatus?.url?.full ?? ''; + + const availability = (ups === 0 && downs === 0) || !ups ? 0 : (ups / (ups + downs)) * 100; + + return ( + <> +
+ +
+ + + {OverallAvailability} + + + + {URL_LABEL} + + + {full} + + + {MonitorIDLabel} + {monitorId} + + + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_by_location.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/status_by_location.tsx similarity index 100% rename from x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_by_location.tsx rename to x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/status_by_location.tsx diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar_container.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/use_status_bar.ts similarity index 66% rename from x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar_container.tsx rename to x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/use_status_bar.ts index 9562295437515..27e953aab1b71 100644 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar_container.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/use_status_bar.ts @@ -4,24 +4,33 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext, useEffect } from 'react'; +import { useContext, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { monitorLocationsSelector, monitorStatusSelector } from '../../../../state/selectors'; -import { MonitorStatusBarComponent } from './index'; -import { getMonitorStatusAction } from '../../../../state/actions'; -import { useGetUrlParams } from '../../../../hooks'; import { UptimeRefreshContext } from '../../../../contexts'; -import { MonitorIdParam } from '../../../../../common/types'; +import { useGetUrlParams, useMonitorId } from '../../../../hooks'; +import { monitorLocationsSelector, monitorStatusSelector } from '../../../../state/selectors'; import { AppState } from '../../../../state'; +import { getMonitorStatusAction } from '../../../../state/actions'; +import { Ping } from '../../../../../common/runtime_types/ping'; +import { MonitorLocations } from '../../../../../common/runtime_types/monitor'; -export const MonitorStatusBar: React.FC = ({ monitorId }) => { +interface MonitorStatusBarProps { + monitorId: string; + monitorStatus: Ping | null; + monitorLocations?: MonitorLocations; +} + +export const useStatusBar = (): MonitorStatusBarProps => { const { lastRefresh } = useContext(UptimeRefreshContext); const { dateRangeStart: dateStart, dateRangeEnd: dateEnd } = useGetUrlParams(); const dispatch = useDispatch(); + const monitorId = useMonitorId(); + const monitorStatus = useSelector(monitorStatusSelector); + const monitorLocations = useSelector((state: AppState) => monitorLocationsSelector(state, monitorId) ); @@ -30,11 +39,5 @@ export const MonitorStatusBar: React.FC = ({ monitorId }) => { dispatch(getMonitorStatusAction({ dateStart, dateEnd, monitorId })); }, [monitorId, dateStart, dateEnd, lastRefresh, dispatch]); - return ( - - ); + return { monitorStatus, monitorLocations, monitorId }; }; diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/status_details.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/status_details.tsx similarity index 72% rename from x-pack/plugins/uptime/public/components/monitor/monitor_status_details/status_details.tsx rename to x-pack/plugins/uptime/public/components/monitor/status_details/status_details.tsx index 3766ac86785f7..dea39494646a6 100644 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/status_details.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/status_details.tsx @@ -7,31 +7,24 @@ import React, { useContext, useEffect, useState } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import styled from 'styled-components'; -import { LocationMap } from '../location_map'; +import { LocationAvailability } from './location_availability/location_availability'; import { UptimeRefreshContext } from '../../../contexts'; import { MonitorLocations } from '../../../../common/runtime_types'; -import { MonitorStatusBar } from './monitor_status_bar'; +import { MonitorStatusBar } from './status_bar'; interface MonitorStatusDetailsProps { - monitorId: string; monitorLocations: MonitorLocations; } const WrapFlexItem = styled(EuiFlexItem)` &&& { - @media (max-width: 768px) { - width: 100%; - } - @media (max-width: 1042px) { - flex-basis: 520px; + @media (max-width: 800px) { + flex-basis: 100%; } } `; -export const MonitorStatusDetailsComponent = ({ - monitorId, - monitorLocations, -}: MonitorStatusDetailsProps) => { +export const MonitorStatusDetailsComponent = ({ monitorLocations }: MonitorStatusDetailsProps) => { const { refreshApp } = useContext(UptimeRefreshContext); const [isTabActive] = useState(document.visibilityState); @@ -56,12 +49,12 @@ export const MonitorStatusDetailsComponent = ({ return ( - - - + + + - - + + diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/status_details_container.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/status_details_container.tsx similarity index 92% rename from x-pack/plugins/uptime/public/components/monitor/monitor_status_details/status_details_container.tsx rename to x-pack/plugins/uptime/public/components/monitor/status_details/status_details_container.tsx index 251f3562f9d1a..92e144bd1b9cc 100644 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/status_details_container.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/status_details_container.tsx @@ -28,7 +28,5 @@ export const MonitorStatusDetails: React.FC = ({ monitorId }) => dispatch(getMonitorLocationsAction({ dateStart, dateEnd, monitorId })); }, [monitorId, dateStart, dateEnd, lastRefresh, dispatch]); - return ( - - ); + return ; }; diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/translations.ts b/x-pack/plugins/uptime/public/components/monitor/status_details/translations.ts similarity index 50% rename from x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/translations.ts rename to x-pack/plugins/uptime/public/components/monitor/status_details/translations.ts index f60a1ceeaafb8..f593525fa0942 100644 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/translations.ts +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/translations.ts @@ -48,3 +48,56 @@ export const timestampFromNowTextAriaLabel = i18n.translate( export const loadingMessage = i18n.translate('xpack.uptime.monitorStatusBar.loadingMessage', { defaultMessage: 'Loading…', }); + +export const MonitorIDLabel = i18n.translate('xpack.uptime.monitorStatusBar.monitor.id', { + defaultMessage: 'Monitor ID', +}); + +export const OverallAvailability = i18n.translate( + 'xpack.uptime.monitorStatusBar.monitor.availability', + { + defaultMessage: 'Overall availability', + } +); + +export const MonitoringFrom = i18n.translate( + 'xpack.uptime.monitorStatusBar.monitor.monitoringFrom', + { + defaultMessage: 'Monitoring from', + } +); + +export const ChangeToMapView = i18n.translate( + 'xpack.uptime.monitorStatusBar.monitor.monitoringFrom.listToMap', + { + defaultMessage: 'Change to map view to check availability by location.', + } +); + +export const ChangeToListView = i18n.translate( + 'xpack.uptime.monitorStatusBar.monitor.monitoringFrom.MapToList', + { + defaultMessage: 'Change to list view to check availability by location.', + } +); + +export const LocationLabel = i18n.translate( + 'xpack.uptime.monitorStatusBar.monitor.availabilityReport.location', + { + defaultMessage: 'Location', + } +); + +export const AvailabilityLabel = i18n.translate( + 'xpack.uptime.monitorStatusBar.monitor.availabilityReport.availability', + { + defaultMessage: 'Availability', + } +); + +export const LastCheckLabel = i18n.translate( + 'xpack.uptime.monitorStatusBar.monitor.availabilityReport.lastCheck', + { + defaultMessage: 'Last check', + } +); diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/cert_status_column.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/cert_status_column.tsx index 5ad0f4d3d1d49..b7c70198912fc 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/cert_status_column.tsx +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/cert_status_column.tsx @@ -8,13 +8,14 @@ import React from 'react'; import moment from 'moment'; import styled from 'styled-components'; import { EuiIcon, EuiText, EuiToolTip } from '@elastic/eui'; -import { Cert } from '../../../../common/runtime_types'; +import { Cert, Tls } from '../../../../common/runtime_types'; import { useCertStatus } from '../../../hooks'; -import { EXPIRED, EXPIRES_SOON } from '../../certificates/translations'; +import { EXPIRED, EXPIRES, EXPIRES_SOON } from '../../certificates/translations'; import { CERT_STATUS } from '../../../../common/constants'; interface Props { - cert: Cert; + cert: Cert | Tls; + boldStyle?: boolean; } const Span = styled.span` @@ -22,7 +23,15 @@ const Span = styled.span` vertical-align: middle; `; -export const CertStatusColumn: React.FC = ({ cert }) => { +const H4Text = styled.h4` + &&& { + margin: 0 0 0 4px; + display: inline-block; + color: inherit; + } +`; + +export const CertStatusColumn: React.FC = ({ cert, boldStyle = false }) => { const certStatus = useCertStatus(cert?.not_after); const relativeDate = moment(cert?.not_after).fromNow(); @@ -32,9 +41,15 @@ export const CertStatusColumn: React.FC = ({ cert }) => { - - {text} {relativeDate} - + {boldStyle ? ( + + {text} {relativeDate} + + ) : ( + + {text} {relativeDate} + + )} ); @@ -47,5 +62,5 @@ export const CertStatusColumn: React.FC = ({ cert }) => { return ; } - return certStatus ? : --; + return certStatus ? : --; }; diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx index 1526838460957..75d587579f66f 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx @@ -30,6 +30,7 @@ import { MonitorListProps } from './monitor_list_container'; import { MonitorList } from '../../../state/reducers/monitor_list'; import { CertStatusColumn } from './cert_status_column'; import { MonitorListHeader } from './monitor_list_header'; +import { URL_LABEL } from '../../common/translations'; interface Props extends MonitorListProps { pageSize: number; @@ -106,7 +107,7 @@ export const MonitorListComponent: React.FC = ({ { align: 'left' as const, field: 'state.url.full', - name: labels.URL, + name: URL_LABEL, render: (url: string, summary: MonitorSummary) => ( {url} diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/translations.ts b/x-pack/plugins/uptime/public/components/overview/monitor_list/translations.ts index e70eef1d91161..ee922a9ef803f 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/translations.ts +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/translations.ts @@ -62,10 +62,6 @@ export const NO_DATA_MESSAGE = i18n.translate('xpack.uptime.monitorList.noItemMe description: 'This message is shown if the monitors table is rendered but has no items.', }); -export const URL = i18n.translate('xpack.uptime.monitorList.table.url.name', { - defaultMessage: 'Url', -}); - export const UP = i18n.translate('xpack.uptime.monitorList.statusColumn.upLabel', { defaultMessage: 'Up', }); diff --git a/x-pack/plugins/uptime/public/pages/__tests__/__snapshots__/page_header.test.tsx.snap b/x-pack/plugins/uptime/public/pages/__tests__/__snapshots__/page_header.test.tsx.snap index ec0ee120b6283..dbdfd6b27e69f 100644 --- a/x-pack/plugins/uptime/public/pages/__tests__/__snapshots__/page_header.test.tsx.snap +++ b/x-pack/plugins/uptime/public/pages/__tests__/__snapshots__/page_header.test.tsx.snap @@ -2,21 +2,25 @@ exports[`PageHeader shallow renders extra links: page_header_with_extra_links 1`] = ` Array [ - @media only screen and (max-width:1024px) and (min-width:868px) { - .c0.c0.c0 .euiSuperDatePicker__flexWrapper { + .c0 { + white-space: nowrap; +} + +@media only screen and (max-width:1024px) and (min-width:868px) { + .c1.c1.c1 .euiSuperDatePicker__flexWrapper { width: 500px; } } @media only screen and (max-width:880px) { - .c0.c0.c0 { + .c1.c1.c1 { -webkit-box-flex: 1; -webkit-flex-grow: 1; -ms-flex-positive: 1; flex-grow: 1; } - .c0.c0.c0 .euiSuperDatePicker__flexWrapper { + .c1.c1.c1 .euiSuperDatePicker__flexWrapper { width: calc(100% + 8px); } } @@ -28,7 +32,7 @@ Array [ class="euiFlexItem" >

TestingHeading

@@ -103,7 +107,7 @@ Array [

TestingHeading

@@ -250,7 +258,7 @@ Array [ class="euiFlexItem euiFlexItem--flexGrowZero" />

TestingHeading

diff --git a/x-pack/plugins/uptime/public/pages/page_header.tsx b/x-pack/plugins/uptime/public/pages/page_header.tsx index d2e1f9036a24a..3b228afe904f4 100644 --- a/x-pack/plugins/uptime/public/pages/page_header.tsx +++ b/x-pack/plugins/uptime/public/pages/page_header.tsx @@ -18,6 +18,7 @@ interface PageHeaderProps { extraLinks?: boolean; datePicker?: boolean; } + const SETTINGS_LINK_TEXT = i18n.translate('xpack.uptime.page_header.settingsLink', { defaultMessage: 'Settings', }); @@ -38,6 +39,10 @@ const StyledPicker = styled(EuiFlexItem)` } `; +const H1Text = styled.h1` + white-space: nowrap; +`; + export const PageHeader = React.memo( ({ headingText, extraLinks = false, datePicker = true }: PageHeaderProps) => { const DatePickerComponent = () => @@ -73,7 +78,7 @@ export const PageHeader = React.memo( > -

{headingText}

+ {headingText}
{extraLinkComponents} diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_locations.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_locations.ts index c8d3ca043edc5..17d79002e6f7d 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_locations.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_locations.ts @@ -32,7 +32,7 @@ export const getMonitorLocations: UMElasticsearchQueryFn< bool: { filter: [ { - match: { + term: { 'monitor.id': monitorId, }, }, @@ -70,6 +70,18 @@ export const getMonitorLocations: UMElasticsearchQueryFn< _source: ['monitor', 'summary', 'observer', '@timestamp'], }, }, + down_history: { + sum: { + field: 'summary.down', + missing: 0, + }, + }, + up_history: { + sum: { + field: 'summary.up', + missing: 0, + }, + }, }, }, }, @@ -99,10 +111,17 @@ export const getMonitorLocations: UMElasticsearchQueryFn< } }; + let totalUps = 0; + let totalDowns = 0; + const monLocs: MonitorLocation[] = []; - locations.forEach((loc: any) => { - const mostRecentLocation = loc.most_recent.hits.hits[0]._source; + locations.forEach(({ most_recent: mostRecent, up_history, down_history }: any) => { + const mostRecentLocation = mostRecent.hits.hits[0]._source; + totalUps += up_history.value; + totalDowns += down_history.value; const location: MonitorLocation = { + up_history: up_history.value ?? 0, + down_history: down_history.value ?? 0, summary: mostRecentLocation?.summary, geo: getGeo(mostRecentLocation?.observer?.geo), timestamp: mostRecentLocation['@timestamp'], @@ -113,5 +132,7 @@ export const getMonitorLocations: UMElasticsearchQueryFn< return { monitorId, locations: monLocs, + up_history: totalUps, + down_history: totalDowns, }; }; diff --git a/x-pack/test/accessibility/apps/uptime.ts b/x-pack/test/accessibility/apps/uptime.ts index dccce4ba1b0b1..ebd120fa0feea 100644 --- a/x-pack/test/accessibility/apps/uptime.ts +++ b/x-pack/test/accessibility/apps/uptime.ts @@ -65,7 +65,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('detail page', async () => { await uptimeService.navigation.goToMonitor(A11Y_TEST_MONITOR_ID); - await uptimeService.monitor.locationMapIsRendered(); + await uptimeService.monitor.displayOverallAvailability('0.00 %'); await a11y.testAppSnapshot(); }); diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/find_statuses.ts b/x-pack/test/detection_engine_api_integration/basic/tests/find_statuses.ts index 6ff5a8c552ab7..cc6fa53939f60 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/find_statuses.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/find_statuses.ts @@ -14,6 +14,7 @@ import { deleteSignalsIndex, deleteAllRulesStatuses, getSimpleRule, + waitFor, } from '../../utils'; // eslint-disable-next-line import/no-default-export @@ -51,7 +52,17 @@ export default ({ getService }: FtrProviderContext): void => { .expect(200); // wait for Task Manager to execute the rule and update status - await new Promise((resolve) => setTimeout(resolve, 5000)); + await waitFor(async () => { + const { body } = await supertest + .post(`${DETECTION_ENGINE_RULES_URL}/_find_statuses`) + .set('kbn-xsrf', 'true') + .send({ ids: [resBody.id] }) + .expect(200); + return ( + body[resBody.id].current_status?.status === 'succeeded' || + body[resBody.id].current_status?.status === 'going to run' + ); + }); // query the single rule from _find const { body } = await supertest diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/import_rules.ts b/x-pack/test/detection_engine_api_integration/basic/tests/import_rules.ts index 66962df14e22d..e0b60ae1fbeeb 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/import_rules.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/import_rules.ts @@ -17,6 +17,7 @@ import { getSimpleRuleOutput, removeServerGeneratedProperties, ruleToNdjson, + waitFor, } from '../../utils'; // eslint-disable-next-line import/no-default-export @@ -33,8 +34,12 @@ export default ({ getService }: FtrProviderContext): void => { .attach('file', getSimpleRuleAsNdjson(['rule-1']), 'rules.ndjson') .expect(400); - // We have to wait up to 5 seconds for any unresolved promises to flush - await new Promise((resolve) => setTimeout(resolve, 5000)); + await waitFor(async () => { + const { body } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}?rule_id=rule-1`) + .send(); + return body.status_code === 404; + }); // Try to fetch the rule which should still be a 404 (not found) const { body } = await supertest.get(`${DETECTION_ENGINE_RULES_URL}?rule_id=rule-1`).send(); diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts b/x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts index fe7783fa2d045..93dbc2471ae18 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts @@ -6,18 +6,31 @@ import expect from '@kbn/expect'; -import { DETECTION_ENGINE_SIGNALS_STATUS_URL } from '../../../../plugins/security_solution/common/constants'; +import { SearchResponse } from 'elasticsearch'; +import { Signal } from '../../../../plugins/security_solution/server/lib/detection_engine/signals/types'; +import { + DETECTION_ENGINE_SIGNALS_STATUS_URL, + DETECTION_ENGINE_RULES_URL, + DETECTION_ENGINE_QUERY_SIGNALS_URL, +} from '../../../../plugins/security_solution/common/constants'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createSignalsIndex, deleteSignalsIndex, setSignalStatus, getSignalStatusEmptyResponse, + getSimpleRule, + waitFor, + getQueryAllSignals, + getQuerySignalIds, + deleteAllAlerts, } from '../../utils'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const es = getService('es'); describe('open_close_signals', () => { describe('validation checks', () => { @@ -50,5 +63,231 @@ export default ({ getService }: FtrProviderContext) => { await deleteSignalsIndex(supertest); }); }); + + describe('tests with auditbeat data', () => { + beforeEach(async () => { + await deleteAllAlerts(es); + await createSignalsIndex(supertest); + await esArchiver.load('auditbeat/hosts'); + }); + afterEach(async () => { + await deleteSignalsIndex(supertest); + await deleteAllAlerts(es); + await esArchiver.unload('auditbeat/hosts'); + }); + + it('should be able to execute and get 10 signals', async () => { + const rule = { ...getSimpleRule(), from: '1900-01-01T00:00:00.000Z', query: '*:*' }; + + // create a simple rule + await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(rule) + .expect(200); + + // wait until all the rules show up and are present + await waitFor(async () => { + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + return signalsOpen.hits.hits.length === 10; + }); + + // Get the collection of signals + await waitFor(async () => { + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + return signalsOpen.hits.hits.length === 10; + }); + + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + + // expect there to be 10 + expect(signalsOpen.hits.hits.length).equal(10); + }); + + it('should be have set the signals in an open state initially', async () => { + const rule = { ...getSimpleRule(), from: '1900-01-01T00:00:00.000Z', query: '*:*' }; + + // create a simple rule + await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(rule) + .expect(200); + + // wait until all the rules show up and are present + await waitFor(async () => { + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + return signalsOpen.hits.hits.length === 10; + }); + + await waitFor(async () => { + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + return signalsOpen.hits.hits.length === 10; + }); + + // Get the collection of signals + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + + const everySignalOpen = signalsOpen.hits.hits.every( + ({ + _source: { + signal: { status }, + }, + }) => status === 'open' + ); + + // expect their initial state to all be open + expect(everySignalOpen).to.eql(true); + }); + + it('should be able to get a count of 10 closed signals when closing 10', async () => { + const rule = { ...getSimpleRule(), from: '1900-01-01T00:00:00.000Z', query: '*:*' }; + + // create a simple rule + await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(rule) + .expect(200); + + // wait until all the rules show up and are present + await waitFor(async () => { + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + return signalsOpen.hits.hits.length === 10; + }); + + // Get a collection of signals to get the id to set open/closed on them + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + + const signalIds = signalsOpen.hits.hits.map((signal) => signal._id); + + // set all of the signals to the state of closed. There is no reason to use a waitUntil here + // as this route intentionally has a waitFor within it and should only return when the query has + // the data. + await supertest + .post(DETECTION_ENGINE_SIGNALS_STATUS_URL) + .set('kbn-xsrf', 'true') + .send(setSignalStatus({ signalIds, status: 'closed' })) + .expect(200); + + const { + body: signalsClosed, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQuerySignalIds(signalIds)) + .expect(200); + + expect(signalsClosed.hits.hits.length).to.equal(10); + }); + + it('should be able close 10 signals immediately and they all should be closed', async () => { + const rule = { ...getSimpleRule(), from: '1900-01-01T00:00:00.000Z', query: '*:*' }; + + // create a simple rule + await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(rule) + .expect(200); + + // wait until all the rules show and are present + await waitFor(async () => { + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + return signalsOpen.hits.hits.length === 10; + }); + + // Get a collection of signals to get the id to set open/closed on them + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + + const signalIds = signalsOpen.hits.hits.map((signal) => signal._id); + + // set all of the signals to the state of closed. There is no reason to use a waitUntil here + // as this route intentionally has a waitFor within it and should only return when the query has + // the data. + await supertest + .post(DETECTION_ENGINE_SIGNALS_STATUS_URL) + .set('kbn-xsrf', 'true') + .send(setSignalStatus({ signalIds, status: 'closed' })) + .expect(200); + + const { + body: signalsClosed, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQuerySignalIds(signalIds)) + .expect(200); + + const everySignalClosed = signalsClosed.hits.hits.every( + ({ + _source: { + signal: { status }, + }, + }) => status === 'closed' + ); + expect(everySignalClosed).to.eql(true); + }); + }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts index 8847b0c3250ac..c763be1c2c3ec 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts @@ -23,6 +23,7 @@ import { removeServerGeneratedPropertiesIncludingRuleId, getSimpleMlRule, getSimpleMlRuleOutput, + waitFor, } from '../../utils'; // eslint-disable-next-line import/no-default-export @@ -96,7 +97,15 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); // wait for Task Manager to execute the rule and update status - await new Promise((resolve) => setTimeout(resolve, 5000)); + await waitFor(async () => { + const { body: statusBody } = await supertest + .post(DETECTION_ENGINE_RULES_STATUS_URL) + .set('kbn-xsrf', 'true') + .send({ ids: [body.id] }) + .expect(200); + return statusBody[body.id].current_status?.status === 'succeeded'; + }); + const { body: statusBody } = await supertest .post(DETECTION_ENGINE_RULES_STATUS_URL) .set('kbn-xsrf', 'true') diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts index 47b09fd1fe3c7..52865e43be750 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts @@ -21,6 +21,7 @@ import { getSimpleRuleWithoutRuleId, removeServerGeneratedProperties, removeServerGeneratedPropertiesIncludingRuleId, + waitFor, } from '../../utils'; // eslint-disable-next-line import/no-default-export @@ -99,7 +100,14 @@ export default ({ getService }: FtrProviderContext): void => { .expect(200); // wait for Task Manager to execute the rule and update status - await new Promise((resolve) => setTimeout(resolve, 5000)); + await waitFor(async () => { + const { body: statusBody } = await supertest + .post(DETECTION_ENGINE_RULES_STATUS_URL) + .set('kbn-xsrf', 'true') + .send({ ids: [body[0].id] }) + .expect(200); + return statusBody[body[0].id].current_status?.status === 'succeeded'; + }); const { body: statusBody } = await supertest .post(DETECTION_ENGINE_RULES_STATUS_URL) .set('kbn-xsrf', 'true') diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/find_statuses.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/find_statuses.ts index f659857b89a97..aee8d400be905 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/find_statuses.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/find_statuses.ts @@ -14,6 +14,7 @@ import { deleteSignalsIndex, deleteAllRulesStatuses, getSimpleRule, + waitFor, } from '../../utils'; // eslint-disable-next-line import/no-default-export @@ -70,7 +71,14 @@ export default ({ getService }: FtrProviderContext): void => { .expect(200); // wait for Task Manager to execute the rule and update status - await new Promise((resolve) => setTimeout(resolve, 5000)); + await waitFor(async () => { + const { body } = await supertest + .post(`${DETECTION_ENGINE_RULES_URL}/_find_statuses`) + .set('kbn-xsrf', 'true') + .send({ ids: [resBody.id] }) + .expect(200); + return body[resBody.id].current_status?.status === 'succeeded'; + }); // query the single rule from _find const { body } = await supertest diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_rules.ts index 66962df14e22d..e0b60ae1fbeeb 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_rules.ts @@ -17,6 +17,7 @@ import { getSimpleRuleOutput, removeServerGeneratedProperties, ruleToNdjson, + waitFor, } from '../../utils'; // eslint-disable-next-line import/no-default-export @@ -33,8 +34,12 @@ export default ({ getService }: FtrProviderContext): void => { .attach('file', getSimpleRuleAsNdjson(['rule-1']), 'rules.ndjson') .expect(400); - // We have to wait up to 5 seconds for any unresolved promises to flush - await new Promise((resolve) => setTimeout(resolve, 5000)); + await waitFor(async () => { + const { body } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}?rule_id=rule-1`) + .send(); + return body.status_code === 404; + }); // Try to fetch the rule which should still be a 404 (not found) const { body } = await supertest.get(`${DETECTION_ENGINE_RULES_URL}?rule_id=rule-1`).send(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts index fe7783fa2d045..6b924eddfdc44 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts @@ -6,18 +6,31 @@ import expect from '@kbn/expect'; -import { DETECTION_ENGINE_SIGNALS_STATUS_URL } from '../../../../plugins/security_solution/common/constants'; +import { SearchResponse } from 'elasticsearch'; +import { Signal } from '../../../../plugins/security_solution/server/lib/detection_engine/signals/types'; +import { + DETECTION_ENGINE_SIGNALS_STATUS_URL, + DETECTION_ENGINE_RULES_URL, + DETECTION_ENGINE_QUERY_SIGNALS_URL, +} from '../../../../plugins/security_solution/common/constants'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createSignalsIndex, deleteSignalsIndex, setSignalStatus, getSignalStatusEmptyResponse, + getSimpleRule, + waitFor, + getQueryAllSignals, + getQuerySignalIds, + deleteAllAlerts, } from '../../utils'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const es = getService('es'); describe('open_close_signals', () => { describe('validation checks', () => { @@ -49,6 +62,210 @@ export default ({ getService }: FtrProviderContext) => { await deleteSignalsIndex(supertest); }); + + describe('tests with auditbeat data', () => { + beforeEach(async () => { + await deleteAllAlerts(es); + await createSignalsIndex(supertest); + await esArchiver.load('auditbeat/hosts'); + }); + afterEach(async () => { + await deleteSignalsIndex(supertest); + await deleteAllAlerts(es); + await esArchiver.unload('auditbeat/hosts'); + }); + + it('should be able to execute and get 10 signals', async () => { + const rule = { ...getSimpleRule(), from: '1900-01-01T00:00:00.000Z', query: '*:*' }; + + // create a simple rule + await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(rule) + .expect(200); + + // wait until all the rules show up and are present + await waitFor(async () => { + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + return signalsOpen.hits.hits.length === 10; + }); + + // Get the collection of signals + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + + // expect there to be 10 + expect(signalsOpen.hits.hits.length).equal(10); + }); + + it('should be have set the signals in an open state initially', async () => { + const rule = { ...getSimpleRule(), from: '1900-01-01T00:00:00.000Z', query: '*:*' }; + + // create a simple rule + await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(rule) + .expect(200); + + // wait until all the rules show up and are present + await waitFor(async () => { + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + return signalsOpen.hits.hits.length === 10; + }); + + // Get the collection of signals + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + + const everySignalOpen = signalsOpen.hits.hits.every( + ({ + _source: { + signal: { status }, + }, + }) => status === 'open' + ); + + // expect their initial state to all be open + expect(everySignalOpen).to.eql(true); + }); + + it('should be able to get a count of 10 closed signals when closing 10', async () => { + const rule = { ...getSimpleRule(), from: '1900-01-01T00:00:00.000Z', query: '*:*' }; + + // create a simple rule + await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(rule) + .expect(200); + + // wait until all the rules show up and are present + await waitFor(async () => { + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + return signalsOpen.hits.hits.length === 10; + }); + + // Get a collection of signals to get the id to set open/closed on them + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + + const signalIds = signalsOpen.hits.hits.map((signal) => signal._id); + + // set all of the signals to the state of closed. There is no reason to use a waitUntil here + // as this route intentionally has a waitFor within it and should only return when the query has + // the data. + await supertest + .post(DETECTION_ENGINE_SIGNALS_STATUS_URL) + .set('kbn-xsrf', 'true') + .send(setSignalStatus({ signalIds, status: 'closed' })) + .expect(200); + + const { + body: signalsClosed, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQuerySignalIds(signalIds)) + .expect(200); + + expect(signalsClosed.hits.hits.length).to.equal(10); + }); + + it('should be able close 10 signals immediately and they all should be closed', async () => { + const rule = { ...getSimpleRule(), from: '1900-01-01T00:00:00.000Z', query: '*:*' }; + + // create a simple rule + await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(rule) + .expect(200); + + // wait until all the rules show up and are present + await waitFor(async () => { + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + return signalsOpen.hits.hits.length === 10; + }); + + // Get a collection of signals to get the id to set open/closed on them + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + + const signalIds = signalsOpen.hits.hits.map((signal) => signal._id); + + // set all of the signals to the state of closed. There is no reason to use a waitUntil here + // as this route intentionally has a waitFor within it and should only return when the query has + // the data. + await supertest + .post(DETECTION_ENGINE_SIGNALS_STATUS_URL) + .set('kbn-xsrf', 'true') + .send(setSignalStatus({ signalIds, status: 'closed' })) + .expect(200); + + const { + body: signalsClosed, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQuerySignalIds(signalIds)) + .expect(200); + + const everySignalClosed = signalsClosed.hits.hits.every( + ({ + _source: { + signal: { status }, + }, + }) => status === 'closed' + ); + expect(everySignalClosed).to.eql(true); + }); + }); }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/utils.ts b/x-pack/test/detection_engine_api_integration/utils.ts index b58089e30e930..816df9c133ea1 100644 --- a/x-pack/test/detection_engine_api_integration/utils.ts +++ b/x-pack/test/detection_engine_api_integration/utils.ts @@ -7,6 +7,10 @@ import { Client } from '@elastic/elasticsearch'; import { SuperTest } from 'supertest'; import supertestAsPromised from 'supertest-as-promised'; +import { + Status, + SignalIds, +} from '../../plugins/security_solution/common/detection_engine/schemas/common/schemas'; import { CreateRulesSchema } from '../../plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema'; import { UpdateRulesSchema } from '../../plugins/security_solution/common/detection_engine/schemas/request/update_rules_schema'; import { RulesSchema } from '../../plugins/security_solution/common/detection_engine/schemas/response/rules_schema'; @@ -106,12 +110,24 @@ export const getSignalStatus = () => ({ aggs: { statuses: { terms: { field: 'signal.status', size: 10 } } }, }); +export const getQueryAllSignals = () => ({ + query: { match_all: {} }, +}); + +export const getQuerySignalIds = (signalIds: SignalIds) => ({ + query: { + terms: { + _id: signalIds, + }, + }, +}); + export const setSignalStatus = ({ signalIds, status, }: { - signalIds: string[]; - status: 'open' | 'closed'; + signalIds: SignalIds; + status: Status; }) => ({ signal_ids: signalIds, status, @@ -457,3 +473,29 @@ export const getComplexRuleOutput = (ruleId = 'rule-1'): Partial => query: 'user.name: root or user.name: admin', exceptions_list: [], }); + +// Similar to ReactJs's waitFor from here: https://testing-library.com/docs/dom-testing-library/api-async#waitfor +export const waitFor = async ( + functionToTest: () => Promise, + maxTimeout: number = 5000, + timeoutWait: number = 10 +) => { + await new Promise(async (resolve, reject) => { + let found = false; + let numberOfTries = 0; + while (!found && numberOfTries < Math.floor(maxTimeout / timeoutWait)) { + const itPasses = await functionToTest(); + if (itPasses) { + found = true; + } else { + numberOfTries++; + } + await new Promise((resolveTimeout) => setTimeout(resolveTimeout, timeoutWait)); + } + if (found) { + resolve(); + } else { + reject(new Error('timed out waiting for function condition to be true')); + } + }); +}; diff --git a/x-pack/test/functional/apps/ingest_pipelines/ingest_pipelines.ts b/x-pack/test/functional/apps/ingest_pipelines/ingest_pipelines.ts index e926bcd6ef997..353902f4265a0 100644 --- a/x-pack/test/functional/apps/ingest_pipelines/ingest_pipelines.ts +++ b/x-pack/test/functional/apps/ingest_pipelines/ingest_pipelines.ts @@ -11,22 +11,6 @@ const PIPELINE = { name: 'test_pipeline', description: 'My pipeline description.', version: 1, - processors: JSON.stringify([ - { - set: { - field: 'foo', - value: 'new', - }, - }, - ]), - onFailureProcessors: JSON.stringify([ - { - set: { - field: '_index', - value: 'failed-{{ _index }}', - }, - }, - ]), }; export default ({ getPageObjects, getService }: FtrProviderContext) => { diff --git a/x-pack/test/functional/apps/uptime/locations.ts b/x-pack/test/functional/apps/uptime/locations.ts index 27c42a365ec74..8aefca6a70195 100644 --- a/x-pack/test/functional/apps/uptime/locations.ts +++ b/x-pack/test/functional/apps/uptime/locations.ts @@ -11,41 +11,58 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ getPageObjects, getService }: FtrProviderContext) => { const { uptime: uptimePage } = getPageObjects(['uptime']); const uptime = getService('uptime'); + const es = getService('legacyEs'); const monitor = () => uptime.monitor; + const MONITOR_ID = 'location-testing-id'; + + const LessAvailMonitor = 'less-availability-monitor'; + + const addMonitorWithNoLocation = async () => { + /** + * This mogrify function will strip the documents of their location + * data (but preserve their location name), which is necessary for + * this test to work as desired. + * @param d current document + */ + const mogrifyNoLocation = (d: any) => { + if (d.observer?.geo?.location) { + d.observer.geo.location = undefined; + } + return d; + }; + await makeChecksWithStatus(es, MONITOR_ID, 5, 2, 10000, {}, 'up', mogrifyNoLocation); + }; + + const addLessAvailMonitor = async () => { + await makeChecksWithStatus(es, LessAvailMonitor, 5, 2, 10000, {}, 'up'); + await makeChecksWithStatus(es, LessAvailMonitor, 5, 2, 10000, {}, 'down'); + }; describe('Observer location', () => { const start = moment().subtract('15', 'm').toISOString(); const end = moment().toISOString(); - const MONITOR_ID = 'location-testing-id'; + before(async () => { + await addMonitorWithNoLocation(); + await addLessAvailMonitor(); + await uptime.navigation.goToUptime(); + await uptimePage.goToRoot(true); + }); beforeEach(async () => { - /** - * This mogrify function will strip the documents of their location - * data (but preserve their location name), which is necessary for - * this test to work as desired. - * @param d current document - */ - const mogrifyNoLocation = (d: any) => { - if (d.observer?.geo?.location) { - d.observer.geo.location = undefined; - } - return d; - }; - await makeChecksWithStatus( - getService('legacyEs'), - MONITOR_ID, - 5, - 2, - 10000, - {}, - 'up', - mogrifyNoLocation - ); - await uptime.navigation.goToUptime(); + await addMonitorWithNoLocation(); + await addLessAvailMonitor(); + if (!(await uptime.navigation.isOnDetailsPage())) + await uptimePage.loadDataAndGoToMonitorPage(start, end, MONITOR_ID); + }); + + it('displays the overall availability', async () => { + await monitor().displayOverallAvailability('100.00 %'); + }); - await uptimePage.loadDataAndGoToMonitorPage(start, end, MONITOR_ID); + it('can change the view to map', async () => { + await monitor().toggleToMapView(); }); it('renders the location panel and canvas', async () => { @@ -55,5 +72,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('renders the location missing popover when monitor has location name, but no geo data', async () => { await monitor().locationMissingExists(); }); + + it('displays less monitor availability', async () => { + await uptime.navigation.goToHomeViaBreadCrumb(); + await uptimePage.loadDataAndGoToMonitorPage(start, end, LessAvailMonitor); + await monitor().displayOverallAvailability('50.00 %'); + }); }); }; diff --git a/x-pack/test/functional/page_objects/security_page.ts b/x-pack/test/functional/page_objects/security_page.ts index 57f62ab33e39a..395e41a91f53a 100644 --- a/x-pack/test/functional/page_objects/security_page.ts +++ b/x-pack/test/functional/page_objects/security_page.ts @@ -16,6 +16,7 @@ export function SecurityPageProvider({ getService, getPageObjects }: FtrProvider const testSubjects = getService('testSubjects'); const esArchiver = getService('esArchiver'); const userMenu = getService('userMenu'); + const comboBox = getService('comboBox'); const PageObjects = getPageObjects(['common', 'header', 'settings', 'home', 'error']); interface LoginOptions { @@ -273,11 +274,7 @@ export function SecurityPageProvider({ getService, getPageObjects }: FtrProvider async addIndexToRole(index: string) { log.debug(`Adding index ${index} to role`); - const indexInput = await retry.try(() => - find.byCssSelector('[data-test-subj="indicesInput0"] input') - ); - await indexInput.type(index); - await indexInput.type('\n'); + await comboBox.setCustom('indicesInput0', index); } async addPrivilegeToRole(privilege: string) { @@ -400,104 +397,78 @@ export function SecurityPageProvider({ getService, getPageObjects }: FtrProvider } } - addRole(roleName: string, roleObj: Role) { + async addRole(roleName: string, roleObj: Role) { const self = this; - return ( - this.clickNewRole() - .then(function () { - // We have to use non-test-subject selectors because this markup is generated by ui-select. - log.debug('roleObj.indices[0].names = ' + roleObj.elasticsearch.indices[0].names); - return testSubjects.append('roleFormNameInput', roleName); - }) - .then(function () { - return find.setValue( - '[data-test-subj="indicesInput0"] input', - roleObj.elasticsearch.indices[0].names + '\n' - ); - }) - .then(function () { - return testSubjects.click('restrictDocumentsQuery0'); - }) - .then(function () { - if (roleObj.elasticsearch.indices[0].query) { - return testSubjects.setValue('queryInput0', roleObj.elasticsearch.indices[0].query); - } - }) - - // KibanaPrivilege - .then(async () => { - const globalPrivileges = (roleObj.kibana as any).global; - if (globalPrivileges) { - for (const privilegeName of globalPrivileges) { - const button = await testSubjects.find('addSpacePrivilegeButton'); - await button.click(); - - const spaceSelector = await testSubjects.find('spaceSelectorComboBox'); - await spaceSelector.click(); - - const globalSpaceOption = await find.byCssSelector(`#spaceOption_\\*`); - await globalSpaceOption.click(); - - const basePrivilegeSelector = await testSubjects.find('basePrivilegeComboBox'); - await basePrivilegeSelector.click(); - - const privilegeOption = await find.byCssSelector(`#basePrivilege_${privilegeName}`); - await privilegeOption.click(); - - const createPrivilegeButton = await testSubjects.find('createSpacePrivilegeButton'); - await createPrivilegeButton.click(); - } - } - }) - - .then(function () { - function addPrivilege(privileges: string[]) { - return privileges.reduce(function (promise: Promise, privilegeName: string) { - // We have to use non-test-subject selectors because this markup is generated by ui-select. - return promise - .then(() => self.addPrivilegeToRole(privilegeName)) - .then(() => PageObjects.common.sleep(250)); - }, Promise.resolve()); - } - return addPrivilege(roleObj.elasticsearch.indices[0].privileges); - }) - // clicking the Granted fields and removing the asterix - .then(async function () { - function addGrantedField(field: string[]) { - return field.reduce(function (promise: Promise, fieldName: string) { - return promise - .then(function () { - return find.setValue('[data-test-subj="fieldInput0"] input', fieldName + '\n'); - }) - .then(function () { - return PageObjects.common.sleep(1000); - }); - }, Promise.resolve()); - } - - if (roleObj.elasticsearch.indices[0].field_security) { - // Toggle FLS switch - await testSubjects.click('restrictFieldsQuery0'); - - // have to remove the '*' - return find - .clickByCssSelector( - 'div[data-test-subj="fieldInput0"] [title="Remove * from selection in this group"] svg.euiIcon' - ) - .then(function () { - return addGrantedField(roleObj.elasticsearch.indices[0].field_security!.grant!); - }); - } - }) // clicking save button - .then(async () => { - log.debug('click save button'); - await testSubjects.click('roleFormSaveButton'); - }) - .then(function () { - return PageObjects.common.sleep(5000); - }) - ); + await this.clickNewRole(); + + // We have to use non-test-subject selectors because this markup is generated by ui-select. + log.debug('roleObj.indices[0].names = ' + roleObj.elasticsearch.indices[0].names); + await testSubjects.append('roleFormNameInput', roleName); + + for (const indexName of roleObj.elasticsearch.indices[0].names) { + await comboBox.setCustom('indicesInput0', indexName); + } + + if (roleObj.elasticsearch.indices[0].query) { + await testSubjects.click('restrictDocumentsQuery0'); + await testSubjects.setValue('queryInput0', roleObj.elasticsearch.indices[0].query); + } + + const globalPrivileges = (roleObj.kibana as any).global; + if (globalPrivileges) { + for (const privilegeName of globalPrivileges) { + await testSubjects.click('addSpacePrivilegeButton'); + + await testSubjects.click('spaceSelectorComboBox'); + + const globalSpaceOption = await find.byCssSelector(`#spaceOption_\\*`); + await globalSpaceOption.click(); + + await testSubjects.click('basePrivilegeComboBox'); + + const privilegeOption = await find.byCssSelector(`#basePrivilege_${privilegeName}`); + await privilegeOption.click(); + + await testSubjects.click('createSpacePrivilegeButton'); + } + } + + function addPrivilege(privileges: string[]) { + return privileges.reduce(function (promise: Promise, privilegeName: string) { + return promise + .then(() => self.addPrivilegeToRole(privilegeName)) + .then(() => PageObjects.common.sleep(250)); + }, Promise.resolve()); + } + + await addPrivilege(roleObj.elasticsearch.indices[0].privileges); + + async function addGrantedField(fields: string[]) { + for (const entry of fields) { + await comboBox.setCustom('fieldInput0', entry); + } + } + + // clicking the Granted fields and removing the asterix + if (roleObj.elasticsearch.indices[0].field_security) { + // Toggle FLS switch + await testSubjects.click('restrictFieldsQuery0'); + + // have to remove the '*' + await find.clickByCssSelector( + 'div[data-test-subj="fieldInput0"] [title="Remove * from selection in this group"] svg.euiIcon' + ); + + await addGrantedField(roleObj.elasticsearch.indices[0].field_security!.grant!); + } + + log.debug('click save button'); + await testSubjects.click('roleFormSaveButton'); + + // Signifies that the role management page redirected back to the role grid page, + // and successfully refreshed the grid + await testSubjects.existOrFail('roleRow'); } async selectRole(role: string) { diff --git a/x-pack/test/functional/services/uptime/common.ts b/x-pack/test/functional/services/uptime/common.ts index b5be1e29a0e8c..5f544b5e46010 100644 --- a/x-pack/test/functional/services/uptime/common.ts +++ b/x-pack/test/functional/services/uptime/common.ts @@ -58,13 +58,13 @@ export function UptimeCommonProvider({ getService }: FtrProviderContext) { '[data-test-subj="xpack.uptime.filterBar.filterStatusUp"]' ); if (await upFilter.elementHasClass('euiFilterButton-hasActiveFilters')) { - this.setStatusFilterUp(); + await this.setStatusFilterUp(); } const downFilter = await find.byCssSelector( '[data-test-subj="xpack.uptime.filterBar.filterStatusDown"]' ); if (await downFilter.elementHasClass('euiFilterButton-hasActiveFilters')) { - this.setStatusFilterDown(); + await this.setStatusFilterDown(); } }, async selectFilterItem(filterType: string, option: string) { diff --git a/x-pack/test/functional/services/uptime/monitor.ts b/x-pack/test/functional/services/uptime/monitor.ts index 3137711698ba4..593950fbb7619 100644 --- a/x-pack/test/functional/services/uptime/monitor.ts +++ b/x-pack/test/functional/services/uptime/monitor.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import expect from '@kbn/expect/expect.js'; import { FtrProviderContext } from '../../ftr_provider_context'; export function UptimeMonitorProvider({ getService }: FtrProviderContext) { @@ -17,6 +18,13 @@ export function UptimeMonitorProvider({ getService }: FtrProviderContext) { timeout: 3000, }); }, + async displayOverallAvailability(availabilityVal: string) { + return retry.tryForTime(60 * 1000, async () => { + await testSubjects.existOrFail('uptimeOverallAvailability'); + const availability = await testSubjects.getVisibleText('uptimeOverallAvailability'); + expect(availability).to.be(availabilityVal); + }); + }, async locationMapIsRendered() { return retry.tryForTime(15000, async () => { await testSubjects.existOrFail('xpack.uptime.locationMap.embeddedPanel', { @@ -45,5 +53,8 @@ export function UptimeMonitorProvider({ getService }: FtrProviderContext) { ); }); }, + async toggleToMapView() { + await testSubjects.click('uptimeMonitorToggleMapBtn'); + }, }; } diff --git a/x-pack/test/functional/services/uptime/navigation.ts b/x-pack/test/functional/services/uptime/navigation.ts index 7c5a4632f8627..f8e0c0cff41f4 100644 --- a/x-pack/test/functional/services/uptime/navigation.ts +++ b/x-pack/test/functional/services/uptime/navigation.ts @@ -56,6 +56,7 @@ export function UptimeNavigationProvider({ getService, getPageObjects }: FtrProv }, goToMonitor: async (monitorId: string) => { + // only go to monitor page if not already there if (!(await testSubjects.exists('uptimeMonitorPage', { timeout: 0 }))) { await testSubjects.click(`monitor-page-link-${monitorId}`); await testSubjects.existOrFail('uptimeMonitorPage', { @@ -80,5 +81,13 @@ export function UptimeNavigationProvider({ getService, getPageObjects }: FtrProv await PageObjects.timePicker.setAbsoluteRange(dateStart, dateEnd); await this.goToMonitor(monitorId); }, + + async isOnDetailsPage() { + return await testSubjects.exists('uptimeMonitorPage', { timeout: 0 }); + }, + + async goToHomeViaBreadCrumb() { + await testSubjects.click('breadcrumb first'); + }, }; } diff --git a/yarn.lock b/yarn.lock index 20089fdb83092..e43cb2997e531 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2076,29 +2076,29 @@ debug "^3.1.0" lodash.once "^4.1.1" -"@elastic/apm-rum-core@^5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@elastic/apm-rum-core/-/apm-rum-core-5.2.0.tgz#2ed30dc226c9b5779532ab2e6065a155587bcea4" - integrity sha512-3ti2dhrqfxjHFXgArQI/sVAG2AgZH0kB1nx+2WjLpuAh8gGS4R772M5VXcWcGQb8UW9jrANwwbW2hT2GKv+uOA== +"@elastic/apm-rum-core@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@elastic/apm-rum-core/-/apm-rum-core-5.3.0.tgz#3ae5e84eba5b5287b92458a49755f6e39e7bba5b" + integrity sha512-b/qAnPqi3km808BhSYo+ROpTINm3eVBQ6hNcxOELwKitS3O/HikkwRn5aPkVIhQXOVrbPSufMl1A991nrE3daA== dependencies: error-stack-parser "^1.3.5" opentracing "^0.14.3" promise-polyfill "^8.1.3" -"@elastic/apm-rum-react@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@elastic/apm-rum-react/-/apm-rum-react-1.1.1.tgz#3a2ba91efc9260da55ef6c31cce642a476eba828" - integrity sha512-ZMixw+82VbZIDBnz0dj5Fo4PZ7pnXlLjAA7XTi3AtSIEjpsZa7YIuCFzJdrgb/nOq7MOFkODkFPTiIYAM/yCqg== +"@elastic/apm-rum-react@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@elastic/apm-rum-react/-/apm-rum-react-1.1.2.tgz#274cc414074d05e33e8f0afcad25ef9a30d99452" + integrity sha512-2/wEaPF4EQaVzU8Qj5aYucDc+VFr7438AieON31fx8wsbvnxh9iG+iV7xky2YtT/mf53BbFgZm35L5y/pxCKwA== dependencies: - "@elastic/apm-rum" "^5.1.1" + "@elastic/apm-rum" "^5.2.0" hoist-non-react-statics "^3.3.0" -"@elastic/apm-rum@^5.1.1": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@elastic/apm-rum/-/apm-rum-5.1.1.tgz#02d29fa606e66f9a3a88f629d468b02457b1b183" - integrity sha512-A0O/0ZffcHm1taLuXyFoUmV2+aARr+9+xbmsEDIExLV0yKaNlaLl3UaZrodSOZ1ijlCEsRRS+y2i0md93YKQTA== +"@elastic/apm-rum@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@elastic/apm-rum/-/apm-rum-5.2.0.tgz#b0cfd6e5771b1e765fda2715a38c87746f49f1aa" + integrity sha512-l8/Ji1GMuahMCN5DsALIf+fioKi1QeY4pU0izfVI37se2/fxsMNEDpw52WxJknHdfBE0Imh3FPg4T56J5MO+IQ== dependencies: - "@elastic/apm-rum-core" "^5.2.0" + "@elastic/apm-rum-core" "^5.3.0" "@elastic/charts@19.2.0": version "19.2.0" @@ -4673,6 +4673,11 @@ resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.9.tgz#d868b6febb02666330410fe7f58f3c4b8258be7b" integrity sha512-MNl+rT5UmZeilaPxAVs6YaPC2m6aA8rofviZbhbxpPpl61uKodfdQVsBtgJGTqGizEf02oW3tsVe7FYB8kK14A== +"@types/clone@~0.1.30": + version "0.1.30" + resolved "https://registry.yarnpkg.com/@types/clone/-/clone-0.1.30.tgz#e7365648c1b42136a59c7d5040637b3b5c83b614" + integrity sha1-5zZWSMG0ITalnH1QQGN7O1yDthQ= + "@types/cmd-shim@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/cmd-shim/-/cmd-shim-2.0.0.tgz#c3d81d3c2a51af3c65c19b4f1d493a75abf07a5c" @@ -4842,6 +4847,11 @@ resolved "https://registry.yarnpkg.com/@types/fancy-log/-/fancy-log-1.3.1.tgz#dd94fbc8c2e2ab8ab402ca8d04bb8c34965f0696" integrity sha512-31Dt9JaGfHretvwVxCBrCFL5iC9MQ3zOXpu+8C4qzW0cxc5rJJVGxB5c/vZ+wmeTk/JjPz/D0gv8BZ+Ip6iCqQ== +"@types/fast-json-stable-stringify@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#40363bb847cb86b2c2e1599f1398d11e8329c921" + integrity sha512-mky/O83TXmGY39P1H9YbUpjV6l6voRYlufqfFCvel8l1phuy8HRjdWc1rrPuN53ITBJlbyMSV6z3niOySO5pgQ== + "@types/fetch-mock@^7.3.1": version "7.3.1" resolved "https://registry.yarnpkg.com/@types/fetch-mock/-/fetch-mock-7.3.1.tgz#df7421e8bcb351b430bfbfa5c52bb353826ac94f" @@ -5540,10 +5550,10 @@ "@types/prop-types" "*" "@types/react" "*" -"@types/react-redux@^7.1.7": - version "7.1.7" - resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.7.tgz#12a0c529aba660696947384a059c5c6e08185c7a" - integrity sha512-U+WrzeFfI83+evZE2dkZ/oF/1vjIYgqrb5dGgedkqVV8HEfDFujNgWCwHL89TDuWKb47U0nTBT6PLGq4IIogWg== +"@types/react-redux@^7.1.9": + version "7.1.9" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.9.tgz#280c13565c9f13ceb727ec21e767abe0e9b4aec3" + integrity sha512-mpC0jqxhP4mhmOl3P4ipRsgTgbNofMRXJb08Ms6gekViLj61v1hOZEKWDCyWsdONr6EjEA6ZHXC446wdywDe0w== dependencies: "@types/hoist-non-react-statics" "^3.3.0" "@types/react" "*" @@ -7355,6 +7365,11 @@ array-find@^1.0.0: resolved "https://registry.npmjs.org/array-find/-/array-find-1.0.0.tgz#6c8e286d11ed768327f8e62ecee87353ca3e78b8" integrity sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg= +array-flat-polyfill@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-flat-polyfill/-/array-flat-polyfill-1.0.1.tgz#1e3a4255be619dfbffbfd1d635c1cf357cd034e7" + integrity sha512-hfJmKupmQN0lwi0xG6FQ5U8Rd97RnIERplymOv/qpq8AoNKPPAnxJadjFA23FNWm88wykh9HmpLJUUwUtNU/iw== + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -10089,7 +10104,7 @@ clone@^1.0.0, clone@^1.0.2, clone@^1.0.4: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= -clone@^2.1.1: +clone@^2.1.1, clone@~2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= @@ -11399,15 +11414,15 @@ cytoscape@^3.10.0: heap "^0.2.6" lodash.debounce "^4.0.8" -d3-array@1, d3-array@^1.1.1, d3-array@^1.2.0, d3-array@^1.2.4: +d3-array@1, d3-array@1.2.4, d3-array@^1.1.1, d3-array@^1.2.0, d3-array@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f" integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw== -d3-array@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.0.3.tgz#9c0531eda701e416f28a030e3d4e6179ba74f19f" - integrity sha512-C7g4aCOoJa+/K5hPVqZLG8wjYHsTUROTk7Z1Ep9F4P5l+WVrvV0+6nAZ1wKTRLMhFWpGbozxUpyjIPZYAaLi+g== +"d3-array@1.2.0 - 2", d3-array@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.4.0.tgz#87f8b9ad11088769c82b5ea846bcb1cc9393f242" + integrity sha512-KQ41bAF2BMakf/HdKT865ALd4cgND6VcIztVQZUTt0+BH3RWy6ZYnHghVXf6NFjt2ritLr8H1T8LreAAlfiNcw== d3-cloud@1.2.5: version "1.2.5" @@ -11416,131 +11431,101 @@ d3-cloud@1.2.5: dependencies: d3-dispatch "^1.0.3" -d3-collection@1, d3-collection@^1.0.7: +d3-collection@1, d3-collection@^1.0.3, d3-collection@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.7.tgz#349bd2aa9977db071091c13144d5e4f16b5b310e" integrity sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A== -d3-collection@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.4.tgz#342dfd12837c90974f33f1cc0a785aea570dcdc2" - integrity sha1-NC39EoN8kJdPM/HMCnha6lcNzcI= - -d3-color@1, d3-color@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.2.3.tgz#6c67bb2af6df3cc8d79efcc4d3a3e83e28c8048f" - integrity sha512-x37qq3ChOTLd26hnps36lexMRhNXEtVxZ4B25rL0DVdDsGQIJGB18S7y9XDwlDD6MD/ZBzITCf4JjGMM10TZkw== - -d3-color@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.0.3.tgz#bc7643fca8e53a8347e2fbdaffa236796b58509b" - integrity sha1-vHZD/KjlOoNH4vva/6I2eWtYUJs= - -d3-color@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.4.0.tgz#89c45a995ed773b13314f06460df26d60ba0ecaf" - integrity sha512-TzNPeJy2+iEepfiL92LAAB7fvnp/dV2YwANPVHdDWmYMm23qIJBYww3qT8I8C1wXrmrg4UWs7BKc2tKIgyjzHg== +d3-color@1, d3-color@^1.0.3, d3-color@^1.4.0, d3-color@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.4.1.tgz#c52002bf8846ada4424d55d97982fef26eb3bc8a" + integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q== d3-contour@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-1.1.2.tgz#21f5456fcf57645922d69a27a58e782c91f842b3" - integrity sha512-po2Gxab58NQMAaVLj1ruASkmHlB8JebFFhm2cDVs2JFjTv9AYZpRQEWMycLoP7JH530dBDl90HI30g5EnpTJfA== - dependencies: - d3-array "^1.1.1" - -d3-contour@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-1.3.2.tgz#652aacd500d2264cb3423cee10db69f6f59bead3" integrity sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg== dependencies: d3-array "^1.1.1" +d3-delaunay@^5.2.1: + version "5.3.0" + resolved "https://registry.yarnpkg.com/d3-delaunay/-/d3-delaunay-5.3.0.tgz#b47f05c38f854a4e7b3cea80e0bb12e57398772d" + integrity sha512-amALSrOllWVLaHTnDLHwMIiz0d1bBu9gZXd1FiLfXf8sHcX9jrcj81TVZOqD4UX7MgBZZ07c8GxzEgBpJqc74w== + dependencies: + delaunator "4" + d3-dispatch@1, d3-dispatch@^1.0.3: - version "1.0.5" - resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.5.tgz#e25c10a186517cd6c82dd19ea018f07e01e39015" - integrity sha512-vwKx+lAqB1UuCeklr6Jh1bvC4SZgbSqbkGBLClItFBIYH4vqDJCA7qfoy14lXmJdnBOdxndAMxjCbImJYW7e6g== + version "1.0.6" + resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.6.tgz#00d37bcee4dd8cd97729dd893a0ac29caaba5d58" + integrity sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA== -d3-dsv@^1.0.10: - version "1.1.1" - resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.1.1.tgz#aaa830ecb76c4b5015572c647cc6441e3c7bb701" - integrity sha512-1EH1oRGSkeDUlDRbhsFytAXU6cAmXFzc52YUe6MRlPClmWb85MP1J5x+YJRzya4ynZWnbELdSAvATFW/MbxaXw== +d3-dsv@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.2.0.tgz#9d5f75c3a5f8abd611f74d3f5847b0d4338b885c" + integrity sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g== dependencies: commander "2" iconv-lite "0.4" rw "1" d3-ease@1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.5.tgz#8ce59276d81241b1b72042d6af2d40e76d936ffb" - integrity sha512-Ct1O//ly5y5lFM9YTdu+ygq7LleSgSE4oj7vUt9tPLHUi8VCV7QoizGpdWRWAwCO9LdYzIrQDg97+hGVdsSGPQ== + version "1.0.6" + resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.6.tgz#ebdb6da22dfac0a22222f2d4da06f66c416a0ec0" + integrity sha512-SZ/lVU7LRXafqp7XtIcBdxnWl8yyLpgOmzAk0mWBI9gXNzLDx5ybZgnRbH9dN/yY5tzVBqCQ9avltSnqVwessQ== -d3-force@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-1.2.0.tgz#60713f7efe8764f53e685d69433c06914dc4ea4c" - integrity sha512-PFLcDnRVANHMudbQlIB87gcfQorEsDIAvRpZ2bNddfM/WxdsEkyrEaOIPoydhH1I1V4HPjNLGOMLXCA0AuGQ9w== +d3-force@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-2.0.1.tgz#31750eee8c43535301d571195bf9683beda534e2" + integrity sha512-zh73/N6+MElRojiUG7vmn+3vltaKon7iD5vB/7r9nUaBeftXMzRo5IWEG63DLBCto4/8vr9i3m9lwr1OTJNiCg== dependencies: - d3-collection "1" d3-dispatch "1" d3-quadtree "1" d3-timer "1" -d3-format@1, d3-format@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.3.2.tgz#6a96b5e31bcb98122a30863f7d92365c00603562" - integrity sha512-Z18Dprj96ExragQ0DeGi+SYPQ7pPfRMtUXtsg/ChVIKNBCzjO8XYJvRTC1usblx52lqge56V5ect+frYTQc8WQ== - -d3-format@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.2.1.tgz#4e19ecdb081a341dafaf5f555ee956bcfdbf167f" - integrity sha512-U4zRVLDXW61bmqoo+OJ/V687e1T5nVd3TAKAJKgtpZ/P1JsMgyod0y9br+mlQOryTAACdiXI3wCjuERHFNp91w== +d3-format@1, d3-format@^1.2.0, d3-format@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.4.tgz#356925f28d0fd7c7983bfad593726fce46844030" + integrity sha512-TWks25e7t8/cqctxCmxpUuzZN11QxIA7YrMbram94zMQ0PXjE4LVIMe/f6a4+xxL8HQ3OsAFULOINQi1pE62Aw== -d3-geo@^1.10.0, d3-geo@^1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.11.3.tgz#5bb08388f45e4b281491faa72d3abd43215dbd1c" - integrity sha512-n30yN9qSKREvV2fxcrhmHUdXP9TNH7ZZj3C/qnaoU0cVf/Ea85+yT7HY7i8ySPwkwjCNYtmKqQFTvLFngfkItQ== +d3-geo-projection@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/d3-geo-projection/-/d3-geo-projection-2.9.0.tgz#826db62f748e8ecd67cd00aced4c26a236ec030c" + integrity sha512-ZULvK/zBn87of5rWAfFMc9mJOipeSo57O+BBitsKIXmU4rTVAnX1kSsJkE0R+TxY8pGNoM1nbyRRE7GYHhdOEQ== dependencies: + commander "2" d3-array "1" + d3-geo "^1.12.0" + resolve "^1.1.10" -d3-geo@^1.6.4: - version "1.9.1" - resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.9.1.tgz#157e3b0f917379d0f73bebfff3be537f49fa7356" - integrity sha512-l9wL/cEQkyZQYXw3xbmLsH3eQ5ij+icNfo4r0GrLa5rOCZR/e/3am45IQ0FvQ5uMsv+77zBRunLc9ufTWSQYFA== +d3-geo@^1.12.0, d3-geo@^1.12.1, d3-geo@^1.6.4: + version "1.12.1" + resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.12.1.tgz#7fc2ab7414b72e59fbcbd603e80d9adc029b035f" + integrity sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg== dependencies: d3-array "1" -d3-hierarchy@^1.1.4: - version "1.1.5" - resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.5.tgz#a1c845c42f84a206bcf1c01c01098ea4ddaa7a26" - integrity sha1-ochFxC+Eoga88cAcAQmOpN2qeiY= - -d3-hierarchy@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz#7a6317bd3ed24e324641b6f1e76e978836b008cc" - integrity sha512-L+GHMSZNwTpiq4rt9GEsNcpLa4M96lXMR8M/nMG9p5hBE0jy6C+3hWtyZMenPQdwla249iJy7Nx0uKt3n+u9+w== - -d3-interpolate@1, d3-interpolate@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.3.2.tgz#417d3ebdeb4bc4efcc8fd4361c55e4040211fd68" - integrity sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w== - dependencies: - d3-color "1" +d3-hierarchy@^1.1.4, d3-hierarchy@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz#2f6bee24caaea43f8dc37545fa01628559647a83" + integrity sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ== -d3-interpolate@^1.1.4: - version "1.1.6" - resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.1.6.tgz#2cf395ae2381804df08aa1bf766b7f97b5f68fb6" - integrity sha512-mOnv5a+pZzkNIHtw/V6I+w9Lqm9L5bG3OTXPM5A+QO0yyVMQ4W1uZhR+VOJmazaOZXri2ppbiZ5BUNWT0pFM9A== +d3-interpolate@1, d3-interpolate@^1.1.4, d3-interpolate@^1.2.0, d3-interpolate@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.4.0.tgz#526e79e2d80daa383f9e0c1c1c7dcc0f0583e987" + integrity sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA== dependencies: d3-color "1" -d3-path@1, d3-path@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.7.tgz#8de7cd693a75ac0b5480d3abaccd94793e58aae8" - integrity sha512-q0cW1RpvA5c5ma2rch62mX8AYaiLX0+bdaSM2wxSU9tXjU4DNvkx9qiUvjkuWCj3p22UO/hlPivujqMiR9PDzA== +d3-path@1, d3-path@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf" + integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg== d3-quadtree@1: - version "1.0.6" - resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.6.tgz#d1ab2a95a7f27bbde88582c94166f6ae35f32056" - integrity sha512-NUgeo9G+ENQCQ1LsRr2qJg3MQ4DJvxcDNCiohdJGHt5gRhBW6orIB5m5FJ9kK3HNL8g9F4ERVoBzcEwQBfXWVA== + version "1.0.7" + resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.7.tgz#ca8b84df7bb53763fe3c2f24bd435137f4e53135" + integrity sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA== d3-sankey@^0.7.1: version "0.7.1" @@ -11551,14 +11536,6 @@ d3-sankey@^0.7.1: d3-collection "1" d3-shape "^1.2.0" -d3-scale-chromatic@^1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-1.3.3.tgz#dad4366f0edcb288f490128979c3c793583ed3c0" - integrity sha512-BWTipif1CimXcYfT02LKjAyItX5gKiwxuPRgr4xM58JwlLocWbjPLI7aMEjkcoOQXMkYsmNsvv3d2yl/OKuHHw== - dependencies: - d3-color "1" - d3-interpolate "1" - d3-scale@1.0.7, d3-scale@^1.0.5, d3-scale@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-1.0.7.tgz#fa90324b3ea8a776422bd0472afab0b252a0945d" @@ -11572,65 +11549,50 @@ d3-scale@1.0.7, d3-scale@^1.0.5, d3-scale@^1.0.7: d3-time "1" d3-time-format "2" -d3-scale@^2.1.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-2.2.2.tgz#4e880e0b2745acaaddd3ede26a9e908a9e17b81f" - integrity sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw== +d3-scale@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-3.2.1.tgz#da1684adce7261b4bc7a76fe193d887f0e909e69" + integrity sha512-huz5byJO/6MPpz6Q8d4lg7GgSpTjIZW/l+1MQkzKfu2u8P6hjaXaStOpmyrD6ymKoW87d2QVFCKvSjLwjzx/rA== dependencies: - d3-array "^1.2.0" - d3-collection "1" + d3-array "1.2.0 - 2" d3-format "1" - d3-interpolate "1" + d3-interpolate "^1.2.0" d3-time "1" d3-time-format "2" d3-selection@^1.0.2, d3-selection@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.4.0.tgz#ab9ac1e664cf967ebf1b479cc07e28ce9908c474" - integrity sha512-EYVwBxQGEjLCKF2pJ4+yrErskDnz5v403qvAid96cNdCMr8rmCYfY5RGzWz24mdIbxmDf6/4EAH+K9xperD5jg== - -d3-shape@^1.1.0, d3-shape@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.2.0.tgz#45d01538f064bafd05ea3d6d2cb748fd8c41f777" - integrity sha1-RdAVOPBkuv0F6j1tLLdI/YxB93c= - dependencies: - d3-path "1" - -d3-shape@^1.2.2: - version "1.3.4" - resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.4.tgz#358e76014645321eecc7c364e188f8ae3d2a07d4" - integrity sha512-izaz4fOpOnY3CD17hkZWNxbaN70sIGagLR/5jb6RS96Y+6VqX+q1BQf1av6QSBRdfULi3Gb8Js4CzG4+KAPjMg== - dependencies: - d3-path "1" + version "1.4.1" + resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.4.1.tgz#98eedbbe085fbda5bafa2f9e3f3a2f4d7d622a98" + integrity sha512-BTIbRjv/m5rcVTfBs4AMBLKs4x8XaaLkwm28KWu9S2vKNqXkXt2AH2Qf0sdPZHjFxcWg/YL53zcqAz+3g4/7PA== -d3-shape@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.5.tgz#e81aea5940f59f0a79cfccac012232a8987c6033" - integrity sha512-VKazVR3phgD+MUCldapHD7P9kcrvPcexeX/PkMJmkUov4JM8IxsSg1DvbYoYich9AtdTsa5nNk2++ImPiDiSxg== +d3-shape@^1.1.0, d3-shape@^1.2.0, d3-shape@^1.3.4, d3-shape@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7" + integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw== dependencies: d3-path "1" -d3-time-format@2, d3-time-format@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.1.3.tgz#ae06f8e0126a9d60d6364eac5b1533ae1bac826b" - integrity sha512-6k0a2rZryzGm5Ihx+aFMuO1GgelgIz+7HhB4PH4OEndD5q2zGn1mDfRdNrulspOfR6JXkb2sThhDK41CSK85QA== +d3-time-format@2, d3-time-format@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.2.3.tgz#0c9a12ee28342b2037e5ea1cf0b9eb4dd75f29cb" + integrity sha512-RAHNnD8+XvC4Zc4d2A56Uw0yJoM7bsvOlJR33bclxq399Rak/b9bhvu/InjxdWhPtkgU53JJcleJTGkNRnN6IA== dependencies: d3-time "1" -d3-time@1, d3-time@^1.0.10: - version "1.0.11" - resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.11.tgz#1d831a3e25cd189eb256c17770a666368762bbce" - integrity sha512-Z3wpvhPLW4vEScGeIMUckDW7+3hWKOQfAWg/U7PlWBnQmeKQ00gCUsTtWSYulrKNA7ta8hJ+xXc6MHrMuITwEw== +d3-time@1, d3-time@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1" + integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA== -d3-timer@1, d3-timer@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.9.tgz#f7bb8c0d597d792ff7131e1c24a36dd471a471ba" - integrity sha512-rT34J5HnQUHhcLvhSB9GjCkN0Ddd5Y8nCwDBG2u6wQEeYxT/Lf51fTFFkldeib/sE/J0clIe0pnCfs6g/lRbyg== +d3-timer@1, d3-timer@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.10.tgz#dfe76b8a91748831b13b6d9c793ffbd508dd9de5" + integrity sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw== d3-transition@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.2.0.tgz#f538c0e21b2aa1f05f3e965f8567e81284b3b2b8" - integrity sha512-VJ7cmX/FPIPJYuaL2r1o1EMHLttvoIuZhhuAlRoOxDzogV8iQS6jYulDm3xEU3TqL80IZIhI551/ebmCMrkvhw== + version "1.3.2" + resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.3.2.tgz#a98ef2151be8d8600543434c1ca80140ae23b398" + integrity sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA== dependencies: d3-color "1" d3-dispatch "1" @@ -11640,9 +11602,9 @@ d3-transition@^1.0.1: d3-timer "1" d3-voronoi@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.2.tgz#1687667e8f13a2d158c80c1480c5a29cb0d8973c" - integrity sha1-Fodmfo8TotFYyAwUgMWinLDYlzw= + version "1.1.4" + resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.4.tgz#dd3c78d7653d2bb359284ae478645d95944c8297" + integrity sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg== d3@3.5.17: version "3.5.17" @@ -12056,6 +12018,11 @@ del@^5.1.0: rimraf "^3.0.0" slash "^3.0.0" +delaunator@4: + version "4.0.1" + resolved "https://registry.yarnpkg.com/delaunator/-/delaunator-4.0.1.tgz#3d779687f57919a7a418f8ab947d3bddb6846957" + integrity sha512-WNPWi1IRKZfCt/qIDMfERkDp93+iZEmOxN2yy4Jg+Xhv8SLk2UTqqbe1sfiipn0and9QrE914/ihdx82Y/Giag== + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -14211,7 +14178,7 @@ fancy-log@^1.3.2: color-support "^1.1.3" time-stamp "^1.0.0" -fast-deep-equal@^1.0.0, fast-deep-equal@^2.0.1, fast-deep-equal@^3.1.1: +fast-deep-equal@^1.0.0, fast-deep-equal@^2.0.1, fast-deep-equal@^3.1.1, fast-deep-equal@~3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== @@ -14250,7 +14217,7 @@ fast-glob@^3.0.3, fast-glob@^3.1.1, fast-glob@^3.2.2: micromatch "^4.0.2" picomatch "^2.2.1" -fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -19397,6 +19364,11 @@ json-stringify-pretty-compact@1.2.0, json-stringify-pretty-compact@^1.0.1: resolved "https://registry.yarnpkg.com/json-stringify-pretty-compact/-/json-stringify-pretty-compact-1.2.0.tgz#0bc316b5e6831c07041fc35612487fb4e9ab98b8" integrity sha512-/11Pj1OyX814QMKO7K8l85SHPTr/KsFxHp8GE2zVa0BtJgGimDjXHfM3FhC7keQdWDea7+nXf+f1de7ATZcZkQ== +json-stringify-pretty-compact@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/json-stringify-pretty-compact/-/json-stringify-pretty-compact-2.0.0.tgz#e77c419f52ff00c45a31f07f4c820c2433143885" + integrity sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ== + json-stringify-safe@5.0.1, json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -25480,14 +25452,13 @@ react-redux@^5.1.2: react-is "^16.6.0" react-lifecycles-compat "^3.0.0" -react-redux@^7.1.0, react-redux@^7.1.1, react-redux@^7.1.3: - version "7.1.3" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.1.3.tgz#717a3d7bbe3a1b2d535c94885ce04cdc5a33fc79" - integrity sha512-uI1wca+ECG9RoVkWQFF4jDMqmaw0/qnvaSvOoL/GA4dNxf6LoV8sUAcNDvE5NWKs4hFpn0t6wswNQnY3f7HT3w== +react-redux@^7.1.0, react-redux@^7.1.1, react-redux@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.0.tgz#f970f62192b3981642fec46fd0db18a074fe879d" + integrity sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA== dependencies: "@babel/runtime" "^7.5.5" hoist-non-react-statics "^3.3.0" - invariant "^2.2.4" loose-envify "^1.4.0" prop-types "^15.7.2" react-is "^16.9.0" @@ -26882,76 +26853,34 @@ resolve@1.1.7, resolve@1.1.x, resolve@~1.1.0, resolve@~1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@1.8.1, resolve@^1.3.2, resolve@^1.8.1: +resolve@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" integrity sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA== dependencies: path-parse "^1.0.5" -resolve@^1.1.5: +resolve@^1.1.10, resolve@^1.1.5, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.0, resolve@^1.11.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.5.0, resolve@^1.7.1, resolve@^1.8.1: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== dependencies: path-parse "^1.0.6" -resolve@^1.1.6, resolve@^1.1.7: - version "1.5.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36" - integrity sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw== - dependencies: - path-parse "^1.0.5" - -resolve@^1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba" - integrity sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg== - dependencies: - path-parse "^1.0.6" - -resolve@^1.10.1, resolve@^1.11.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.0.tgz#4014870ba296176b86343d50b60f3b50609ce232" - integrity sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw== - dependencies: - path-parse "^1.0.6" - -resolve@^1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e" - integrity sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw== - dependencies: - path-parse "^1.0.6" - -resolve@^1.12.0, resolve@^1.4.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" - integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w== +resolve@~1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.1.tgz#664842ac960795bbe758221cdccda61fb64b5f18" + integrity sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA== dependencies: path-parse "^1.0.6" -resolve@^1.13.1, resolve@~1.14.2: +resolve@~1.14.2: version "1.14.2" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.2.tgz#dbf31d0fa98b1f29aa5169783b9c290cb865fea2" integrity sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ== dependencies: path-parse "^1.0.6" -resolve@^1.5.0, resolve@^1.7.1: - version "1.7.1" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz#aadd656374fd298aee895bc026b8297418677fd3" - integrity sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw== - dependencies: - path-parse "^1.0.5" - -resolve@~1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.1.tgz#664842ac960795bbe758221cdccda61fb64b5f18" - integrity sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA== - dependencies: - path-parse "^1.0.6" - responselike@1.0.2, responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" @@ -30033,13 +29962,20 @@ topo@3.x.x: dependencies: hoek "5.x.x" -topojson-client@3.0.0, topojson-client@^3.0.0: +topojson-client@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/topojson-client/-/topojson-client-3.0.0.tgz#1f99293a77ef42a448d032a81aa982b73f360d2f" integrity sha1-H5kpOnfvQqRI0DKoGqmCtz82DS8= dependencies: commander "2" +topojson-client@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/topojson-client/-/topojson-client-3.1.0.tgz#22e8b1ed08a2b922feeb4af6f53b6ef09a467b99" + integrity sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw== + dependencies: + commander "2" + touch@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" @@ -30232,17 +30168,12 @@ tsd@^0.7.4: typescript "^3.0.1" update-notifier "^2.5.0" -tslib@^1, tslib@^1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== - -tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.2, tslib@^1.9.3: - version "1.9.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" - integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== +tslib@^1, tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: + version "1.13.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" + integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== -tslib@^2.0.0: +tslib@^2.0.0, tslib@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.0.tgz#18d13fc2dce04051e20f074cc8387fd8089ce4f3" integrity sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g== @@ -31612,207 +31543,234 @@ vary@^1, vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= -vega-canvas@^1.0.1, vega-canvas@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/vega-canvas/-/vega-canvas-1.1.0.tgz#99ce74d4510a46fc9ed1a8721014da725898ec9f" - integrity sha512-0RBwjnrFf4VRhNm5ICY+o1/q6mynli+VheOXKGML9mElRsplZgzd/pv9NnNRNigU+M66Kr7zUpskoakh8EhW9Q== +vega-canvas@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/vega-canvas/-/vega-canvas-1.2.2.tgz#f31aae9ac1e1ed01bb7817a1e53099279e2d3d43" + integrity sha512-39h8/fZp4kDwSeDGIEoyEiIgtP3mgY3D08InD1Ldm0FntePpSe1tXzC1zcvoLe/+f7Qprl6Jfwux/ksOXvpj2w== -vega-crossfilter@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/vega-crossfilter/-/vega-crossfilter-3.0.1.tgz#8b4394fb5e354e5c6f79ca9f491531a292c04209" - integrity sha512-GNCP0k1otJKtE9SnYm1cDBqUfBvWTaxJ3/bdMpWvGNUtAdDBAlrtspDBTpwMu4MLNWbAy1zp9jN0ztCXBZF29Q== +vega-crossfilter@~4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/vega-crossfilter/-/vega-crossfilter-4.0.2.tgz#739a26eb8eb741b3e1725679d09ee2af56f983b5" + integrity sha512-wlKpqBEUpDd/Y3aaC1u91lebXR+sS7LElYv2jGDDG5pA+RS8lRo3NmSClKVBM5NcY80IeMywG+0a/ogzVeBrPQ== dependencies: - d3-array "^2.0.2" - vega-dataflow "^4.1.0" - vega-util "^1.7.0" + d3-array "^2.4.0" + vega-dataflow "^5.5.1" + vega-util "^1.13.2" -vega-dataflow@^4.0.0, vega-dataflow@^4.0.4, vega-dataflow@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/vega-dataflow/-/vega-dataflow-4.1.0.tgz#c63abee8502eedf42a972ad5d3a2ce7475aab7d8" - integrity sha512-LuXoN3LkYWNYTPeMiOgSlw2TZAWjmN46Q9HmHM8ClhXYAj+pYme3IPdtYn1OmcvWe4rKeiYgNYrtJCgTOvCepg== +vega-dataflow@^5.5.1, vega-dataflow@^5.6.0, vega-dataflow@^5.7.0, vega-dataflow@~5.7.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/vega-dataflow/-/vega-dataflow-5.7.0.tgz#811381d946e044cafe093c7c7f8111d366cfbc11" + integrity sha512-W8Q6NBCmk3TCDjTiy/I2PA4JTQljqp3XF6227CiCvZfhmCZCeHchuZKrgwJUBVZ2CoqT+UL2JpCi1SFzrPObIQ== dependencies: - vega-loader "^3.1.0" - vega-util "^1.7.0" + vega-format "^1.0.0" + vega-loader "^4.3.0" + vega-util "^1.14.0" -vega-encode@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/vega-encode/-/vega-encode-3.2.2.tgz#b7bdee200629b1d54de8267b1b8aafef9f1be8b7" - integrity sha512-Hmk+ReH6R1wTnz56gWyk8CnzgAzq11QYkrEzw794MMY2l61EG3sX9veyZ9AdtDufOq9oDa58/kfgk65UD9A+sA== - dependencies: - d3-array "^2.0.2" - d3-format "^1.3.2" - d3-interpolate "^1.3.2" - vega-dataflow "^4.1.0" - vega-scale "^2.5.0" - vega-util "^1.7.0" +vega-encode@~4.8.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/vega-encode/-/vega-encode-4.8.0.tgz#948f3302892360cffc7a76d2107295c421835d12" + integrity sha512-1OYYblMu9oyhuRIiFRK+R7/ChwXn3ckWyl6omK5Q46vkeLZhafBNb8ZdqftPoM/5BnZUTpia0SQ06AGX8VDjHw== + dependencies: + d3-array "^2.4.0" + d3-interpolate "^1.4.0" + vega-dataflow "^5.7.0" + vega-scale "^7.0.0" + vega-util "^1.14.0" -vega-event-selector@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/vega-event-selector/-/vega-event-selector-2.0.0.tgz#6af8dc7345217017ceed74e9155b8d33bad05d42" - integrity sha512-EZeStM/7LNfJiRuop0lvhOR52Q1l9i/EIYUnm/XddhjR+UqhPkeCmZcffMTr41z3aGm/zciVLlKanUWNT+jQ1A== +vega-event-selector@^2.0.3, vega-event-selector@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/vega-event-selector/-/vega-event-selector-2.0.3.tgz#760c61af7ab5c325d3274fd3ab284d067ff16f8c" + integrity sha512-rUnAvBSy5tkk+0MELY7qICTgjMNjH/DDNIH603q3GRi+bBRCd4MlJxWrPYBhwZIYpmr6XCe130lZ90/F5SgVfA== -vega-expression@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/vega-expression/-/vega-expression-2.4.0.tgz#02eb789623bf24c959b7b8756bf2cacb10bd54a6" - integrity sha512-Ti2rqnXscu281omgAukd37yDcw2AjETshB8tWcTOciFPUkfLQKwL6d1UIuIq4RkWuKDTU9Htd2UCpvFDu+O1eQ== +vega-expression@^2.6.4, vega-expression@^2.6.5, vega-expression@~2.6.5: + version "2.6.5" + resolved "https://registry.yarnpkg.com/vega-expression/-/vega-expression-2.6.5.tgz#7bda7524b9223cbbf9034071695c7c2a9bd81971" + integrity sha512-3hJts0gKomu3ePXYeIb+VAw7yNKoHJ6VqSKsHHFPyoEGNdwmlgI5d9IBblelPCiMCHK4sMt7h1OTWB33cfxZGA== dependencies: - vega-util "^1.7.0" + vega-util "^1.14.0" -vega-force@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/vega-force/-/vega-force-3.0.0.tgz#f5d10bb0a49e41c47f2d83441e407510948eb89a" - integrity sha512-Uar26RDxDQEpIdWBIFKnOr6/B30RU8/2qBtoiux1C3goZIWBRkXNlCR5kMDkll8Mg60deD6ynflsXXNwyGS69w== +vega-force@~4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/vega-force/-/vega-force-4.0.4.tgz#b73cba2e169cc248c994c9d981c374eb06aff764" + integrity sha512-ETTezb3lpQIbrk8pdmv4WpoNlChWdIK1Hv5CHL8Q/oOT/lIop/NHnI+JZO4yuzaYv+o3UqNWPcjiY0U5/i51dw== dependencies: - d3-force "^1.1.0" - vega-dataflow "^4.0.0" - vega-util "^1.7.0" + d3-force "^2.0.1" + vega-dataflow "^5.5.1" + vega-util "^1.13.2" -vega-geo@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/vega-geo/-/vega-geo-3.1.1.tgz#5ff84061dea93d89a453e1b56b3444a6031810f6" - integrity sha512-EltBQmid6DZ7d4iArgTnsGRsx4ZaHrwvaegq6iIwWp7GHtJ8i+8bzPFfHo1pBuRVmHG4ZA2NH+cNaW2IIgWcPg== - dependencies: - d3-array "^2.0.2" - d3-contour "^1.3.2" - d3-geo "^1.11.3" - vega-dataflow "^4.1.0" - vega-projection "^1.2.0" - vega-util "^1.7.0" +vega-format@^1.0.0, vega-format@^1.0.1, vega-format@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vega-format/-/vega-format-1.0.1.tgz#94b5be9eb3d6a8b315fcf3d523ed6a1bb2ea4d71" + integrity sha512-f9IZ+SDHVFFneDDc+d8RfeJhXXvUgquAuM+1MZ2Rjf4xqpg+E8FSNQkh8wjeo82mc6G3KVa9hynSdfN/a0AktQ== + dependencies: + d3-array "^2.4.0" + d3-format "^1.4.4" + d3-time-format "^2.2.3" + vega-time "^2.0.0" + vega-util "^1.14.0" -vega-hierarchy@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/vega-hierarchy/-/vega-hierarchy-3.1.0.tgz#ce3df9ab09b3324144df9273d650391f082696ec" - integrity sha512-zPxOsQbswVDMfn9JdDG0ihZA4qhQL5WJxBsSRFsMeuyDTFuE6biBInpm/g0QDGmHMF2EOY4AwD2WRyF+jAyTqw== +vega-functions@^5.7.0, vega-functions@^5.7.1, vega-functions@~5.7.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/vega-functions/-/vega-functions-5.7.1.tgz#4231f013c7a8a6cdcefaa6ee08c96f361c0c9ef5" + integrity sha512-PQUcRkLAJwiRK+Y2o8MZdHJOHZwGcIYKvnYZnes2IY5433lhKYL7b1DmwQhUqyHyKCudqlz/pRnoLpmuL8sAgg== + dependencies: + d3-array "^2.4.0" + d3-color "^1.4.1" + d3-geo "^1.12.1" + vega-dataflow "^5.7.0" + vega-expression "^2.6.5" + vega-scale "^7.0.0" + vega-scenegraph "^4.8.0" + vega-selections "^5.1.1" + vega-statistics "^1.7.5" + vega-time "^2.0.1" + vega-util "^1.14.0" + +vega-geo@~4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/vega-geo/-/vega-geo-4.3.4.tgz#1bd26e438a304ff2596d51f57acee0e119e81313" + integrity sha512-sfMK1XGCLzMnfmy7fBJ2D+h8NG5WDwnSiPvcsjgwwAyonlUgCZWKdrNouAyLaRODy5ICZUEj/GDILSBdlIfUCg== + dependencies: + d3-array "^2.4.0" + d3-color "^1.4.1" + d3-geo "^1.12.1" + vega-canvas "^1.2.2" + vega-dataflow "^5.6.0" + vega-projection "^1.4.2" + vega-statistics "^1.7.5" + vega-util "^1.14.0" + +vega-hierarchy@~4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/vega-hierarchy/-/vega-hierarchy-4.0.6.tgz#e286e917e47f1d4d9bfefa278cfdfa163d9f1225" + integrity sha512-v71NQzz9503aBJgRPnrBEZ/87q58EjwylmAs3uh+SaI5ocMCn9+goE+x5ZwZ0gNT9qJv4Umm5L3GZ9h8LuXjlg== dependencies: - d3-collection "^1.0.7" - d3-hierarchy "^1.1.8" - vega-dataflow "^4.0.4" - vega-util "^1.7.0" + d3-hierarchy "^1.1.9" + vega-dataflow "^5.5.1" + vega-util "^1.13.2" -vega-lib@4.3.0: +vega-lite@^4.13.1: + version "4.13.1" + resolved "https://registry.yarnpkg.com/vega-lite/-/vega-lite-4.13.1.tgz#e9ee31c76cb93565e287a760700e432e57244064" + integrity sha512-OHZSSqVLuikoZ3idz3jIRk0UCKtVU2Lq5gaD6cLNTnJjNetoHKKdfZ023LVj4+Y9yWPz5meb+EJUsfBAGfF4Vw== + dependencies: + "@types/clone" "~0.1.30" + "@types/fast-json-stable-stringify" "^2.0.0" + array-flat-polyfill "^1.0.1" + clone "~2.1.2" + fast-deep-equal "~3.1.1" + fast-json-stable-stringify "~2.1.0" + json-stringify-pretty-compact "~2.0.0" + tslib "~2.0.0" + vega-event-selector "~2.0.3" + vega-expression "~2.6.5" + vega-util "~1.14.0" + yargs "~15.3.1" + +vega-loader@^4.3.0, vega-loader@~4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/vega-lib/-/vega-lib-4.3.0.tgz#b17bea4393a281db3e647e665b9e97aa793ccfd6" - integrity sha512-NNTpxpUAQfFUqkf6XnsL5nko9sCIQPMRe5AZI9Bsb8mHaA0oukAV1dPIb57MIYZg/wYwtrvc6EwhKxsjCQZHnA== - dependencies: - vega-crossfilter "^3.0.0" - vega-dataflow "^4.0.4" - vega-encode "^3.2.0" - vega-event-selector "^2.0.0" - vega-expression "^2.4.0" - vega-force "^3.0.0" - vega-geo "^3.1.0" - vega-hierarchy "^3.1.0" - vega-loader "^3.0.1" - vega-parser "^3.8.0" - vega-projection "^1.2.0" - vega-runtime "^3.1.1" - vega-scale "^2.5.0" - vega-scenegraph "^3.2.3" - vega-statistics "^1.2.2" - vega-transforms "^2.3.0" - vega-typings "*" - vega-util "^1.7.0" - vega-view "^3.4.0" - vega-view-transforms "^2.0.3" - vega-voronoi "^3.0.0" - vega-wordcloud "^3.0.0" - -vega-lite@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/vega-lite/-/vega-lite-2.6.0.tgz#ce79c2db0311b0b920afdf2cd7384556a334e2f0" - integrity sha512-m4TY1xVk2q9kmxpL0SR8WV8RTTsk7TRE9FXSiLuZ58TIuVjZ0CN2QCDg6rNYXtzURiokFOsqaFrzbxIQQLPsxg== + resolved "https://registry.yarnpkg.com/vega-loader/-/vega-loader-4.3.0.tgz#f32d70e2dd57d753e4160aae2e1f48c20e0d00e5" + integrity sha512-XrwwJ1xWnsVS2N2M4vdvzieUdXWegdD31t04sCPQ5C3US58NYlq1ho1Md+5FVrtl0uCd0wG/mk700Jp7yPhN+w== dependencies: - "@types/json-stable-stringify" "^1.0.32" - json-stable-stringify "^1.0.1" - tslib "^1.9.2" - vega-event-selector "^2.0.0" - vega-typings "^0.3.17" - vega-util "^1.7.0" - yargs "^11.0.0" + d3-dsv "^1.2.0" + node-fetch "^2.6.0" + topojson-client "^3.1.0" + vega-format "^1.0.0" + vega-util "^1.14.0" -vega-loader@^3.0.1, vega-loader@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/vega-loader/-/vega-loader-3.1.0.tgz#21caa0e78e158a676eafd0e7cb5bae4c18996c5a" - integrity sha512-FD9KJdPxBOa+fTnjC2dfY5+kB05hXyVOfjIkssmgyyhELJPp2FwclcF4mVy7Ay1E8fUHY3GgbwSE5jL8k4pYUg== +vega-parser@~6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/vega-parser/-/vega-parser-6.0.2.tgz#c76dfd13a38abedacb07658fe75fd0c05a509a1c" + integrity sha512-3337WvsUuuYZ0+H7ew4uZFgn82QWoaWv/9uinlMOH7ncnu8qTuWt4nV3WoUX9RFqie38qIMw/mf6+HK5gfXBoQ== dependencies: - d3-dsv "^1.0.10" - d3-time-format "^2.1.3" - node-fetch "^2.3.0" - topojson-client "^3.0.0" - vega-util "^1.7.0" + vega-dataflow "^5.6.0" + vega-event-selector "^2.0.3" + vega-functions "^5.7.0" + vega-scale "^7.0.0" + vega-util "^1.14.0" -vega-parser@^3.8.0, vega-parser@^3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/vega-parser/-/vega-parser-3.9.0.tgz#a7bbe380c5ae70ddd501163302a948f25aadd686" - integrity sha512-/fdPt5wcZgbPi0zwzJsBgi/k2GO3s53j7kJUYFGff75+wLJ2n/XtLCU295Wo7+cGCfkCZs0FfYKWa8AJrQZiag== - dependencies: - d3-array "^2.0.2" - d3-color "^1.2.3" - d3-format "^1.3.2" - d3-geo "^1.11.3" - d3-time-format "^2.1.3" - vega-dataflow "^4.1.0" - vega-event-selector "^2.0.0" - vega-expression "^2.4.0" - vega-scale "^2.5.1" - vega-scenegraph "^3.2.3" - vega-statistics "^1.2.3" - vega-util "^1.7.0" +vega-projection@^1.4.2, vega-projection@~1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/vega-projection/-/vega-projection-1.4.2.tgz#2e5edfffac54e8ba8ab56fba29f174dab0bc98d1" + integrity sha512-eULwc/8TMVjFkGtIVF5IGpJzEksnS0ccbaaCH9QjHtQTyBaR2CA679r5/98x6ur7ZLaYgcm2o082kjReUoyncA== + dependencies: + d3-geo "^1.12.1" + d3-geo-projection "^2.9.0" -vega-projection@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/vega-projection/-/vega-projection-1.2.0.tgz#812c955251dab495fda83d9406ba72d9833a2014" - integrity sha1-gSyVUlHatJX9qD2UBrpy2YM6IBQ= +vega-regression@~1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/vega-regression/-/vega-regression-1.0.6.tgz#0081a91328e933c826813c06afe7041915532d4f" + integrity sha512-s4kjsKp23WvDJDHkpIrGNUaLI3/95k6nTURj9RDtM4C6CbUgO2snIaEfki4JfOCnBYtvotwDuZgXKmJInu9hVw== dependencies: - d3-geo "^1.10.0" + d3-array "^2.4.0" + vega-dataflow "^5.5.1" + vega-statistics "^1.7.4" + vega-util "^1.13.2" -vega-runtime@^3.1.1, vega-runtime@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/vega-runtime/-/vega-runtime-3.2.0.tgz#ad4152079989058db90ce1993f16b3876f628d8b" - integrity sha512-aoWqH+U5tiByj3cIGZsTDPMTb10tUN2nm4zWa3Z7lOUilbw/+gEaOuy1qvr4VrVhUShsnytudED4OpQNUkKy3Q== +vega-runtime@^6.1.0, vega-runtime@~6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/vega-runtime/-/vega-runtime-6.1.0.tgz#7b3016e616bcea779f971e242065b03e8d3ea6b5" + integrity sha512-wKzymOtUsselAIJZbiC/88zVgeuhB1lHZTdPN7IrB2o1qgxF50DdDa7eNUpKrkFJ2DK6gCJ8JlqLtM3QVr3iXQ== dependencies: - vega-dataflow "^4.1.0" - vega-util "^1.7.0" + vega-dataflow "^5.7.0" + vega-util "^1.14.0" -vega-scale@^2.1.1, vega-scale@^2.5.0, vega-scale@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/vega-scale/-/vega-scale-2.5.1.tgz#5b5ce7752e904c17077db9a924418dabd6ffb991" - integrity sha512-EOpUDOjTAD7DhXglyOquXTzXFXjnNvrGyMDCOsfRL/XUTsbjYYNkdl0Q30c9fVN1I+H65lMz52xwN16yxwMuTw== - dependencies: - d3-array "^2.0.2" - d3-interpolate "^1.3.2" - d3-scale "^2.1.2" - d3-scale-chromatic "^1.3.3" - d3-time "^1.0.10" - vega-util "^1.7.0" +vega-scale@^7.0.0, vega-scale@~7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/vega-scale/-/vega-scale-7.0.0.tgz#0101802c6da29e2e903887160a269d57d6da479e" + integrity sha512-3oQAQYLRk+PIs6aF6kdb7tbhm5IpxNiwdFVM9fNS+SSsii6v8kFC681EuUMqLVZOHELiklWIE1rZIHaB5dNRXg== + dependencies: + d3-array "^2.4.0" + d3-interpolate "^1.4.0" + d3-scale "^3.2.1" + vega-time "^2.0.0" + vega-util "^1.14.0" + +vega-scenegraph@^4.8.0, vega-scenegraph@^4.8.3, vega-scenegraph@~4.8.3: + version "4.8.3" + resolved "https://registry.yarnpkg.com/vega-scenegraph/-/vega-scenegraph-4.8.3.tgz#11b557019175ec5ddc258c3a5eb7371e64cec5e1" + integrity sha512-2GznqXm/py7/XX9juohFbLYQTKxHY5VNRZLHc0bL35Nd7lShKeOlHY9uVkHw2FoLLCz78UcXFminWM8lddvGxw== + dependencies: + d3-path "^1.0.9" + d3-shape "^1.3.7" + vega-canvas "^1.2.2" + vega-loader "^4.3.0" + vega-scale "^7.0.0" + vega-util "^1.14.0" + +vega-schema-url-parser@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vega-schema-url-parser/-/vega-schema-url-parser-1.1.0.tgz#39168ec04e5468ce278a06c16ec0d126035a85b5" + integrity sha512-Tc85J2ofMZZOsxiqDM9sbvfsa+Vdo3GwNLjEEsPOsCDeYqsUHKAlc1IpbbhPLZ6jusyM9Lk0e1izF64GGklFDg== -vega-scenegraph@^3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/vega-scenegraph/-/vega-scenegraph-3.2.3.tgz#72060c7f3b0e4421c4317a2f7a9a901870920a25" - integrity sha512-L4mZ6LpEKvW5Q0c8gyqozGuoY5miJI4DiRipiAG0BQ6rB67tK+8qlaTfslX4tNBz88mu+CyVO9ZjNW/M4nBI3w== +vega-selections@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/vega-selections/-/vega-selections-5.1.1.tgz#5ea3931bbf7bc13f2ab7c5c5ebf39aed98e4c114" + integrity sha512-ztZUMfDicuIGJHZimSdVvMGzMvaa37ICzUHHvwxS51OhYv096dzKgoSypjx+tsmR7wnhY7ZL+iQgpT1/O29jlA== dependencies: - d3-path "^1.0.7" - d3-shape "^1.2.2" - vega-canvas "^1.1.0" - vega-loader "^3.0.1" - vega-util "^1.7.0" - -vega-schema-url-parser@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/vega-schema-url-parser/-/vega-schema-url-parser-1.0.0.tgz#fc17631e354280d663ed39e3fa8eddb62145402e" - integrity sha512-pIG9Ox6fwaveiAntC9FxjpIT2GlSP/CruJtJxrUk4OdV624p9OC5KHfA3AUWOcVv3GddRO6FaQaZ6N6k9cD1rA== + vega-expression "^2.6.4" + vega-util "^1.13.2" vega-spec-injector@^0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/vega-spec-injector/-/vega-spec-injector-0.0.2.tgz#f1d990109dd9d845c524738f818baa4b72a60ca6" integrity sha512-wOMMqmpssn0/ZFPW7wl1v26vbseRX7zHPWzEyS9TwNXTRCu1TcjIBIR+X23lCWocxhoBqFxmqyn8UowMhlGtAg== -vega-statistics@^1.2.1, vega-statistics@^1.2.2, vega-statistics@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/vega-statistics/-/vega-statistics-1.2.3.tgz#4a7ca4c5fd9cc00c3700cb9cde336995439a55fa" - integrity sha512-Acs6kswwO7lJYiG7KXxSI+Hr+PDKF+EMX/KmCOmyn8JB1W9fWIhTMBCNKf27r8XuAboBd0fQixuIx9L1Scdo+g== +vega-statistics@^1.7.4, vega-statistics@^1.7.5, vega-statistics@~1.7.6: + version "1.7.6" + resolved "https://registry.yarnpkg.com/vega-statistics/-/vega-statistics-1.7.6.tgz#6f920c83adbec9a25087ca60c41af272838f85bd" + integrity sha512-w6z5t4p1zYNSvsg3lln4TZakxXlH/tM0w5WAP1EXLYrCYRw0F/SvxqLQ+WqEZVnI/WGQDq2v5xMAn0WvHJ/kUg== + dependencies: + d3-array "^2.4.0" + +vega-time@^2.0.0, vega-time@^2.0.1, vega-time@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/vega-time/-/vega-time-2.0.1.tgz#2a91c3acafd091e6724063dea26e4b3fe6061d2f" + integrity sha512-Ij0gmABKDRKAMUTh/1AGSSkU6ocWiteLkIK/cmcnt98u8LiuVcFT5w7gusd0+ibO9EooeMKazn5xPmjvQs0qEg== dependencies: - d3-array "^2.0.2" + d3-array "^2.4.0" + d3-time "^1.1.0" + vega-util "^1.14.0" vega-tooltip@^0.12.0: version "0.12.0" @@ -31821,69 +31779,103 @@ vega-tooltip@^0.12.0: dependencies: vega-util "^1.7.0" -vega-transforms@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/vega-transforms/-/vega-transforms-2.3.1.tgz#a31a1ff8086c6909384ddfcc973bd58d53d801ae" - integrity sha512-jvDz33ohZiP6cN74quEvesHr0sbSMMQ69ZZqgL6cRDHBqfiuHPhZofBKWDXE1nEWDmJqTEyvg0gsnA8vpHzpjQ== +vega-transforms@~4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/vega-transforms/-/vega-transforms-4.9.0.tgz#272be37adb8565633ccde68f64079fb5029a8ac9" + integrity sha512-xsgvkHsyKgEWdCB86DVts2Zu6fJ+cGjpc56MpcCWPArNuhcUSugivIoTAFAh8w7QempQBsAtnPrnbaytMYOJ8w== dependencies: - d3-array "^2.0.2" - vega-dataflow "^4.1.0" - vega-statistics "^1.2.3" - vega-util "^1.7.0" + d3-array "^2.4.0" + vega-dataflow "^5.7.0" + vega-statistics "^1.7.5" + vega-time "^2.0.1" + vega-util "^1.14.0" -vega-typings@*, vega-typings@^0.3.17: - version "0.3.53" - resolved "https://registry.yarnpkg.com/vega-typings/-/vega-typings-0.3.53.tgz#a70b9730ebe1e4c557019ccccc5fd98035b0aab0" - integrity sha512-XQRd66eL62ll6tHENQIJHtdwXemqXoB4KnVVbGUwGJIHjQkHHluCbkoWVRvPYuRd+OLM1RXVc+EBxA015hJ1SQ== +vega-typings@~0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/vega-typings/-/vega-typings-0.18.0.tgz#c8f66406cae8418eafed6fbc4c7c63f88eee670d" + integrity sha512-uMSS7EEP8Q2gg4dN7D2xhi4S+dp/IQGTQp3VgieJx8ki8mrm0N43pdUC14nNYTtiUDxDdmup5nyj6JHuboKUmg== dependencies: - vega-util "^1.7.0" + vega-util "^1.14.0" -vega-util@^1.7.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/vega-util/-/vega-util-1.13.1.tgz#3eae51043184c6b873c17b148755c21b01274a0e" - integrity sha512-TmvZSMKqhGlS7eAXphqJUhq+NZVYbvXX2ahargTRkVckGWjEUpWhMC7T13vYihrU2Lf/OevKbrruSXKOBxke2w== +vega-util@^1.13.2, vega-util@^1.14.0, vega-util@^1.7.0, vega-util@~1.14.0, vega-util@~1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/vega-util/-/vega-util-1.14.1.tgz#0fb614277764f98738ba0b80e5cdfbe663426183" + integrity sha512-pSKJ8OCkgfgHZDTljyj+gmGltgulceWbk1BV6LWrXqp6P3J8qPA/oZA8+a93YNApYxXZ3yzIVUDOo5O27xk0jw== -vega-view-transforms@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/vega-view-transforms/-/vega-view-transforms-2.0.3.tgz#9999f83301efbe65ed1971018f538f5aeb62a16e" - integrity sha512-m42sP2G72KIIEhbno5P3wYXuGe4C5fj0ztfg1TrSEmGsIHOqoehRvte/1e9q/dV+1rB3TqfcWXgQVEDHCFLEvQ== +vega-view-transforms@~4.5.5: + version "4.5.5" + resolved "https://registry.yarnpkg.com/vega-view-transforms/-/vega-view-transforms-4.5.5.tgz#b934da0fc1b41c10039300c036cfa38a56c83631" + integrity sha512-HFTA6j2zFKRlfBlS6b9tmLLDNt7g78ZoyKFAT9fCm3X0KLT6FTn13PiiB4KppMg40nwgm0c2KUQmjnC6fGgIdQ== dependencies: - vega-dataflow "^4.0.4" - vega-scenegraph "^3.2.3" - vega-util "^1.7.0" - -vega-view@^3.4.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/vega-view/-/vega-view-3.4.1.tgz#8f36fea88792b3b1ee3a535c5322dc7ecd975532" - integrity sha512-hT9Bj9qRCGz+4umid8tFuADyUF7xOHTQmeu18XtRgEkNOtTALlDYLmCSpcGkP1N6eeZm3aRWBtkUz/XE7/6d+Q== - dependencies: - d3-array "^2.0.2" - d3-timer "^1.0.9" - vega-dataflow "^4.1.0" - vega-parser "^3.9.0" - vega-runtime "^3.2.0" - vega-scenegraph "^3.2.3" - vega-util "^1.7.0" + vega-dataflow "^5.6.0" + vega-scenegraph "^4.8.0" + vega-util "^1.14.0" -vega-voronoi@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/vega-voronoi/-/vega-voronoi-3.0.0.tgz#e83d014c0d8d083592d5246122e3a9d4af0ce434" - integrity sha512-ZkQw4UprxqiS3IjrdLOoQq1oEeH0REqWonf7Wz5zt2pKDHyMPlFX89EueoDYOKnfQjk9/7IiptBDK1ruAbDNiQ== +vega-view@~5.8.0: + version "5.8.0" + resolved "https://registry.yarnpkg.com/vega-view/-/vega-view-5.8.0.tgz#64ff5acd32855c16b4799cdb4b657d9f3e15b7f9" + integrity sha512-0H+m0olEYpadUq+3z35t6g6ozbr9AzpGqg8KOklHpe+rSmmaaItEF8B+eGr3Ut5i/+u0c7PKa6jEdVG61xbpGA== + dependencies: + d3-array "^2.4.0" + d3-timer "^1.0.10" + vega-dataflow "^5.7.0" + vega-format "^1.0.1" + vega-functions "^5.7.1" + vega-runtime "^6.1.0" + vega-scenegraph "^4.8.3" + vega-util "^1.14.0" + +vega-voronoi@~4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/vega-voronoi/-/vega-voronoi-4.1.2.tgz#459b78f5191fb707e94d9afa7d8c1a68ad9aec7a" + integrity sha512-XXp2UChi4/6jkEqWkLFbjDBVLMizQICWDv4RUkfMeDNhWmhEY/3kPHCU6taqfTVkbxfA7aN20ivbakJzoywiAQ== dependencies: - d3-voronoi "^1.1.2" - vega-dataflow "^4.0.0" - vega-util "^1.7.0" + d3-delaunay "^5.2.1" + vega-dataflow "^5.5.1" + vega-util "^1.13.2" -vega-wordcloud@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/vega-wordcloud/-/vega-wordcloud-3.0.0.tgz#3843d5233673a36a93f78c849d3c7568c1cdc2ce" - integrity sha512-/2F09L2tNTQ8aqK/ZLjd7m+fYwJR8/waE8YWuexLZob4+4BEByzqFfRMATE39ZpdTHOreCEQ5uUKyvv0qA6O0A== +vega-wordcloud@~4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/vega-wordcloud/-/vega-wordcloud-4.1.0.tgz#edf020a99378eb88109cb9ac4702fec0e0ac8cdd" + integrity sha512-WiISiNlHdbTL6QsnxyzxbniUgcPmjzwdwZzu6clQSHXNRz9kThCPhXOyLwYdbFV+9sjd4sJlW0YOaCcx7wMT2Q== dependencies: - vega-canvas "^1.0.1" - vega-dataflow "^4.0.0" - vega-scale "^2.1.1" - vega-statistics "^1.2.1" - vega-util "^1.7.0" + vega-canvas "^1.2.2" + vega-dataflow "^5.6.0" + vega-scale "^7.0.0" + vega-statistics "^1.7.5" + vega-util "^1.14.0" + +vega@^5.13.0: + version "5.13.0" + resolved "https://registry.yarnpkg.com/vega/-/vega-5.13.0.tgz#12253667d203e8f82fe317014084c55b479057ee" + integrity sha512-3X6ptCqQrfYg1xdy6rCkywKXOiIQIIJBFESV5JJOXaqm1ye9LRD3NOmZukqenLJXlg6aaPbP6kFFqpjBzmAefg== + dependencies: + vega-crossfilter "~4.0.2" + vega-dataflow "~5.7.0" + vega-encode "~4.8.0" + vega-event-selector "~2.0.3" + vega-expression "~2.6.5" + vega-force "~4.0.4" + vega-format "~1.0.1" + vega-functions "~5.7.1" + vega-geo "~4.3.4" + vega-hierarchy "~4.0.6" + vega-loader "~4.3.0" + vega-parser "~6.0.2" + vega-projection "~1.4.2" + vega-regression "~1.0.6" + vega-runtime "~6.1.0" + vega-scale "~7.0.0" + vega-scenegraph "~4.8.3" + vega-statistics "~1.7.6" + vega-time "~2.0.1" + vega-transforms "~4.9.0" + vega-typings "~0.18.0" + vega-util "~1.14.1" + vega-view "~5.8.0" + vega-view-transforms "~4.5.5" + vega-voronoi "~4.1.2" + vega-wordcloud "~4.1.0" venn.js@0.2.20: version "0.2.20" @@ -32987,7 +32979,7 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yargs-parser@13.1.2, yargs-parser@^13.1.0, yargs-parser@^13.1.1, yargs-parser@^13.1.2: +yargs-parser@13.1.2, yargs-parser@^13.1.0, yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== @@ -32995,6 +32987,14 @@ yargs-parser@13.1.2, yargs-parser@^13.1.0, yargs-parser@^13.1.1, yargs-parser@^1 camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@5.0.0-security.0: + version "5.0.0-security.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz#4ff7271d25f90ac15643b86076a2ab499ec9ee24" + integrity sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ== + dependencies: + camelcase "^3.0.0" + object.assign "^4.1.0" + yargs-parser@^10.0.0: version "10.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" @@ -33011,9 +33011,9 @@ yargs-parser@^11.1.1: decamelize "^1.2.0" yargs-parser@^18.1.1: - version "18.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.1.tgz#bf7407b915427fc760fcbbccc6c82b4f0ffcbd37" - integrity sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA== + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" @@ -33026,20 +33026,6 @@ yargs-parser@^2.4.1: camelcase "^3.0.0" lodash.assign "^4.0.6" -yargs-parser@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" - integrity sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo= - dependencies: - camelcase "^3.0.0" - -yargs-parser@^9.0.2: - version "9.0.2" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" - integrity sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc= - dependencies: - camelcase "^4.1.0" - yargs-unparser@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" @@ -33084,7 +33070,7 @@ yargs@13.2.4: y18n "^4.0.0" yargs-parser "^13.1.0" -yargs@13.3.2: +yargs@13.3.2, yargs@^13.2.2, yargs@^13.3.0: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== @@ -33120,41 +33106,7 @@ yargs@4.8.1: y18n "^3.2.1" yargs-parser "^2.4.1" -yargs@^11.0.0: - version "11.1.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.1.tgz#5052efe3446a4df5ed669c995886cc0f13702766" - integrity sha512-PRU7gJrJaXv3q3yQZ/+/X6KBswZiaQ+zOmdprZcouPYtQgvNU35i+68M4b1ZHLZtYFT5QObFLV+ZkmJYcwKdiw== - dependencies: - cliui "^4.0.0" - decamelize "^1.1.1" - find-up "^2.1.0" - get-caller-file "^1.0.1" - os-locale "^3.1.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^9.0.2" - -yargs@^13.2.2, yargs@^13.3.0: - version "13.3.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" - integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== - dependencies: - cliui "^5.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.1" - -yargs@^15.0.2, yargs@^15.1.0, yargs@^15.3.1: +yargs@^15.0.2, yargs@^15.1.0, yargs@^15.3.1, yargs@~15.3.1: version "15.3.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA== @@ -33185,9 +33137,9 @@ yargs@^3.15.0: y18n "^3.2.0" yargs@^7.0.0, yargs@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" - integrity sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg= + version "7.1.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.1.tgz#67f0ef52e228d4ee0d6311acede8850f53464df6" + integrity sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g== dependencies: camelcase "^3.0.0" cliui "^3.2.0" @@ -33201,7 +33153,7 @@ yargs@^7.0.0, yargs@^7.1.0: string-width "^1.0.2" which-module "^1.0.0" y18n "^3.2.1" - yargs-parser "^5.0.0" + yargs-parser "5.0.0-security.0" yargs@~3.10.0: version "3.10.0"