From ebe46c0580bf397dad6e8e8abc72aca1875f76cc Mon Sep 17 00:00:00 2001 From: Sonja Krause-Harder Date: Thu, 6 Aug 2020 21:30:24 +0200 Subject: [PATCH 01/19] Make test less brittle when registry is changed. (#74554) --- x-pack/test/ingest_manager_api_integration/apis/epm/list.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/ingest_manager_api_integration/apis/epm/list.ts b/x-pack/test/ingest_manager_api_integration/apis/epm/list.ts index 0b6a37d77387e..bfe1954e46c9f 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/epm/list.ts +++ b/x-pack/test/ingest_manager_api_integration/apis/epm/list.ts @@ -29,7 +29,7 @@ export default function ({ getService }: FtrProviderContext) { return response.body; }; const listResponse = await fetchPackageList(); - expect(listResponse.response.length).to.be(8); + expect(listResponse.response.length).not.to.be(0); } else { warnAndSkipTest(this, log); } From e807ddd1c1f8edcb0708fe2f365c511557986435 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Thu, 6 Aug 2020 15:33:24 -0400 Subject: [PATCH 02/19] [Monitoring] Handle getClient call throwing an exception (#74550) * This call can throw an exception in dist builds with ssl disabled * Fix typo --- .../lib/cluster/get_clusters_from_request.js | 106 +++++++++--------- x-pack/plugins/monitoring/server/plugin.ts | 18 ++- 2 files changed, 70 insertions(+), 54 deletions(-) diff --git a/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js index 18db738bba38e..16d42d896ca11 100644 --- a/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js +++ b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js @@ -119,65 +119,67 @@ export async function getClustersFromRequest( // add alerts data if (isInCodePath(codePaths, [CODE_PATH_ALERTS])) { const alertsClient = req.getAlertsClient(); - for (const cluster of clusters) { - const verification = verifyMonitoringLicense(req.server); - if (!verification.enabled) { - // return metadata detailing that alerts is disabled because of the monitoring cluster license - cluster.alerts = { - alertsMeta: { - enabled: verification.enabled, - message: verification.message, // NOTE: this is only defined when the alert feature is disabled - }, - list: {}, - }; - continue; - } + if (alertsClient) { + for (const cluster of clusters) { + const verification = verifyMonitoringLicense(req.server); + if (!verification.enabled) { + // return metadata detailing that alerts is disabled because of the monitoring cluster license + cluster.alerts = { + alertsMeta: { + enabled: verification.enabled, + message: verification.message, // NOTE: this is only defined when the alert feature is disabled + }, + list: {}, + }; + continue; + } + + // check the license type of the production cluster for alerts feature support + const license = cluster.license || {}; + const prodLicenseInfo = checkLicenseForAlerts( + license.type, + license.status === 'active', + 'production' + ); + if (prodLicenseInfo.clusterAlerts.enabled) { + cluster.alerts = { + list: await fetchStatus( + alertsClient, + req.server.plugins.monitoring.info, + undefined, + cluster.cluster_uuid, + start, + end, + [] + ), + alertsMeta: { + enabled: true, + }, + }; + continue; + } - // check the license type of the production cluster for alerts feature support - const license = cluster.license || {}; - const prodLicenseInfo = checkLicenseForAlerts( - license.type, - license.status === 'active', - 'production' - ); - if (prodLicenseInfo.clusterAlerts.enabled) { cluster.alerts = { - list: await fetchStatus( - alertsClient, - req.server.plugins.monitoring.info, - undefined, - cluster.cluster_uuid, - start, - end, - [] - ), + list: {}, alertsMeta: { enabled: true, }, + clusterMeta: { + enabled: false, + message: i18n.translate( + 'xpack.monitoring.clusterAlerts.unsupportedClusterAlertsDescription', + { + defaultMessage: + 'Cluster [{clusterName}] license type [{licenseType}] does not support Cluster Alerts', + values: { + clusterName: cluster.cluster_name, + licenseType: `${license.type}`, + }, + } + ), + }, }; - continue; } - - cluster.alerts = { - list: {}, - alertsMeta: { - enabled: true, - }, - clusterMeta: { - enabled: false, - message: i18n.translate( - 'xpack.monitoring.clusterAlerts.unsupportedClusterAlertsDescription', - { - defaultMessage: - 'Cluster [{clusterName}] license type [{licenseType}] does not support Cluster Alerts', - values: { - clusterName: cluster.cluster_name, - licenseType: `${license.type}`, - }, - } - ), - }, - }; } } } diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts index ed091d4b8d7a7..3aedb6831e7ab 100644 --- a/x-pack/plugins/monitoring/server/plugin.ts +++ b/x-pack/plugins/monitoring/server/plugin.ts @@ -325,8 +325,22 @@ export class Plugin { getKibanaStatsCollector: () => this.legacyShimDependencies.kibanaStatsCollector, getUiSettingsService: () => context.core.uiSettings.client, getActionTypeRegistry: () => context.actions?.listTypes(), - getAlertsClient: () => plugins.alerts.getAlertsClientWithRequest(req), - getActionsClient: () => plugins.actions.getActionsClientWithRequest(req), + getAlertsClient: () => { + try { + return plugins.alerts.getAlertsClientWithRequest(req); + } catch (err) { + // If security is disabled, this call will throw an error unless a certain config is set for dist builds + return null; + } + }, + getActionsClient: () => { + try { + return plugins.actions.getActionsClientWithRequest(req); + } catch (err) { + // If security is disabled, this call will throw an error unless a certain config is set for dist builds + return null; + } + }, server: { config: legacyConfigWrapper, newPlatform: { From 47fdd59e1c168a820260e2bd28a71843e4a0f3c1 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Thu, 6 Aug 2020 23:00:40 +0300 Subject: [PATCH 03/19] bump babel deps to support TS v4 (#74495) --- package.json | 10 +- packages/elastic-datemath/package.json | 4 +- packages/kbn-analytics/package.json | 2 +- packages/kbn-babel-preset/package.json | 16 +- packages/kbn-i18n/package.json | 4 +- packages/kbn-interpreter/package.json | 10 +- packages/kbn-optimizer/package.json | 2 +- packages/kbn-plugin-helpers/package.json | 2 +- packages/kbn-pm/package.json | 10 +- packages/kbn-test/package.json | 2 +- packages/kbn-ui-framework/package.json | 2 +- x-pack/package.json | 6 +- yarn.lock | 1289 +++++++++++----------- 13 files changed, 696 insertions(+), 663 deletions(-) diff --git a/package.json b/package.json index fa34fc3d0936a..fc3af14ecae09 100644 --- a/package.json +++ b/package.json @@ -117,9 +117,9 @@ ] }, "dependencies": { - "@babel/core": "^7.10.2", - "@babel/plugin-transform-modules-commonjs": "^7.10.1", - "@babel/register": "^7.10.1", + "@babel/core": "^7.11.1", + "@babel/plugin-transform-modules-commonjs": "^7.10.4", + "@babel/register": "^7.10.5", "@elastic/apm-rum": "^5.2.0", "@elastic/charts": "19.8.1", "@elastic/datemath": "5.0.3", @@ -289,8 +289,8 @@ "yauzl": "2.10.0" }, "devDependencies": { - "@babel/parser": "^7.10.2", - "@babel/types": "^7.10.2", + "@babel/parser": "^7.11.2", + "@babel/types": "^7.11.0", "@elastic/eslint-config-kibana": "0.15.0", "@elastic/eslint-plugin-eui": "0.0.2", "@elastic/github-checks-reporter": "0.0.20b3", diff --git a/packages/elastic-datemath/package.json b/packages/elastic-datemath/package.json index 15040a6243ff2..ad4190f981439 100644 --- a/packages/elastic-datemath/package.json +++ b/packages/elastic-datemath/package.json @@ -11,8 +11,8 @@ "kbn:watch": "yarn build --watch" }, "devDependencies": { - "@babel/cli": "^7.10.1", - "@babel/preset-env": "^7.10.2", + "@babel/cli": "^7.10.5", + "@babel/preset-env": "^7.11.0", "babel-plugin-add-module-exports": "^1.0.2", "moment": "^2.24.0" }, diff --git a/packages/kbn-analytics/package.json b/packages/kbn-analytics/package.json index bd3f5832b7140..873252ceb0a1a 100644 --- a/packages/kbn-analytics/package.json +++ b/packages/kbn-analytics/package.json @@ -14,7 +14,7 @@ "kbn:watch": "node scripts/build --source-maps --watch" }, "devDependencies": { - "@babel/cli": "^7.10.1", + "@babel/cli": "^7.10.5", "@kbn/dev-utils": "1.0.0", "@kbn/babel-preset": "1.0.0", "typescript": "3.9.5" diff --git a/packages/kbn-babel-preset/package.json b/packages/kbn-babel-preset/package.json index 83530beffd2b2..db1f2161b6e38 100644 --- a/packages/kbn-babel-preset/package.json +++ b/packages/kbn-babel-preset/package.json @@ -4,14 +4,14 @@ "version": "1.0.0", "license": "Apache-2.0", "dependencies": { - "@babel/plugin-proposal-class-properties": "^7.10.1", - "@babel/plugin-proposal-export-namespace-from": "^7.10.1", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.1", - "@babel/plugin-proposal-optional-chaining": "^7.10.1", - "@babel/plugin-proposal-private-methods": "^7.10.1", - "@babel/preset-env": "^7.10.2", - "@babel/preset-react": "^7.10.1", - "@babel/preset-typescript": "^7.10.1", + "@babel/plugin-proposal-class-properties": "^7.10.4", + "@babel/plugin-proposal-export-namespace-from": "^7.10.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", + "@babel/plugin-proposal-optional-chaining": "^7.11.0", + "@babel/plugin-proposal-private-methods": "^7.10.4", + "@babel/preset-env": "^7.11.0", + "@babel/preset-react": "^7.10.4", + "@babel/preset-typescript": "^7.10.4", "babel-plugin-add-module-exports": "^1.0.2", "babel-plugin-filter-imports": "^3.0.0", "babel-plugin-styled-components": "^1.10.7", diff --git a/packages/kbn-i18n/package.json b/packages/kbn-i18n/package.json index c5da144688c3c..0f830acb284a0 100644 --- a/packages/kbn-i18n/package.json +++ b/packages/kbn-i18n/package.json @@ -12,8 +12,8 @@ "kbn:watch": "node scripts/build --watch --source-maps" }, "devDependencies": { - "@babel/cli": "^7.10.1", - "@babel/core": "^7.10.2", + "@babel/cli": "^7.10.5", + "@babel/core": "^7.11.1", "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@types/intl-relativeformat": "^2.1.0", diff --git a/packages/kbn-interpreter/package.json b/packages/kbn-interpreter/package.json index c6bb06e68b9c0..aef63229ebe96 100644 --- a/packages/kbn-interpreter/package.json +++ b/packages/kbn-interpreter/package.json @@ -9,16 +9,16 @@ "kbn:watch": "node scripts/build --dev --watch" }, "dependencies": { - "@babel/runtime": "^7.10.2", + "@babel/runtime": "^7.11.2", "@kbn/i18n": "1.0.0", "lodash": "^4.17.15", "uuid": "3.3.2" }, "devDependencies": { - "@babel/cli": "^7.10.1", - "@babel/core": "^7.10.2", - "@babel/plugin-transform-modules-commonjs": "^7.10.1", - "@babel/plugin-transform-runtime": "^7.10.1", + "@babel/cli": "^7.10.5", + "@babel/core": "^7.11.1", + "@babel/plugin-transform-modules-commonjs": "^7.10.4", + "@babel/plugin-transform-runtime": "^7.11.0", "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "babel-loader": "^8.0.6", diff --git a/packages/kbn-optimizer/package.json b/packages/kbn-optimizer/package.json index e6eb5de31abd8..84e5c79e2e358 100644 --- a/packages/kbn-optimizer/package.json +++ b/packages/kbn-optimizer/package.json @@ -10,7 +10,7 @@ "kbn:watch": "yarn build --watch" }, "dependencies": { - "@babel/cli": "^7.10.1", + "@babel/cli": "^7.10.5", "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@kbn/ui-shared-deps": "1.0.0", diff --git a/packages/kbn-plugin-helpers/package.json b/packages/kbn-plugin-helpers/package.json index f370265876df3..45582ad2af97a 100644 --- a/packages/kbn-plugin-helpers/package.json +++ b/packages/kbn-plugin-helpers/package.json @@ -12,7 +12,7 @@ "plugin-helpers": "bin/plugin-helpers.js" }, "dependencies": { - "@babel/core": "^7.10.2", + "@babel/core": "^7.11.1", "argv-split": "^2.0.1", "commander": "^3.0.0", "del": "^5.1.0", diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index 188db0a8321a2..3e40bf40222e6 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -10,11 +10,11 @@ "prettier": "prettier --write './src/**/*.ts'" }, "devDependencies": { - "@babel/core": "^7.10.2", - "@babel/plugin-proposal-class-properties": "^7.10.1", - "@babel/plugin-proposal-object-rest-spread": "^7.10.1", - "@babel/preset-env": "^7.10.2", - "@babel/preset-typescript": "^7.10.1", + "@babel/core": "^7.11.1", + "@babel/plugin-proposal-class-properties": "^7.10.4", + "@babel/plugin-proposal-object-rest-spread": "^7.11.0", + "@babel/preset-env": "^7.11.0", + "@babel/preset-typescript": "^7.10.4", "@types/cmd-shim": "^2.0.0", "@types/cpy": "^5.1.0", "@types/dedent": "^0.7.0", diff --git a/packages/kbn-test/package.json b/packages/kbn-test/package.json index 38e4668fc1e42..9482ea83cc257 100644 --- a/packages/kbn-test/package.json +++ b/packages/kbn-test/package.json @@ -10,7 +10,7 @@ "kbn:watch": "yarn build --watch" }, "devDependencies": { - "@babel/cli": "^7.10.1", + "@babel/cli": "^7.10.5", "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@types/joi": "^13.4.2", diff --git a/packages/kbn-ui-framework/package.json b/packages/kbn-ui-framework/package.json index 7933ce06d6847..a095d9ac2a77f 100644 --- a/packages/kbn-ui-framework/package.json +++ b/packages/kbn-ui-framework/package.json @@ -30,7 +30,7 @@ "enzyme-adapter-react-16": "^1.9.1" }, "devDependencies": { - "@babel/core": "^7.10.2", + "@babel/core": "^7.11.1", "@elastic/eui": "0.0.55", "@kbn/babel-preset": "1.0.0", "@kbn/optimizer": "1.0.0", diff --git a/x-pack/package.json b/x-pack/package.json index dcba01a771fd5..8fbb94c97c143 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -199,9 +199,9 @@ "yargs": "4.8.1" }, "dependencies": { - "@babel/core": "^7.10.2", - "@babel/register": "^7.10.1", - "@babel/runtime": "^7.10.2", + "@babel/core": "^7.11.1", + "@babel/register": "^7.10.5", + "@babel/runtime": "^7.11.2", "@elastic/apm-rum-react": "^1.1.2", "@elastic/datemath": "5.0.3", "@elastic/ems-client": "7.9.3", diff --git a/yarn.lock b/yarn.lock index f17418f07c5cc..33083667a3c5e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,16 +2,16 @@ # yarn lockfile v1 -"@babel/cli@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.10.1.tgz#b6e5cd43a17b8f639442ab027976408ebe6d79a0" - integrity sha512-cVB+dXeGhMOqViIaZs3A9OUAe4pKw4SBNdMw6yHJMYR7s4TB+Cei7ThquV/84O19PdIFWuwe03vxxES0BHUm5g== +"@babel/cli@^7.10.5": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.10.5.tgz#57df2987c8cf89d0fc7d4b157ec59d7619f1b77a" + integrity sha512-j9H9qSf3kLdM0Ao3aGPbGZ73mEA9XazuupcS6cDGWuiyAcANoguhP0r2Lx32H5JGw4sSSoHG3x/mxVnHgvOoyA== dependencies: commander "^4.0.1" convert-source-map "^1.1.0" fs-readdir-recursive "^1.1.0" glob "^7.0.0" - lodash "^4.17.13" + lodash "^4.17.19" make-dir "^2.1.0" slash "^2.0.0" source-map "^0.5.0" @@ -32,17 +32,17 @@ dependencies: "@babel/highlight" "^7.8.3" -"@babel/code-frame@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.1.tgz#d5481c5095daa1c57e16e54c6f9198443afb49ff" - integrity sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw== +"@babel/code-frame@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== dependencies: - "@babel/highlight" "^7.10.1" + "@babel/highlight" "^7.10.4" -"@babel/compat-data@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.10.1.tgz#b1085ffe72cd17bf2c0ee790fc09f9626011b2db" - integrity sha512-CHvCj7So7iCkGKPRFUfryXIkU2gSBw7VSZFYLsqVhrS47269VK2Hfi9S/YcublPMW8k1u2bQBlbDruoQEm4fgw== +"@babel/compat-data@^7.10.4", "@babel/compat-data@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.11.0.tgz#e9f73efe09af1355b723a7f39b11bad637d7c99c" + integrity sha512-TPSvJfv73ng0pfnEOh17bYMPQbI95+nGWc71Ss4vZdRBHTDqmM9Z8ZV4rYz8Ks7sfzc95n30k6ODIq5UGnXcYQ== dependencies: browserslist "^4.12.0" invariant "^2.2.4" @@ -79,24 +79,24 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.10.2": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.10.2.tgz#bd6786046668a925ac2bd2fd95b579b92a23b36a" - integrity sha512-KQmV9yguEjQsXqyOUGKjS4+3K8/DlOCE2pZcq4augdQmtTy5iv5EHtmMSJ7V4c1BIPjuwtZYqYLCq9Ga+hGBRQ== - dependencies: - "@babel/code-frame" "^7.10.1" - "@babel/generator" "^7.10.2" - "@babel/helper-module-transforms" "^7.10.1" - "@babel/helpers" "^7.10.1" - "@babel/parser" "^7.10.2" - "@babel/template" "^7.10.1" - "@babel/traverse" "^7.10.1" - "@babel/types" "^7.10.2" +"@babel/core@^7.11.1": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.1.tgz#2c55b604e73a40dc21b0e52650b11c65cf276643" + integrity sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.0" + "@babel/helper-module-transforms" "^7.11.0" + "@babel/helpers" "^7.10.4" + "@babel/parser" "^7.11.1" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.11.0" + "@babel/types" "^7.11.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.1" json5 "^2.1.2" - lodash "^4.17.13" + lodash "^4.17.19" resolve "^1.3.2" semver "^5.4.1" source-map "^0.5.0" @@ -111,14 +111,13 @@ lodash "^4.17.13" source-map "^0.5.0" -"@babel/generator@^7.10.1", "@babel/generator@^7.10.2": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.2.tgz#0fa5b5b2389db8bfdfcc3492b551ee20f5dd69a9" - integrity sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA== +"@babel/generator@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.0.tgz#4b90c78d8c12825024568cbe83ee6c9af193585c" + integrity sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ== dependencies: - "@babel/types" "^7.10.2" + "@babel/types" "^7.11.0" jsesc "^2.5.1" - lodash "^4.17.13" source-map "^0.5.0" "@babel/generator@^7.9.5": @@ -138,20 +137,20 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-annotate-as-pure@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.1.tgz#f6d08acc6f70bbd59b436262553fb2e259a1a268" - integrity sha512-ewp3rvJEwLaHgyWGe4wQssC2vjks3E80WiUe2BpMb0KhreTjMROCbxXcEovTrbeGVdQct5VjQfrv9EgC+xMzCw== +"@babel/helper-annotate-as-pure@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3" + integrity sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA== dependencies: - "@babel/types" "^7.10.1" + "@babel/types" "^7.10.4" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.1.tgz#0ec7d9be8174934532661f87783eb18d72290059" - integrity sha512-cQpVq48EkYxUU0xozpGCLla3wlkdRRqLWu1ksFMXA9CM5KQmyyRpSEsYXbao7JUkOw/tAaYKCaYyZq6HOFYtyw== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz#bb0b75f31bf98cbf9ff143c1ae578b87274ae1a3" + integrity sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg== dependencies: - "@babel/helper-explode-assignable-expression" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/helper-explode-assignable-expression" "^7.10.4" + "@babel/types" "^7.10.4" "@babel/helper-builder-binary-assignment-operator-visitor@^7.8.3": version "7.8.3" @@ -161,14 +160,14 @@ "@babel/helper-explode-assignable-expression" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/helper-builder-react-jsx-experimental@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.10.1.tgz#9a7d58ad184d3ac3bafb1a452cec2bad7e4a0bc8" - integrity sha512-irQJ8kpQUV3JasXPSFQ+LCCtJSc5ceZrPFVj6TElR6XCHssi3jV8ch3odIrNtjJFRZZVbrOEfJMI79TPU/h1pQ== +"@babel/helper-builder-react-jsx-experimental@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.10.5.tgz#f35e956a19955ff08c1258e44a515a6d6248646b" + integrity sha512-Buewnx6M4ttG+NLkKyt7baQn7ScC/Td+e99G914fRU8fGIUivDDgVIQeDHFa5e4CRSJQt58WpNHhsAZgtzVhsg== dependencies: - "@babel/helper-annotate-as-pure" "^7.10.1" - "@babel/helper-module-imports" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-module-imports" "^7.10.4" + "@babel/types" "^7.10.5" "@babel/helper-builder-react-jsx-experimental@^7.9.0": version "7.9.0" @@ -179,13 +178,13 @@ "@babel/helper-module-imports" "^7.8.3" "@babel/types" "^7.9.0" -"@babel/helper-builder-react-jsx@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.1.tgz#a327f0cf983af5554701b1215de54a019f09b532" - integrity sha512-KXzzpyWhXgzjXIlJU1ZjIXzUPdej1suE6vzqgImZ/cpAsR/CC8gUcX4EWRmDfWz/cs6HOCPMBIJ3nKoXt3BFuw== +"@babel/helper-builder-react-jsx@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.4.tgz#8095cddbff858e6fa9c326daee54a2f2732c1d5d" + integrity sha512-5nPcIZ7+KKDxT1427oBivl9V9YTal7qk0diccnh7RrcgrT/pGFOjgGw1dgryyx1GvHEpXVfoDF6Ak3rTiWh8Rg== dependencies: - "@babel/helper-annotate-as-pure" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/types" "^7.10.4" "@babel/helper-builder-react-jsx@^7.9.0": version "7.9.0" @@ -195,12 +194,12 @@ "@babel/helper-annotate-as-pure" "^7.8.3" "@babel/types" "^7.9.0" -"@babel/helper-compilation-targets@^7.10.2": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.2.tgz#a17d9723b6e2c750299d2a14d4637c76936d8285" - integrity sha512-hYgOhF4To2UTB4LTaZepN/4Pl9LD4gfbJx8A34mqoluT8TLbof1mhUlYuNWTEebONa8+UlCC4X0TEXu7AOUyGA== +"@babel/helper-compilation-targets@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz#804ae8e3f04376607cc791b9d47d540276332bd2" + integrity sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ== dependencies: - "@babel/compat-data" "^7.10.1" + "@babel/compat-data" "^7.10.4" browserslist "^4.12.0" invariant "^2.2.4" levenary "^1.1.1" @@ -217,17 +216,17 @@ levenary "^1.1.1" semver "^5.5.0" -"@babel/helper-create-class-features-plugin@^7.10.1": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.2.tgz#7474295770f217dbcf288bf7572eb213db46ee67" - integrity sha512-5C/QhkGFh1vqcziq1vAL6SI9ymzUp8BCYjFpvYVhWP4DlATIb3u5q3iUd35mvlyGs8fO7hckkW7i0tmH+5+bvQ== +"@babel/helper-create-class-features-plugin@^7.10.4", "@babel/helper-create-class-features-plugin@^7.10.5": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d" + integrity sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A== dependencies: - "@babel/helper-function-name" "^7.10.1" - "@babel/helper-member-expression-to-functions" "^7.10.1" - "@babel/helper-optimise-call-expression" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-replace-supers" "^7.10.1" - "@babel/helper-split-export-declaration" "^7.10.1" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-member-expression-to-functions" "^7.10.5" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.10.4" "@babel/helper-create-class-features-plugin@^7.8.3": version "7.8.6" @@ -241,13 +240,13 @@ "@babel/helper-replace-supers" "^7.8.6" "@babel/helper-split-export-declaration" "^7.8.3" -"@babel/helper-create-regexp-features-plugin@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.1.tgz#1b8feeab1594cbcfbf3ab5a3bbcabac0468efdbd" - integrity sha512-Rx4rHS0pVuJn5pJOqaqcZR4XSgeF9G/pO/79t+4r7380tXFJdzImFnxMU19f83wjSrmKHq6myrM10pFHTGzkUA== +"@babel/helper-create-regexp-features-plugin@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz#fdd60d88524659a0b6959c0579925e425714f3b8" + integrity sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g== dependencies: - "@babel/helper-annotate-as-pure" "^7.10.1" - "@babel/helper-regex" "^7.10.1" + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-regex" "^7.10.4" regexpu-core "^4.7.0" "@babel/helper-create-regexp-features-plugin@^7.8.3", "@babel/helper-create-regexp-features-plugin@^7.8.8": @@ -259,14 +258,14 @@ "@babel/helper-regex" "^7.8.3" regexpu-core "^4.7.0" -"@babel/helper-define-map@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.1.tgz#5e69ee8308648470dd7900d159c044c10285221d" - integrity sha512-+5odWpX+OnvkD0Zmq7panrMuAGQBu6aPUgvMzuMGo4R+jUOvealEj2hiqI6WhxgKrTpFoFj0+VdsuA8KDxHBDg== +"@babel/helper-define-map@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz#b53c10db78a640800152692b13393147acb9bb30" + integrity sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ== dependencies: - "@babel/helper-function-name" "^7.10.1" - "@babel/types" "^7.10.1" - lodash "^4.17.13" + "@babel/helper-function-name" "^7.10.4" + "@babel/types" "^7.10.5" + lodash "^4.17.19" "@babel/helper-define-map@^7.8.3": version "7.8.3" @@ -277,13 +276,13 @@ "@babel/types" "^7.8.3" lodash "^4.17.13" -"@babel/helper-explode-assignable-expression@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.1.tgz#e9d76305ee1162ca467357ae25df94f179af2b7e" - integrity sha512-vcUJ3cDjLjvkKzt6rHrl767FeE7pMEYfPanq5L16GRtrXIoznc0HykNW2aEYkcnP76P0isoqJ34dDMFZwzEpJg== +"@babel/helper-explode-assignable-expression@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz#40a1cd917bff1288f699a94a75b37a1a2dbd8c7c" + integrity sha512-4K71RyRQNPRrR85sr5QY4X3VwG4wtVoXZB9+L3r1Gp38DhELyHCtovqydRi7c1Ovb17eRGiQ/FD5s8JdU0Uy5A== dependencies: - "@babel/traverse" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" "@babel/helper-explode-assignable-expression@^7.8.3": version "7.8.3" @@ -293,14 +292,14 @@ "@babel/traverse" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/helper-function-name@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.1.tgz#92bd63829bfc9215aca9d9defa85f56b539454f4" - integrity sha512-fcpumwhs3YyZ/ttd5Rz0xn0TpIwVkN7X0V38B9TWNfVF42KEkhkAAuPCQ3oXmtTRtiPJrmZ0TrfS0GKF0eMaRQ== +"@babel/helper-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" + integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== dependencies: - "@babel/helper-get-function-arity" "^7.10.1" - "@babel/template" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" "@babel/helper-function-name@^7.8.3": version "7.8.3" @@ -320,12 +319,12 @@ "@babel/template" "^7.8.3" "@babel/types" "^7.9.5" -"@babel/helper-get-function-arity@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.1.tgz#7303390a81ba7cb59613895a192b93850e373f7d" - integrity sha512-F5qdXkYGOQUb0hpRaPoetF9AnsXknKjWMZ+wmsIRsp5ge5sFh4c3h1eH2pRTTuy9KKAA2+TTYomGXAtEL2fQEw== +"@babel/helper-get-function-arity@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" + integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== dependencies: - "@babel/types" "^7.10.1" + "@babel/types" "^7.10.4" "@babel/helper-get-function-arity@^7.8.3": version "7.8.3" @@ -334,12 +333,12 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-hoist-variables@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.1.tgz#7e77c82e5dcae1ebf123174c385aaadbf787d077" - integrity sha512-vLm5srkU8rI6X3+aQ1rQJyfjvCBLXP8cAGeuw04zeAM2ItKb1e7pmVmLyHb4sDaAYnLL13RHOZPLEtcGZ5xvjg== +"@babel/helper-hoist-variables@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz#d49b001d1d5a68ca5e6604dda01a6297f7c9381e" + integrity sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA== dependencies: - "@babel/types" "^7.10.1" + "@babel/types" "^7.10.4" "@babel/helper-hoist-variables@^7.8.3": version "7.8.3" @@ -348,12 +347,12 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-member-expression-to-functions@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.1.tgz#432967fd7e12a4afef66c4687d4ca22bc0456f15" - integrity sha512-u7XLXeM2n50gb6PWJ9hoO5oO7JFPaZtrh35t8RqKLT1jFKj9IWeD1zrcrYp1q1qiZTdEarfDWfTIP8nGsu0h5g== +"@babel/helper-member-expression-to-functions@^7.10.4", "@babel/helper-member-expression-to-functions@^7.10.5": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df" + integrity sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q== dependencies: - "@babel/types" "^7.10.1" + "@babel/types" "^7.11.0" "@babel/helper-member-expression-to-functions@^7.8.3": version "7.8.3" @@ -369,25 +368,25 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-module-imports@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.1.tgz#dd331bd45bccc566ce77004e9d05fe17add13876" - integrity sha512-SFxgwYmZ3HZPyZwJRiVNLRHWuW2OgE5k2nrVs6D9Iv4PPnXVffuEHy83Sfx/l4SqF+5kyJXjAyUmrG7tNm+qVg== +"@babel/helper-module-imports@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" + integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw== dependencies: - "@babel/types" "^7.10.1" + "@babel/types" "^7.10.4" -"@babel/helper-module-transforms@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.10.1.tgz#24e2f08ee6832c60b157bb0936c86bef7210c622" - integrity sha512-RLHRCAzyJe7Q7sF4oy2cB+kRnU4wDZY/H2xJFGof+M+SJEGhZsb+GFj5j1AD8NiSaVBJ+Pf0/WObiXu/zxWpFg== +"@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5", "@babel/helper-module-transforms@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359" + integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg== dependencies: - "@babel/helper-module-imports" "^7.10.1" - "@babel/helper-replace-supers" "^7.10.1" - "@babel/helper-simple-access" "^7.10.1" - "@babel/helper-split-export-declaration" "^7.10.1" - "@babel/template" "^7.10.1" - "@babel/types" "^7.10.1" - lodash "^4.17.13" + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-simple-access" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/template" "^7.10.4" + "@babel/types" "^7.11.0" + lodash "^4.17.19" "@babel/helper-module-transforms@^7.9.0": version "7.9.0" @@ -402,12 +401,12 @@ "@babel/types" "^7.9.0" lodash "^4.17.13" -"@babel/helper-optimise-call-expression@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.1.tgz#b4a1f2561870ce1247ceddb02a3860fa96d72543" - integrity sha512-a0DjNS1prnBsoKx83dP2falChcs7p3i8VMzdrSbfLhuQra/2ENC4sbri34dz/rWmDADsmF1q5GbfaXydh0Jbjg== +"@babel/helper-optimise-call-expression@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" + integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== dependencies: - "@babel/types" "^7.10.1" + "@babel/types" "^7.10.4" "@babel/helper-optimise-call-expression@^7.8.3": version "7.8.3" @@ -421,17 +420,17 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ== -"@babel/helper-plugin-utils@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.1.tgz#ec5a5cf0eec925b66c60580328b122c01230a127" - integrity sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA== +"@babel/helper-plugin-utils@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== -"@babel/helper-regex@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.1.tgz#021cf1a7ba99822f993222a001cc3fec83255b96" - integrity sha512-7isHr19RsIJWWLLFn21ubFt223PjQyg1HY7CZEMRr820HttHPpVvrsIN3bUOo44DEfFV4kBXO7Abbn9KTUZV7g== +"@babel/helper-regex@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.5.tgz#32dfbb79899073c415557053a19bd055aae50ae0" + integrity sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg== dependencies: - lodash "^4.17.13" + lodash "^4.17.19" "@babel/helper-regex@^7.8.3": version "7.8.3" @@ -440,16 +439,16 @@ dependencies: lodash "^4.17.13" -"@babel/helper-remap-async-to-generator@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.1.tgz#bad6aaa4ff39ce8d4b82ccaae0bfe0f7dbb5f432" - integrity sha512-RfX1P8HqsfgmJ6CwaXGKMAqbYdlleqglvVtht0HGPMSsy2V6MqLlOJVF/0Qyb/m2ZCi2z3q3+s6Pv7R/dQuZ6A== +"@babel/helper-remap-async-to-generator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.4.tgz#fce8bea4e9690bbe923056ded21e54b4e8b68ed5" + integrity sha512-86Lsr6NNw3qTNl+TBcF1oRZMaVzJtbWTyTko+CQL/tvNvcGYEFKbLXDPxtW0HKk3McNOk4KzY55itGWCAGK5tg== dependencies: - "@babel/helper-annotate-as-pure" "^7.10.1" - "@babel/helper-wrap-function" "^7.10.1" - "@babel/template" "^7.10.1" - "@babel/traverse" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-wrap-function" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" "@babel/helper-remap-async-to-generator@^7.8.3": version "7.8.3" @@ -462,15 +461,15 @@ "@babel/traverse" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/helper-replace-supers@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.1.tgz#ec6859d20c5d8087f6a2dc4e014db7228975f13d" - integrity sha512-SOwJzEfpuQwInzzQJGjGaiG578UYmyi2Xw668klPWV5n07B73S0a9btjLk/52Mlcxa+5AdIYqws1KyXRfMoB7A== +"@babel/helper-replace-supers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" + integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A== dependencies: - "@babel/helper-member-expression-to-functions" "^7.10.1" - "@babel/helper-optimise-call-expression" "^7.10.1" - "@babel/traverse" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/helper-member-expression-to-functions" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" "@babel/helper-replace-supers@^7.8.3", "@babel/helper-replace-supers@^7.8.6": version "7.8.6" @@ -482,13 +481,13 @@ "@babel/traverse" "^7.8.6" "@babel/types" "^7.8.6" -"@babel/helper-simple-access@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.1.tgz#08fb7e22ace9eb8326f7e3920a1c2052f13d851e" - integrity sha512-VSWpWzRzn9VtgMJBIWTZ+GP107kZdQ4YplJlCmIrjoLVSi/0upixezHCDG8kpPVTBJpKfxTH01wDhh+jS2zKbw== +"@babel/helper-simple-access@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461" + integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw== dependencies: - "@babel/template" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" "@babel/helper-simple-access@^7.8.3": version "7.8.3" @@ -498,12 +497,19 @@ "@babel/template" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/helper-split-export-declaration@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz#c6f4be1cbc15e3a868e4c64a17d5d31d754da35f" - integrity sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g== +"@babel/helper-skip-transparent-expression-wrappers@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz#eec162f112c2f58d3af0af125e3bb57665146729" + integrity sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q== + dependencies: + "@babel/types" "^7.11.0" + +"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" + integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== dependencies: - "@babel/types" "^7.10.1" + "@babel/types" "^7.11.0" "@babel/helper-split-export-declaration@^7.8.3": version "7.8.3" @@ -512,10 +518,10 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-validator-identifier@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz#5770b0c1a826c4f53f5ede5e153163e0318e94b5" - integrity sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw== +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== "@babel/helper-validator-identifier@^7.9.0": version "7.9.0" @@ -527,15 +533,15 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== -"@babel/helper-wrap-function@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.1.tgz#956d1310d6696257a7afd47e4c42dfda5dfcedc9" - integrity sha512-C0MzRGteVDn+H32/ZgbAv5r56f2o1fZSA/rj/TYo8JEJNHg+9BdSmKBUND0shxWRztWhjlT2cvHYuynpPsVJwQ== +"@babel/helper-wrap-function@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87" + integrity sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug== dependencies: - "@babel/helper-function-name" "^7.10.1" - "@babel/template" "^7.10.1" - "@babel/traverse" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/helper-function-name" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" "@babel/helper-wrap-function@^7.8.3": version "7.8.3" @@ -547,14 +553,14 @@ "@babel/traverse" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/helpers@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.1.tgz#a6827b7cb975c9d9cef5fd61d919f60d8844a973" - integrity sha512-muQNHF+IdU6wGgkaJyhhEmI54MOZBKsFfsXFhboz1ybwJ1Kl7IHlbm2a++4jwrmY5UYsgitt5lfqo1wMFcHmyw== +"@babel/helpers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044" + integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA== dependencies: - "@babel/template" "^7.10.1" - "@babel/traverse" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" "@babel/helpers@^7.9.0": version "7.9.2" @@ -574,12 +580,12 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/highlight@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.1.tgz#841d098ba613ba1a427a2b383d79e35552c38ae0" - integrity sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg== +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== dependencies: - "@babel/helper-validator-identifier" "^7.10.1" + "@babel/helper-validator-identifier" "^7.10.4" chalk "^2.0.0" js-tokens "^4.0.0" @@ -588,18 +594,18 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.4.tgz#68a35e6b0319bbc014465be43828300113f2f2e8" integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA== -"@babel/parser@^7.10.1", "@babel/parser@^7.10.2": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0" - integrity sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ== +"@babel/parser@^7.10.4", "@babel/parser@^7.11.0", "@babel/parser@^7.11.1", "@babel/parser@^7.11.2": + version "7.11.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.2.tgz#0882ab8a455df3065ea2dcb4c753b2460a24bead" + integrity sha512-Vuj/+7vLo6l1Vi7uuO+1ngCDNeVmNbTngcJFKCR/oEtz8tKz0CJxZEGmPt9KcIloZhOZ3Zit6xbpXT2MDlS9Vw== -"@babel/plugin-proposal-async-generator-functions@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.1.tgz#6911af5ba2e615c4ff3c497fe2f47b35bf6d7e55" - integrity sha512-vzZE12ZTdB336POZjmpblWfNNRpMSua45EYnRigE2XsZxcXcIyly2ixnTJasJE4Zq3U7t2d8rRF7XRUuzHxbOw== +"@babel/plugin-proposal-async-generator-functions@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz#3491cabf2f7c179ab820606cec27fed15e0e8558" + integrity sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-remap-async-to-generator" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.10.4" "@babel/plugin-syntax-async-generators" "^7.8.0" "@babel/plugin-proposal-async-generator-functions@^7.8.3": @@ -611,13 +617,13 @@ "@babel/helper-remap-async-to-generator" "^7.8.3" "@babel/plugin-syntax-async-generators" "^7.8.0" -"@babel/plugin-proposal-class-properties@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.1.tgz#046bc7f6550bb08d9bd1d4f060f5f5a4f1087e01" - integrity sha512-sqdGWgoXlnOdgMXU+9MbhzwFRgxVLeiGBqTrnuS7LC2IBU31wSsESbTUreT2O418obpfPdGUR2GbEufZF1bpqw== +"@babel/plugin-proposal-class-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz#a33bf632da390a59c7a8c570045d1115cd778807" + integrity sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg== dependencies: - "@babel/helper-create-class-features-plugin" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-create-class-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-proposal-class-properties@^7.7.0": version "7.8.3" @@ -627,12 +633,12 @@ "@babel/helper-create-class-features-plugin" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-proposal-dynamic-import@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.1.tgz#e36979dc1dc3b73f6d6816fc4951da2363488ef0" - integrity sha512-Cpc2yUVHTEGPlmiQzXj026kqwjEQAD9I4ZC16uzdbgWgitg/UHKHLffKNCQZ5+y8jpIZPJcKcwsr2HwPh+w3XA== +"@babel/plugin-proposal-dynamic-import@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz#ba57a26cb98b37741e9d5bca1b8b0ddf8291f17e" + integrity sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-dynamic-import" "^7.8.0" "@babel/plugin-proposal-dynamic-import@^7.8.3": @@ -643,20 +649,20 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-dynamic-import" "^7.8.0" -"@babel/plugin-proposal-export-namespace-from@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.10.1.tgz#512ee069cd866256600bdf89639cf7e1b51fbfe9" - integrity sha512-eR4CoYb6mh5y9LWjnb4CyUatuhtZ8pNLXLDi46GkqtF7WPafFqXycHdvF5qWviozZVGRSAmHzdayc8wUReCdjA== +"@babel/plugin-proposal-export-namespace-from@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.10.4.tgz#570d883b91031637b3e2958eea3c438e62c05f54" + integrity sha512-aNdf0LY6/3WXkhh0Fdb6Zk9j1NMD8ovj3F6r0+3j837Pn1S1PdNtcwJ5EG9WkVPNHPxyJDaxMaAOVq4eki0qbg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-proposal-json-strings@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.1.tgz#b1e691ee24c651b5a5e32213222b2379734aff09" - integrity sha512-m8r5BmV+ZLpWPtMY2mOKN7wre6HIO4gfIiV+eOmsnZABNenrt/kzYBwrh+KOfgumSWpnlGs5F70J8afYMSJMBg== +"@babel/plugin-proposal-json-strings@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz#593e59c63528160233bd321b1aebe0820c2341db" + integrity sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.0" "@babel/plugin-proposal-json-strings@^7.8.3": @@ -667,12 +673,20 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-json-strings" "^7.8.0" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.1.tgz#02dca21673842ff2fe763ac253777f235e9bbf78" - integrity sha512-56cI/uHYgL2C8HVuHOuvVowihhX0sxb3nnfVRzUeVHTWmRHTZrKuAh/OBIMggGU/S1g/1D2CRCXqP+3u7vX7iA== +"@babel/plugin-proposal-logical-assignment-operators@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz#9f80e482c03083c87125dee10026b58527ea20c8" + integrity sha512-/f8p4z+Auz0Uaf+i8Ekf1iM7wUNLcViFUGiPxKeXvxTSl63B875YPiVdUDdem7hREcI0E0kSpEhS8tF5RphK7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz#02a7e961fc32e6d5b2db0649e01bf80ddee7e04a" + integrity sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" "@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3": @@ -683,13 +697,13 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" -"@babel/plugin-proposal-numeric-separator@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.1.tgz#a9a38bc34f78bdfd981e791c27c6fdcec478c123" - integrity sha512-jjfym4N9HtCiNfyyLAVD8WqPYeHUrw4ihxuAynWj6zzp2gf9Ey2f7ImhFm6ikB3CLf5Z/zmcJDri6B4+9j9RsA== +"@babel/plugin-proposal-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz#ce1590ff0a65ad12970a609d78855e9a4c1aef06" + integrity sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-numeric-separator" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" "@babel/plugin-proposal-numeric-separator@^7.8.3": version "7.8.3" @@ -699,14 +713,14 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-numeric-separator" "^7.8.3" -"@babel/plugin-proposal-object-rest-spread@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.1.tgz#cba44908ac9f142650b4a65b8aa06bf3478d5fb6" - integrity sha512-Z+Qri55KiQkHh7Fc4BW6o+QBuTagbOp9txE+4U1i79u9oWlf2npkiDx+Rf3iK3lbcHBuNy9UOkwuR5wOMH3LIQ== +"@babel/plugin-proposal-object-rest-spread@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz#bd81f95a1f746760ea43b6c2d3d62b11790ad0af" + integrity sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-transform-parameters" "^7.10.1" + "@babel/plugin-transform-parameters" "^7.10.4" "@babel/plugin-proposal-object-rest-spread@^7.6.2", "@babel/plugin-proposal-object-rest-spread@^7.9.0": version "7.9.0" @@ -716,12 +730,12 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-object-rest-spread" "^7.8.0" -"@babel/plugin-proposal-optional-catch-binding@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.1.tgz#c9f86d99305f9fa531b568ff5ab8c964b8b223d2" - integrity sha512-VqExgeE62YBqI3ogkGoOJp1R6u12DFZjqwJhqtKc2o5m1YTUuUWnos7bZQFBhwkxIFpWYJ7uB75U7VAPPiKETA== +"@babel/plugin-proposal-optional-catch-binding@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz#31c938309d24a78a49d68fdabffaa863758554dd" + integrity sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" "@babel/plugin-proposal-optional-catch-binding@^7.8.3": @@ -732,12 +746,13 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" -"@babel/plugin-proposal-optional-chaining@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.1.tgz#15f5d6d22708629451a91be28f8facc55b0e818c" - integrity sha512-dqQj475q8+/avvok72CF3AOSV/SGEcH29zT5hhohqqvvZ2+boQoOr7iGldBG5YXTO2qgCgc2B3WvVLUdbeMlGA== +"@babel/plugin-proposal-optional-chaining@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz#de5866d0646f6afdaab8a566382fe3a221755076" + integrity sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" "@babel/plugin-syntax-optional-chaining" "^7.8.0" "@babel/plugin-proposal-optional-chaining@^7.9.0": @@ -748,21 +763,21 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-optional-chaining" "^7.8.0" -"@babel/plugin-proposal-private-methods@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.1.tgz#ed85e8058ab0fe309c3f448e5e1b73ca89cdb598" - integrity sha512-RZecFFJjDiQ2z6maFprLgrdnm0OzoC23Mx89xf1CcEsxmHuzuXOdniEuI+S3v7vjQG4F5sa6YtUp+19sZuSxHg== +"@babel/plugin-proposal-private-methods@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz#b160d972b8fdba5c7d111a145fc8c421fc2a6909" + integrity sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-create-class-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-proposal-unicode-property-regex@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.1.tgz#dc04feb25e2dd70c12b05d680190e138fa2c0c6f" - integrity sha512-JjfngYRvwmPwmnbRZyNiPFI8zxCZb8euzbCG/LxyKdeTb59tVciKo9GK9bi6JYKInk1H11Dq9j/zRqIH4KigfQ== +"@babel/plugin-proposal-unicode-property-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz#4483cda53041ce3413b7fe2f00022665ddfaa75d" + integrity sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-proposal-unicode-property-regex@^7.4.4", "@babel/plugin-proposal-unicode-property-regex@^7.8.3": version "7.8.8" @@ -786,12 +801,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-class-properties@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.1.tgz#d5bc0645913df5b17ad7eda0fa2308330bde34c5" - integrity sha512-Gf2Yx/iRs1JREDtVZ56OrjjgFHCaldpTnuy9BHla10qyVT3YkIIGEtoDWhyop0ksu1GvNjHIoYRBqm3zoR1jyQ== +"@babel/plugin-syntax-class-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz#6644e6a0baa55a61f9e3231f6c9eeb6ee46c124c" + integrity sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-class-properties@^7.8.3": version "7.8.3" @@ -828,12 +843,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.1.tgz#0ae371134a42b91d5418feb3c8c8d43e1565d2da" - integrity sha512-+OxyOArpVFXQeXKLO9o+r2I4dIoVoy6+Uu0vKELrlweDM3QJADZj+Z+5ERansZqIZBcLj42vHnDI8Rz9BnRIuQ== +"@babel/plugin-syntax-jsx@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.4.tgz#39abaae3cbf710c4373d8429484e6ba21340166c" + integrity sha512-KCg9mio9jwiARCB7WAcQ7Y1q+qicILjoK8LP/VkPkEKaf5dkaZZK1EcTe91a3JJlZ3qy6L5s9X52boEYi8DM9g== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-jsx@^7.8.3": version "7.8.3" @@ -842,6 +857,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.8.3.tgz#3995d7d7ffff432f6ddc742b47e730c054599897" @@ -856,12 +878,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.1.tgz#25761ee7410bc8cf97327ba741ee94e4a61b7d99" - integrity sha512-uTd0OsHrpe3tH5gRPTxG8Voh99/WCU78vIm5NMRYPAqC8lR4vajt6KkCAknCHrx24vkPdd/05yfdGSB4EIY2mg== +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-numeric-separator@^7.8.0", "@babel/plugin-syntax-numeric-separator@^7.8.3": version "7.8.3" @@ -891,12 +913,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-top-level-await@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.1.tgz#8b8733f8c57397b3eaa47ddba8841586dcaef362" - integrity sha512-hgA5RYkmZm8FTFT3yu2N9Bx7yVVOKYT6yEdXXo6j2JTm0wNxgqaGeQVaSHRjhfnQbX91DtjFB6McRFSlcJH3xQ== +"@babel/plugin-syntax-top-level-await@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz#4bbeb8917b54fcf768364e0a81f560e33a3ef57d" + integrity sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-top-level-await@^7.8.3": version "7.8.3" @@ -905,19 +927,19 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-typescript@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.10.1.tgz#5e82bc27bb4202b93b949b029e699db536733810" - integrity sha512-X/d8glkrAtra7CaQGMiGs/OGa6XgUzqPcBXCIGFCpCqnfGlT0Wfbzo/B89xHhnInTaItPK8LALblVXcUOEh95Q== +"@babel/plugin-syntax-typescript@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.10.4.tgz#2f55e770d3501e83af217d782cb7517d7bb34d25" + integrity sha512-oSAEz1YkBCAKr5Yiq8/BNtvSAPwkp/IyUnwZogd8p+F0RuYQQrLeRUzIQhueQTTBy/F+a40uS7OFKxnkRvmvFQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-arrow-functions@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.1.tgz#cb5ee3a36f0863c06ead0b409b4cc43a889b295b" - integrity sha512-6AZHgFJKP3DJX0eCNJj01RpytUa3SOGawIxweHkNX2L6PYikOZmoh5B0d7hIHaIgveMjX990IAa/xK7jRTN8OA== +"@babel/plugin-transform-arrow-functions@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz#e22960d77e697c74f41c501d44d73dbf8a6a64cd" + integrity sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-arrow-functions@^7.8.3": version "7.8.3" @@ -926,14 +948,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-async-to-generator@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.1.tgz#e5153eb1a3e028f79194ed8a7a4bf55f862b2062" - integrity sha512-XCgYjJ8TY2slj6SReBUyamJn3k2JLUIiiR5b6t1mNCMSvv7yx+jJpaewakikp0uWFQSF7ChPPoe3dHmXLpISkg== +"@babel/plugin-transform-async-to-generator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz#41a5017e49eb6f3cda9392a51eef29405b245a37" + integrity sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ== dependencies: - "@babel/helper-module-imports" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-remap-async-to-generator" "^7.10.1" + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.10.4" "@babel/plugin-transform-async-to-generator@^7.8.3": version "7.8.3" @@ -944,12 +966,12 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/helper-remap-async-to-generator" "^7.8.3" -"@babel/plugin-transform-block-scoped-functions@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.1.tgz#146856e756d54b20fff14b819456b3e01820b85d" - integrity sha512-B7K15Xp8lv0sOJrdVAoukKlxP9N59HS48V1J3U/JGj+Ad+MHq+am6xJVs85AgXrQn4LV8vaYFOB+pr/yIuzW8Q== +"@babel/plugin-transform-block-scoped-functions@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz#1afa595744f75e43a91af73b0d998ecfe4ebc2e8" + integrity sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-block-scoped-functions@^7.8.3": version "7.8.3" @@ -958,13 +980,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-block-scoping@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.1.tgz#47092d89ca345811451cd0dc5d91605982705d5e" - integrity sha512-8bpWG6TtF5akdhIm/uWTyjHqENpy13Fx8chg7pFH875aNLwX8JxIxqm08gmAT+Whe6AOmaTeLPe7dpLbXt+xUw== +"@babel/plugin-transform-block-scoping@^7.10.4": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz#5b7efe98852bef8d652c0b28144cd93a9e4b5215" + integrity sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - lodash "^4.17.13" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-block-scoping@^7.8.3": version "7.8.3" @@ -974,18 +995,18 @@ "@babel/helper-plugin-utils" "^7.8.3" lodash "^4.17.13" -"@babel/plugin-transform-classes@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.1.tgz#6e11dd6c4dfae70f540480a4702477ed766d733f" - integrity sha512-P9V0YIh+ln/B3RStPoXpEQ/CoAxQIhRSUn7aXqQ+FZJ2u8+oCtjIXR3+X0vsSD8zv+mb56K7wZW1XiDTDGiDRQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.10.1" - "@babel/helper-define-map" "^7.10.1" - "@babel/helper-function-name" "^7.10.1" - "@babel/helper-optimise-call-expression" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-replace-supers" "^7.10.1" - "@babel/helper-split-export-declaration" "^7.10.1" +"@babel/plugin-transform-classes@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz#405136af2b3e218bc4a1926228bc917ab1a0adc7" + integrity sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-define-map" "^7.10.4" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.10.4" globals "^11.1.0" "@babel/plugin-transform-classes@^7.9.0": @@ -1002,12 +1023,12 @@ "@babel/helper-split-export-declaration" "^7.8.3" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.1.tgz#59aa399064429d64dce5cf76ef9b90b7245ebd07" - integrity sha512-mqSrGjp3IefMsXIenBfGcPXxJxweQe2hEIwMQvjtiDQ9b1IBvDUjkAtV/HMXX47/vXf14qDNedXsIiNd1FmkaQ== +"@babel/plugin-transform-computed-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz#9ded83a816e82ded28d52d4b4ecbdd810cdfc0eb" + integrity sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-computed-properties@^7.8.3": version "7.8.3" @@ -1016,12 +1037,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-destructuring@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.1.tgz#abd58e51337815ca3a22a336b85f62b998e71907" - integrity sha512-V/nUc4yGWG71OhaTH705pU8ZSdM6c1KmmLP8ys59oOYbT7RpMYAR3MsVOt6OHL0WzG7BlTU076va9fjJyYzJMA== +"@babel/plugin-transform-destructuring@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz#70ddd2b3d1bea83d01509e9bb25ddb3a74fc85e5" + integrity sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-destructuring@^7.8.3": version "7.8.8" @@ -1030,13 +1051,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-dotall-regex@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.1.tgz#920b9fec2d78bb57ebb64a644d5c2ba67cc104ee" - integrity sha512-19VIMsD1dp02RvduFUmfzj8uknaO3uiHHF0s3E1OHnVsNj8oge8EQ5RzHRbJjGSetRnkEuBYO7TG1M5kKjGLOA== +"@babel/plugin-transform-dotall-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz#469c2062105c1eb6a040eaf4fac4b488078395ee" + integrity sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-dotall-regex@^7.4.4", "@babel/plugin-transform-dotall-regex@^7.8.3": version "7.8.3" @@ -1046,12 +1067,12 @@ "@babel/helper-create-regexp-features-plugin" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-duplicate-keys@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.1.tgz#c900a793beb096bc9d4d0a9d0cde19518ffc83b9" - integrity sha512-wIEpkX4QvX8Mo9W6XF3EdGttrIPZWozHfEaDTU0WJD/TDnXMvdDh30mzUl/9qWhnf7naicYartcEfUghTCSNpA== +"@babel/plugin-transform-duplicate-keys@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz#697e50c9fee14380fe843d1f306b295617431e47" + integrity sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-duplicate-keys@^7.8.3": version "7.8.3" @@ -1060,13 +1081,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-exponentiation-operator@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.1.tgz#279c3116756a60dd6e6f5e488ba7957db9c59eb3" - integrity sha512-lr/przdAbpEA2BUzRvjXdEDLrArGRRPwbaF9rvayuHRvdQ7lUTTkZnhZrJ4LE2jvgMRFF4f0YuPQ20vhiPYxtA== +"@babel/plugin-transform-exponentiation-operator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz#5ae338c57f8cf4001bdb35607ae66b92d665af2e" + integrity sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-exponentiation-operator@^7.8.3": version "7.8.3" @@ -1084,12 +1105,12 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-flow" "^7.8.3" -"@babel/plugin-transform-for-of@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.1.tgz#ff01119784eb0ee32258e8646157ba2501fcfda5" - integrity sha512-US8KCuxfQcn0LwSCMWMma8M2R5mAjJGsmoCBVwlMygvmDUMkTCykc84IqN1M7t+agSfOmLYTInLCHJM+RUoz+w== +"@babel/plugin-transform-for-of@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz#c08892e8819d3a5db29031b115af511dbbfebae9" + integrity sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-for-of@^7.9.0": version "7.9.0" @@ -1098,13 +1119,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-function-name@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.1.tgz#4ed46fd6e1d8fde2a2ec7b03c66d853d2c92427d" - integrity sha512-//bsKsKFBJfGd65qSNNh1exBy5Y9gD9ZN+DvrJ8f7HXr4avE5POW6zB7Rj6VnqHV33+0vXWUwJT0wSHubiAQkw== +"@babel/plugin-transform-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz#6a467880e0fc9638514ba369111811ddbe2644b7" + integrity sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg== dependencies: - "@babel/helper-function-name" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-function-name@^7.8.3": version "7.8.3" @@ -1114,12 +1135,12 @@ "@babel/helper-function-name" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-literals@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.1.tgz#5794f8da82846b22e4e6631ea1658bce708eb46a" - integrity sha512-qi0+5qgevz1NHLZroObRm5A+8JJtibb7vdcPQF1KQE12+Y/xxl8coJ+TpPW9iRq+Mhw/NKLjm+5SHtAHCC7lAw== +"@babel/plugin-transform-literals@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz#9f42ba0841100a135f22712d0e391c462f571f3c" + integrity sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-literals@^7.8.3": version "7.8.3" @@ -1128,12 +1149,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-member-expression-literals@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.1.tgz#90347cba31bca6f394b3f7bd95d2bbfd9fce2f39" - integrity sha512-UmaWhDokOFT2GcgU6MkHC11i0NQcL63iqeufXWfRy6pUOGYeCGEKhvfFO6Vz70UfYJYHwveg62GS83Rvpxn+NA== +"@babel/plugin-transform-member-expression-literals@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz#b1ec44fcf195afcb8db2c62cd8e551c881baf8b7" + integrity sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-member-expression-literals@^7.8.3": version "7.8.3" @@ -1142,13 +1163,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-modules-amd@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.1.tgz#65950e8e05797ebd2fe532b96e19fc5482a1d52a" - integrity sha512-31+hnWSFRI4/ACFr1qkboBbrTxoBIzj7qA69qlq8HY8p7+YCzkCT6/TvQ1a4B0z27VeWtAeJd6pr5G04dc1iHw== +"@babel/plugin-transform-modules-amd@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz#1b9cddaf05d9e88b3aad339cb3e445c4f020a9b1" + integrity sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw== dependencies: - "@babel/helper-module-transforms" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-module-transforms" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-modules-amd@^7.9.0": @@ -1160,14 +1181,14 @@ "@babel/helper-plugin-utils" "^7.8.3" babel-plugin-dynamic-import-node "^2.3.0" -"@babel/plugin-transform-modules-commonjs@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.1.tgz#d5ff4b4413ed97ffded99961056e1fb980fb9301" - integrity sha512-AQG4fc3KOah0vdITwt7Gi6hD9BtQP/8bhem7OjbaMoRNCH5Djx42O2vYMfau7QnAzQCa+RJnhJBmFFMGpQEzrg== +"@babel/plugin-transform-modules-commonjs@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz#66667c3eeda1ebf7896d41f1f16b17105a2fbca0" + integrity sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w== dependencies: - "@babel/helper-module-transforms" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-simple-access" "^7.10.1" + "@babel/helper-module-transforms" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-simple-access" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-modules-commonjs@^7.9.0": @@ -1180,14 +1201,14 @@ "@babel/helper-simple-access" "^7.8.3" babel-plugin-dynamic-import-node "^2.3.0" -"@babel/plugin-transform-modules-systemjs@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.1.tgz#9962e4b0ac6aaf2e20431ada3d8ec72082cbffb6" - integrity sha512-ewNKcj1TQZDL3YnO85qh9zo1YF1CHgmSTlRQgHqe63oTrMI85cthKtZjAiZSsSNjPQ5NCaYo5QkbYqEw1ZBgZA== +"@babel/plugin-transform-modules-systemjs@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz#6270099c854066681bae9e05f87e1b9cadbe8c85" + integrity sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw== dependencies: - "@babel/helper-hoist-variables" "^7.10.1" - "@babel/helper-module-transforms" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-hoist-variables" "^7.10.4" + "@babel/helper-module-transforms" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-modules-systemjs@^7.9.0": @@ -1200,13 +1221,13 @@ "@babel/helper-plugin-utils" "^7.8.3" babel-plugin-dynamic-import-node "^2.3.0" -"@babel/plugin-transform-modules-umd@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.1.tgz#ea080911ffc6eb21840a5197a39ede4ee67b1595" - integrity sha512-EIuiRNMd6GB6ulcYlETnYYfgv4AxqrswghmBRQbWLHZxN4s7mupxzglnHqk9ZiUpDI4eRWewedJJNj67PWOXKA== +"@babel/plugin-transform-modules-umd@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz#9a8481fe81b824654b3a0b65da3df89f3d21839e" + integrity sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA== dependencies: - "@babel/helper-module-transforms" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-module-transforms" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-modules-umd@^7.9.0": version "7.9.0" @@ -1216,6 +1237,13 @@ "@babel/helper-module-transforms" "^7.9.0" "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-named-capturing-groups-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz#78b4d978810b6f3bcf03f9e318f2fc0ed41aecb6" + integrity sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/plugin-transform-named-capturing-groups-regex@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz#a2a72bffa202ac0e2d0506afd0939c5ecbc48c6c" @@ -1223,12 +1251,12 @@ dependencies: "@babel/helper-create-regexp-features-plugin" "^7.8.3" -"@babel/plugin-transform-new-target@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.1.tgz#6ee41a5e648da7632e22b6fb54012e87f612f324" - integrity sha512-MBlzPc1nJvbmO9rPr1fQwXOM2iGut+JC92ku6PbiJMMK7SnQc1rytgpopveE3Evn47gzvGYeCdgfCDbZo0ecUw== +"@babel/plugin-transform-new-target@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz#9097d753cb7b024cb7381a3b2e52e9513a9c6888" + integrity sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-new-target@^7.8.3": version "7.8.3" @@ -1237,13 +1265,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-object-super@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.1.tgz#2e3016b0adbf262983bf0d5121d676a5ed9c4fde" - integrity sha512-WnnStUDN5GL+wGQrJylrnnVlFhFmeArINIR9gjhSeYyvroGhBrSAXYg/RHsnfzmsa+onJrTJrEClPzgNmmQ4Gw== +"@babel/plugin-transform-object-super@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz#d7146c4d139433e7a6526f888c667e314a093894" + integrity sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-replace-supers" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" "@babel/plugin-transform-object-super@^7.8.3": version "7.8.3" @@ -1253,13 +1281,13 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/helper-replace-supers" "^7.8.3" -"@babel/plugin-transform-parameters@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.1.tgz#b25938a3c5fae0354144a720b07b32766f683ddd" - integrity sha512-tJ1T0n6g4dXMsL45YsSzzSDZCxiHXAQp/qHrucOq5gEHncTA3xDxnd5+sZcoQp+N1ZbieAaB8r/VUCG0gqseOg== +"@babel/plugin-transform-parameters@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz#59d339d58d0b1950435f4043e74e2510005e2c4a" + integrity sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw== dependencies: - "@babel/helper-get-function-arity" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-parameters@^7.8.7": version "7.9.3" @@ -1269,12 +1297,12 @@ "@babel/helper-get-function-arity" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-property-literals@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.1.tgz#cffc7315219230ed81dc53e4625bf86815b6050d" - integrity sha512-Kr6+mgag8auNrgEpbfIWzdXYOvqDHZOF0+Bx2xh4H2EDNwcbRb9lY6nkZg8oSjsX+DH9Ebxm9hOqtKW+gRDeNA== +"@babel/plugin-transform-property-literals@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz#f6fe54b6590352298785b83edd815d214c42e3c0" + integrity sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-property-literals@^7.8.3": version "7.8.3" @@ -1290,12 +1318,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-react-display-name@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.10.1.tgz#e6a33f6d48dfb213dda5e007d0c7ff82b6a3d8ef" - integrity sha512-rBjKcVwjk26H3VX8pavMxGf33LNlbocMHdSeldIEswtQ/hrjyTG8fKKILW1cSkODyRovckN/uZlGb2+sAV9JUQ== +"@babel/plugin-transform-react-display-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.10.4.tgz#b5795f4e3e3140419c3611b7a2a3832b9aef328d" + integrity sha512-Zd4X54Mu9SBfPGnEcaGcOrVAYOtjT2on8QZkLKEq1S/tHexG39d9XXGZv19VfRrDjPJzFmPfTAqOQS1pfFOujw== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-react-display-name@^7.8.3": version "7.8.3" @@ -1304,14 +1332,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-react-jsx-development@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.10.1.tgz#1ac6300d8b28ef381ee48e6fec430cc38047b7f3" - integrity sha512-XwDy/FFoCfw9wGFtdn5Z+dHh6HXKHkC6DwKNWpN74VWinUagZfDcEJc3Y8Dn5B3WMVnAllX8Kviaw7MtC5Epwg== +"@babel/plugin-transform-react-jsx-development@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.10.4.tgz#6ec90f244394604623880e15ebc3c34c356258ba" + integrity sha512-RM3ZAd1sU1iQ7rI2dhrZRZGv0aqzNQMbkIUCS1txYpi9wHQ2ZHNjo5TwX+UD6pvFW4AbWqLVYvKy5qJSAyRGjQ== dependencies: - "@babel/helper-builder-react-jsx-experimental" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-jsx" "^7.10.1" + "@babel/helper-builder-react-jsx-experimental" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-jsx" "^7.10.4" "@babel/plugin-transform-react-jsx-development@^7.9.0": version "7.9.0" @@ -1322,13 +1350,13 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-jsx" "^7.8.3" -"@babel/plugin-transform-react-jsx-self@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.10.1.tgz#22143e14388d72eb88649606bb9e46f421bc3821" - integrity sha512-4p+RBw9d1qV4S749J42ZooeQaBomFPrSxa9JONLHJ1TxCBo3TzJ79vtmG2S2erUT8PDDrPdw4ZbXGr2/1+dILA== +"@babel/plugin-transform-react-jsx-self@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.10.4.tgz#cd301a5fed8988c182ed0b9d55e9bd6db0bd9369" + integrity sha512-yOvxY2pDiVJi0axdTWHSMi5T0DILN+H+SaeJeACHKjQLezEzhLx9nEF9xgpBLPtkZsks9cnb5P9iBEi21En3gg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-jsx" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-jsx" "^7.10.4" "@babel/plugin-transform-react-jsx-self@^7.9.0": version "7.9.0" @@ -1338,13 +1366,13 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-jsx" "^7.8.3" -"@babel/plugin-transform-react-jsx-source@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.10.1.tgz#30db3d4ee3cdebbb26a82a9703673714777a4273" - integrity sha512-neAbaKkoiL+LXYbGDvh6PjPG+YeA67OsZlE78u50xbWh2L1/C81uHiNP5d1fw+uqUIoiNdCC8ZB+G4Zh3hShJA== +"@babel/plugin-transform-react-jsx-source@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.10.5.tgz#34f1779117520a779c054f2cdd9680435b9222b4" + integrity sha512-wTeqHVkN1lfPLubRiZH3o73f4rfon42HpgxUSs86Nc+8QIcm/B9s8NNVXu/gwGcOyd7yDib9ikxoDLxJP0UiDA== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-jsx" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-jsx" "^7.10.4" "@babel/plugin-transform-react-jsx-source@^7.9.0": version "7.9.0" @@ -1354,15 +1382,15 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-jsx" "^7.8.3" -"@babel/plugin-transform-react-jsx@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.1.tgz#91f544248ba131486decb5d9806da6a6e19a2896" - integrity sha512-MBVworWiSRBap3Vs39eHt+6pJuLUAaK4oxGc8g+wY+vuSJvLiEQjW1LSTqKb8OUPtDvHCkdPhk7d6sjC19xyFw== +"@babel/plugin-transform-react-jsx@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.4.tgz#673c9f913948764a4421683b2bef2936968fddf2" + integrity sha512-L+MfRhWjX0eI7Js093MM6MacKU4M6dnCRa/QPDwYMxjljzSCzzlzKzj9Pk4P3OtrPcxr2N3znR419nr3Xw+65A== dependencies: - "@babel/helper-builder-react-jsx" "^7.10.1" - "@babel/helper-builder-react-jsx-experimental" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-jsx" "^7.10.1" + "@babel/helper-builder-react-jsx" "^7.10.4" + "@babel/helper-builder-react-jsx-experimental" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-jsx" "^7.10.4" "@babel/plugin-transform-react-jsx@^7.9.4": version "7.9.4" @@ -1374,18 +1402,18 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-jsx" "^7.8.3" -"@babel/plugin-transform-react-pure-annotations@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.10.1.tgz#f5e7c755d3e7614d4c926e144f501648a5277b70" - integrity sha512-mfhoiai083AkeewsBHUpaS/FM1dmUENHBMpS/tugSJ7VXqXO5dCN1Gkint2YvM1Cdv1uhmAKt1ZOuAjceKmlLA== +"@babel/plugin-transform-react-pure-annotations@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.10.4.tgz#3eefbb73db94afbc075f097523e445354a1c6501" + integrity sha512-+njZkqcOuS8RaPakrnR9KvxjoG1ASJWpoIv/doyWngId88JoFlPlISenGXjrVacZUIALGUr6eodRs1vmPnF23A== dependencies: - "@babel/helper-annotate-as-pure" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-regenerator@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.1.tgz#10e175cbe7bdb63cc9b39f9b3f823c5c7c5c5490" - integrity sha512-B3+Y2prScgJ2Bh/2l9LJxKbb8C8kRfsG4AdPT+n7ixBHIxJaIG8bi8tgjxUMege1+WqSJ+7gu1YeoMVO3gPWzw== +"@babel/plugin-transform-regenerator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz#2015e59d839074e76838de2159db421966fd8b63" + integrity sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw== dependencies: regenerator-transform "^0.14.2" @@ -1396,12 +1424,12 @@ dependencies: regenerator-transform "^0.14.2" -"@babel/plugin-transform-reserved-words@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.1.tgz#0fc1027312b4d1c3276a57890c8ae3bcc0b64a86" - integrity sha512-qN1OMoE2nuqSPmpTqEM7OvJ1FkMEV+BjVeZZm9V9mq/x1JLKQ4pcv8riZJMNN3u2AUGl0ouOMjRr2siecvHqUQ== +"@babel/plugin-transform-reserved-words@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz#8f2682bcdcef9ed327e1b0861585d7013f8a54dd" + integrity sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-reserved-words@^7.8.3": version "7.8.3" @@ -1410,22 +1438,22 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-runtime@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.10.1.tgz#fd1887f749637fb2ed86dc278e79eb41df37f4b1" - integrity sha512-4w2tcglDVEwXJ5qxsY++DgWQdNJcCCsPxfT34wCUwIf2E7dI7pMpH8JczkMBbgBTNzBX62SZlNJ9H+De6Zebaw== +"@babel/plugin-transform-runtime@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.11.0.tgz#e27f78eb36f19448636e05c33c90fd9ad9b8bccf" + integrity sha512-LFEsP+t3wkYBlis8w6/kmnd6Kb1dxTd+wGJ8MlxTGzQo//ehtqlVL4S9DNUa53+dtPSQobN2CXx4d81FqC58cw== dependencies: - "@babel/helper-module-imports" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" resolve "^1.8.1" semver "^5.5.1" -"@babel/plugin-transform-shorthand-properties@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.1.tgz#e8b54f238a1ccbae482c4dce946180ae7b3143f3" - integrity sha512-AR0E/lZMfLstScFwztApGeyTHJ5u3JUKMjneqRItWeEqDdHWZwAOKycvQNCasCK/3r5YXsuNG25funcJDu7Y2g== +"@babel/plugin-transform-shorthand-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz#9fd25ec5cdd555bb7f473e5e6ee1c971eede4dd6" + integrity sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-shorthand-properties@^7.8.3": version "7.8.3" @@ -1434,12 +1462,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-spread@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.1.tgz#0c6d618a0c4461a274418460a28c9ccf5239a7c8" - integrity sha512-8wTPym6edIrClW8FI2IoaePB91ETOtg36dOkj3bYcNe7aDMN2FXEoUa+WrmPc4xa1u2PQK46fUX2aCb+zo9rfw== +"@babel/plugin-transform-spread@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz#fa84d300f5e4f57752fe41a6d1b3c554f13f17cc" + integrity sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" "@babel/plugin-transform-spread@^7.8.3": version "7.8.3" @@ -1448,13 +1477,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-sticky-regex@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.1.tgz#90fc89b7526228bed9842cff3588270a7a393b00" - integrity sha512-j17ojftKjrL7ufX8ajKvwRilwqTok4q+BjkknmQw9VNHnItTyMP5anPFzxFJdCQs7clLcWpCV3ma+6qZWLnGMA== +"@babel/plugin-transform-sticky-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz#8f3889ee8657581130a29d9cc91d7c73b7c4a28d" + integrity sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-regex" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-regex" "^7.10.4" "@babel/plugin-transform-sticky-regex@^7.8.3": version "7.8.3" @@ -1464,13 +1493,13 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/helper-regex" "^7.8.3" -"@babel/plugin-transform-template-literals@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.1.tgz#914c7b7f4752c570ea00553b4284dad8070e8628" - integrity sha512-t7B/3MQf5M1T9hPCRG28DNGZUuxAuDqLYS03rJrIk2prj/UV7Z6FOneijhQhnv/Xa039vidXeVbvjK2SK5f7Gg== +"@babel/plugin-transform-template-literals@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz#78bc5d626a6642db3312d9d0f001f5e7639fde8c" + integrity sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw== dependencies: - "@babel/helper-annotate-as-pure" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-template-literals@^7.8.3": version "7.8.3" @@ -1480,12 +1509,12 @@ "@babel/helper-annotate-as-pure" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-typeof-symbol@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.1.tgz#60c0239b69965d166b80a84de7315c1bc7e0bb0e" - integrity sha512-qX8KZcmbvA23zDi+lk9s6hC1FM7jgLHYIjuLgULgc8QtYnmB3tAVIYkNoKRQ75qWBeyzcoMoK8ZQmogGtC/w0g== +"@babel/plugin-transform-typeof-symbol@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz#9509f1a7eec31c4edbffe137c16cc33ff0bc5bfc" + integrity sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-typeof-symbol@^7.8.4": version "7.8.4" @@ -1494,29 +1523,29 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-typescript@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.10.1.tgz#2c54daea231f602468686d9faa76f182a94507a6" - integrity sha512-v+QWKlmCnsaimLeqq9vyCsVRMViZG1k2SZTlcZvB+TqyH570Zsij8nvVUZzOASCRiQFUxkLrn9Wg/kH0zgy5OQ== +"@babel/plugin-transform-typescript@^7.10.4": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.11.0.tgz#2b4879676af37342ebb278216dd090ac67f13abb" + integrity sha512-edJsNzTtvb3MaXQwj8403B7mZoGu9ElDJQZOKjGUnvilquxBA3IQoEIOvkX/1O8xfAsnHS/oQhe2w/IXrr+w0w== dependencies: - "@babel/helper-create-class-features-plugin" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-typescript" "^7.10.1" + "@babel/helper-create-class-features-plugin" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-typescript" "^7.10.4" -"@babel/plugin-transform-unicode-escapes@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.1.tgz#add0f8483dab60570d9e03cecef6c023aa8c9940" - integrity sha512-zZ0Poh/yy1d4jeDWpx/mNwbKJVwUYJX73q+gyh4bwtG0/iUlzdEu0sLMda8yuDFS6LBQlT/ST1SJAR6zYwXWgw== +"@babel/plugin-transform-unicode-escapes@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz#feae523391c7651ddac115dae0a9d06857892007" + integrity sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-unicode-regex@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.1.tgz#6b58f2aea7b68df37ac5025d9c88752443a6b43f" - integrity sha512-Y/2a2W299k0VIUdbqYm9X2qS6fE0CUBhhiPpimK6byy7OJ/kORLlIX+J6UrjgNu5awvs62k+6RSslxhcvVw2Tw== +"@babel/plugin-transform-unicode-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz#e56d71f9282fac6db09c82742055576d5e6d80a8" + integrity sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-unicode-regex@^7.8.3": version "7.8.3" @@ -1592,70 +1621,74 @@ levenary "^1.1.1" semver "^5.5.0" -"@babel/preset-env@^7.10.2": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.10.2.tgz#715930f2cf8573b0928005ee562bed52fb65fdfb" - integrity sha512-MjqhX0RZaEgK/KueRzh+3yPSk30oqDKJ5HP5tqTSB1e2gzGS3PLy7K0BIpnp78+0anFuSwOeuCf1zZO7RzRvEA== - dependencies: - "@babel/compat-data" "^7.10.1" - "@babel/helper-compilation-targets" "^7.10.2" - "@babel/helper-module-imports" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-proposal-async-generator-functions" "^7.10.1" - "@babel/plugin-proposal-class-properties" "^7.10.1" - "@babel/plugin-proposal-dynamic-import" "^7.10.1" - "@babel/plugin-proposal-json-strings" "^7.10.1" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.10.1" - "@babel/plugin-proposal-numeric-separator" "^7.10.1" - "@babel/plugin-proposal-object-rest-spread" "^7.10.1" - "@babel/plugin-proposal-optional-catch-binding" "^7.10.1" - "@babel/plugin-proposal-optional-chaining" "^7.10.1" - "@babel/plugin-proposal-private-methods" "^7.10.1" - "@babel/plugin-proposal-unicode-property-regex" "^7.10.1" +"@babel/preset-env@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.11.0.tgz#860ee38f2ce17ad60480c2021ba9689393efb796" + integrity sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg== + dependencies: + "@babel/compat-data" "^7.11.0" + "@babel/helper-compilation-targets" "^7.10.4" + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-proposal-async-generator-functions" "^7.10.4" + "@babel/plugin-proposal-class-properties" "^7.10.4" + "@babel/plugin-proposal-dynamic-import" "^7.10.4" + "@babel/plugin-proposal-export-namespace-from" "^7.10.4" + "@babel/plugin-proposal-json-strings" "^7.10.4" + "@babel/plugin-proposal-logical-assignment-operators" "^7.11.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.10.4" + "@babel/plugin-proposal-numeric-separator" "^7.10.4" + "@babel/plugin-proposal-object-rest-spread" "^7.11.0" + "@babel/plugin-proposal-optional-catch-binding" "^7.10.4" + "@babel/plugin-proposal-optional-chaining" "^7.11.0" + "@babel/plugin-proposal-private-methods" "^7.10.4" + "@babel/plugin-proposal-unicode-property-regex" "^7.10.4" "@babel/plugin-syntax-async-generators" "^7.8.0" - "@babel/plugin-syntax-class-properties" "^7.10.1" + "@babel/plugin-syntax-class-properties" "^7.10.4" "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" - "@babel/plugin-syntax-numeric-separator" "^7.10.1" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" "@babel/plugin-syntax-object-rest-spread" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" "@babel/plugin-syntax-optional-chaining" "^7.8.0" - "@babel/plugin-syntax-top-level-await" "^7.10.1" - "@babel/plugin-transform-arrow-functions" "^7.10.1" - "@babel/plugin-transform-async-to-generator" "^7.10.1" - "@babel/plugin-transform-block-scoped-functions" "^7.10.1" - "@babel/plugin-transform-block-scoping" "^7.10.1" - "@babel/plugin-transform-classes" "^7.10.1" - "@babel/plugin-transform-computed-properties" "^7.10.1" - "@babel/plugin-transform-destructuring" "^7.10.1" - "@babel/plugin-transform-dotall-regex" "^7.10.1" - "@babel/plugin-transform-duplicate-keys" "^7.10.1" - "@babel/plugin-transform-exponentiation-operator" "^7.10.1" - "@babel/plugin-transform-for-of" "^7.10.1" - "@babel/plugin-transform-function-name" "^7.10.1" - "@babel/plugin-transform-literals" "^7.10.1" - "@babel/plugin-transform-member-expression-literals" "^7.10.1" - "@babel/plugin-transform-modules-amd" "^7.10.1" - "@babel/plugin-transform-modules-commonjs" "^7.10.1" - "@babel/plugin-transform-modules-systemjs" "^7.10.1" - "@babel/plugin-transform-modules-umd" "^7.10.1" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3" - "@babel/plugin-transform-new-target" "^7.10.1" - "@babel/plugin-transform-object-super" "^7.10.1" - "@babel/plugin-transform-parameters" "^7.10.1" - "@babel/plugin-transform-property-literals" "^7.10.1" - "@babel/plugin-transform-regenerator" "^7.10.1" - "@babel/plugin-transform-reserved-words" "^7.10.1" - "@babel/plugin-transform-shorthand-properties" "^7.10.1" - "@babel/plugin-transform-spread" "^7.10.1" - "@babel/plugin-transform-sticky-regex" "^7.10.1" - "@babel/plugin-transform-template-literals" "^7.10.1" - "@babel/plugin-transform-typeof-symbol" "^7.10.1" - "@babel/plugin-transform-unicode-escapes" "^7.10.1" - "@babel/plugin-transform-unicode-regex" "^7.10.1" + "@babel/plugin-syntax-top-level-await" "^7.10.4" + "@babel/plugin-transform-arrow-functions" "^7.10.4" + "@babel/plugin-transform-async-to-generator" "^7.10.4" + "@babel/plugin-transform-block-scoped-functions" "^7.10.4" + "@babel/plugin-transform-block-scoping" "^7.10.4" + "@babel/plugin-transform-classes" "^7.10.4" + "@babel/plugin-transform-computed-properties" "^7.10.4" + "@babel/plugin-transform-destructuring" "^7.10.4" + "@babel/plugin-transform-dotall-regex" "^7.10.4" + "@babel/plugin-transform-duplicate-keys" "^7.10.4" + "@babel/plugin-transform-exponentiation-operator" "^7.10.4" + "@babel/plugin-transform-for-of" "^7.10.4" + "@babel/plugin-transform-function-name" "^7.10.4" + "@babel/plugin-transform-literals" "^7.10.4" + "@babel/plugin-transform-member-expression-literals" "^7.10.4" + "@babel/plugin-transform-modules-amd" "^7.10.4" + "@babel/plugin-transform-modules-commonjs" "^7.10.4" + "@babel/plugin-transform-modules-systemjs" "^7.10.4" + "@babel/plugin-transform-modules-umd" "^7.10.4" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.10.4" + "@babel/plugin-transform-new-target" "^7.10.4" + "@babel/plugin-transform-object-super" "^7.10.4" + "@babel/plugin-transform-parameters" "^7.10.4" + "@babel/plugin-transform-property-literals" "^7.10.4" + "@babel/plugin-transform-regenerator" "^7.10.4" + "@babel/plugin-transform-reserved-words" "^7.10.4" + "@babel/plugin-transform-shorthand-properties" "^7.10.4" + "@babel/plugin-transform-spread" "^7.11.0" + "@babel/plugin-transform-sticky-regex" "^7.10.4" + "@babel/plugin-transform-template-literals" "^7.10.4" + "@babel/plugin-transform-typeof-symbol" "^7.10.4" + "@babel/plugin-transform-unicode-escapes" "^7.10.4" + "@babel/plugin-transform-unicode-regex" "^7.10.4" "@babel/preset-modules" "^0.1.3" - "@babel/types" "^7.10.2" + "@babel/types" "^7.11.0" browserslist "^4.12.0" core-js-compat "^3.6.2" invariant "^2.2.2" @@ -1693,34 +1726,34 @@ "@babel/plugin-transform-react-jsx-self" "^7.9.0" "@babel/plugin-transform-react-jsx-source" "^7.9.0" -"@babel/preset-react@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.10.1.tgz#e2ab8ae9a363ec307b936589f07ed753192de041" - integrity sha512-Rw0SxQ7VKhObmFjD/cUcKhPTtzpeviEFX1E6PgP+cYOhQ98icNqtINNFANlsdbQHrmeWnqdxA4Tmnl1jy5tp3Q== +"@babel/preset-react@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.10.4.tgz#92e8a66d816f9911d11d4cc935be67adfc82dbcf" + integrity sha512-BrHp4TgOIy4M19JAfO1LhycVXOPWdDbTRep7eVyatf174Hff+6Uk53sDyajqZPu8W1qXRBiYOfIamek6jA7YVw== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-transform-react-display-name" "^7.10.1" - "@babel/plugin-transform-react-jsx" "^7.10.1" - "@babel/plugin-transform-react-jsx-development" "^7.10.1" - "@babel/plugin-transform-react-jsx-self" "^7.10.1" - "@babel/plugin-transform-react-jsx-source" "^7.10.1" - "@babel/plugin-transform-react-pure-annotations" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-react-display-name" "^7.10.4" + "@babel/plugin-transform-react-jsx" "^7.10.4" + "@babel/plugin-transform-react-jsx-development" "^7.10.4" + "@babel/plugin-transform-react-jsx-self" "^7.10.4" + "@babel/plugin-transform-react-jsx-source" "^7.10.4" + "@babel/plugin-transform-react-pure-annotations" "^7.10.4" -"@babel/preset-typescript@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.10.1.tgz#a8d8d9035f55b7d99a2461a0bdc506582914d07e" - integrity sha512-m6GV3y1ShiqxnyQj10600ZVOFrSSAa8HQ3qIUk2r+gcGtHTIRw0dJnFLt1WNXpKjtVw7yw1DAPU/6ma2ZvgJuA== +"@babel/preset-typescript@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.10.4.tgz#7d5d052e52a682480d6e2cc5aa31be61c8c25e36" + integrity sha512-SdYnvGPv+bLlwkF2VkJnaX/ni1sMNetcGI1+nThF1gyv6Ph8Qucc4ZZAjM5yZcE/AKRXIOTZz7eSRDWOEjPyRQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-transform-typescript" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-typescript" "^7.10.4" -"@babel/register@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.10.1.tgz#b6567c5cb5049f44bbf8c35d6ff68ca3c43238ed" - integrity sha512-sl96+kB3IA2B9EzpwwBmYadOT14vw3KaXOknGDbJaZCOj52GDA4Tivudq9doCJcB+bEIKCEARZYwRgBBsCGXyg== +"@babel/register@^7.10.5": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.10.5.tgz#354f3574895f1307f79efe37a51525e52fd38d89" + integrity sha512-eYHdLv43nyvmPn9bfNfrcC4+iYNwdQ8Pxk1MFJuU/U5LpSYl/PH4dFMazCYZDFVi8ueG3shvO+AQfLrxpYulQw== dependencies: find-cache-dir "^2.0.0" - lodash "^4.17.13" + lodash "^4.17.19" make-dir "^2.1.0" pirates "^4.0.0" source-map-support "^0.5.16" @@ -1747,10 +1780,10 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.10.2": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.2.tgz#d103f21f2602497d38348a32e008637d506db839" - integrity sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg== +"@babel/runtime@^7.11.2": + version "7.11.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" + integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw== dependencies: regenerator-runtime "^0.13.4" @@ -1775,14 +1808,14 @@ "@babel/parser" "^7.8.6" "@babel/types" "^7.8.6" -"@babel/template@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" - integrity sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig== +"@babel/template@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" + integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== dependencies: - "@babel/code-frame" "^7.10.1" - "@babel/parser" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.10.4" + "@babel/types" "^7.10.4" "@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.4.5", "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.6", "@babel/traverse@^7.9.0": version "7.9.0" @@ -1799,20 +1832,20 @@ globals "^11.1.0" lodash "^4.17.13" -"@babel/traverse@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.1.tgz#bbcef3031e4152a6c0b50147f4958df54ca0dd27" - integrity sha512-C/cTuXeKt85K+p08jN6vMDz8vSV0vZcI0wmQ36o6mjbuo++kPMdpOYw23W2XH04dbRt9/nMEfA4W3eR21CD+TQ== - dependencies: - "@babel/code-frame" "^7.10.1" - "@babel/generator" "^7.10.1" - "@babel/helper-function-name" "^7.10.1" - "@babel/helper-split-export-declaration" "^7.10.1" - "@babel/parser" "^7.10.1" - "@babel/types" "^7.10.1" +"@babel/traverse@^7.10.4", "@babel/traverse@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.0.tgz#9b996ce1b98f53f7c3e4175115605d56ed07dd24" + integrity sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.0" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.11.0" + "@babel/types" "^7.11.0" debug "^4.1.0" globals "^11.1.0" - lodash "^4.17.13" + lodash "^4.17.19" "@babel/traverse@^7.7.4": version "7.9.5" @@ -1838,13 +1871,13 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@babel/types@^7.10.1", "@babel/types@^7.10.2": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.2.tgz#30283be31cad0dbf6fb00bd40641ca0ea675172d" - integrity sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng== +"@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.0.tgz#2ae6bf1ba9ae8c3c43824e5861269871b206e90d" + integrity sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA== dependencies: - "@babel/helper-validator-identifier" "^7.10.1" - lodash "^4.17.13" + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" to-fast-properties "^2.0.0" "@babel/types@^7.3.3": @@ -20330,7 +20363,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@4.17.11, lodash@4.17.19, lodash@>4.17.4, lodash@^4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.10.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.16, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@~4.17.10, lodash@~4.17.15, lodash@~4.17.5: +lodash@4.17.11, lodash@4.17.19, lodash@>4.17.4, lodash@^4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.10.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.16, lodash@^4.17.19, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@~4.17.10, lodash@~4.17.15, lodash@~4.17.5: version "4.17.19" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== From 810ff87afbac92aa8b655dc944b27894f1c4682a Mon Sep 17 00:00:00 2001 From: Kevin Qualters <56408403+kqualters-elastic@users.noreply.github.com> Date: Thu, 6 Aug 2020 16:29:09 -0400 Subject: [PATCH 04/19] [Security Solution][Resolver] standardize resolver panel component naming (#74537) --- ...ess_cube_icon.tsx => cube_for_process.tsx} | 90 +-- ...ounts.tsx => event_counts_for_process.tsx} | 288 +++---- .../view/{panel.tsx => panels/index.tsx} | 28 +- ...process_detail.tsx => process_details.tsx} | 2 +- ...elated_list.tsx => process_event_list.tsx} | 580 +++++++------- ..._list.tsx => process_list_with_counts.tsx} | 2 +- ...ed_detail.tsx => related_event_detail.tsx} | 750 +++++++++--------- .../public/resolver/view/styles.tsx | 2 +- 8 files changed, 871 insertions(+), 871 deletions(-) rename x-pack/plugins/security_solution/public/resolver/view/panels/{process_cube_icon.tsx => cube_for_process.tsx} (96%) rename x-pack/plugins/security_solution/public/resolver/view/panels/{panel_content_related_counts.tsx => event_counts_for_process.tsx} (97%) rename x-pack/plugins/security_solution/public/resolver/view/{panel.tsx => panels/index.tsx} (90%) rename x-pack/plugins/security_solution/public/resolver/view/panels/{panel_content_process_detail.tsx => process_details.tsx} (98%) rename x-pack/plugins/security_solution/public/resolver/view/panels/{panel_content_related_list.tsx => process_event_list.tsx} (95%) rename x-pack/plugins/security_solution/public/resolver/view/panels/{panel_content_process_list.tsx => process_list_with_counts.tsx} (99%) rename x-pack/plugins/security_solution/public/resolver/view/panels/{panel_content_related_detail.tsx => related_event_detail.tsx} (97%) diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/process_cube_icon.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/cube_for_process.tsx similarity index 96% rename from x-pack/plugins/security_solution/public/resolver/view/panels/process_cube_icon.tsx rename to x-pack/plugins/security_solution/public/resolver/view/panels/cube_for_process.tsx index b073324b27f9b..0d8f65b4e39e6 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/process_cube_icon.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/cube_for_process.tsx @@ -1,45 +1,45 @@ -/* - * 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, { memo } from 'react'; -import { useResolverTheme } from '../assets'; - -/** - * During user testing, one user indicated they wanted to see stronger visual relationships between - * Nodes on the graph and what's in the table. Using the same symbol in both places (as below) could help with that. - */ -export const CubeForProcess = memo(function CubeForProcess({ - isProcessTerminated, -}: { - isProcessTerminated: boolean; -}) { - const { cubeAssetsForNode } = useResolverTheme(); - const { cubeSymbol, descriptionText } = cubeAssetsForNode(isProcessTerminated, false); - - return ( - <> - - {descriptionText} - - - - ); -}); +/* + * 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, { memo } from 'react'; +import { useResolverTheme } from '../assets'; + +/** + * During user testing, one user indicated they wanted to see stronger visual relationships between + * Nodes on the graph and what's in the table. Using the same symbol in both places (as below) could help with that. + */ +export const CubeForProcess = memo(function CubeForProcess({ + isProcessTerminated, +}: { + isProcessTerminated: boolean; +}) { + const { cubeAssetsForNode } = useResolverTheme(); + const { cubeSymbol, descriptionText } = cubeAssetsForNode(isProcessTerminated, false); + + return ( + <> + + {descriptionText} + + + + ); +}); diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_counts.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/event_counts_for_process.tsx similarity index 97% rename from x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_counts.tsx rename to x-pack/plugins/security_solution/public/resolver/view/panels/event_counts_for_process.tsx index 880ee1dc7a10a..129aff776808a 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_counts.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/event_counts_for_process.tsx @@ -1,144 +1,144 @@ -/* - * 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, { memo, useMemo } from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiBasicTableColumn, EuiButtonEmpty, EuiSpacer, EuiInMemoryTable } from '@elastic/eui'; -import { FormattedMessage } from 'react-intl'; -import { CrumbInfo, StyledBreadcrumbs } from './panel_content_utilities'; - -import * as event from '../../../../common/endpoint/models/event'; -import { ResolverEvent, ResolverNodeStats } from '../../../../common/endpoint/types'; - -/** - * This view gives counts for all the related events of a process grouped by related event type. - * It should look something like: - * - * | Count | Event Type | - * | :--------------------- | :------------------------- | - * | 5 | DNS | - * | 12 | Registry | - * | 2 | Network | - * - */ -export const EventCountsForProcess = memo(function EventCountsForProcess({ - processEvent, - pushToQueryParams, - relatedStats, -}: { - processEvent: ResolverEvent; - pushToQueryParams: (queryStringKeyValuePair: CrumbInfo) => unknown; - relatedStats: ResolverNodeStats; -}) { - interface EventCountsTableView { - name: string; - count: number; - } - - const relatedEventsState = { stats: relatedStats.events.byCategory }; - const processName = processEvent && event.eventName(processEvent); - const processEntityId = event.entityId(processEvent); - /** - * totalCount: This will reflect the aggregated total by category for all related events - * e.g. [dns,file],[dns,file],[registry] will have an aggregate total of 5. This is to keep the - * total number consistent with the "broken out" totals we see elsewhere in the app. - * E.g. on the rleated list by type, the above would show as: - * 2 dns - * 2 file - * 1 registry - * So it would be extremely disorienting to show the user a "3" above that as a total. - */ - const totalCount = Object.values(relatedStats.events.byCategory).reduce( - (sum, val) => sum + val, - 0 - ); - const eventsString = i18n.translate( - 'xpack.securitySolution.endpoint.resolver.panel.processEventCounts.events', - { - defaultMessage: 'Events', - } - ); - const crumbs = useMemo(() => { - return [ - { - text: eventsString, - onClick: () => { - pushToQueryParams({ crumbId: '', crumbEvent: '' }); - }, - }, - { - text: processName, - onClick: () => { - pushToQueryParams({ crumbId: processEntityId, crumbEvent: '' }); - }, - }, - { - text: ( - <> - - - ), - onClick: () => { - pushToQueryParams({ crumbId: processEntityId, crumbEvent: '' }); - }, - }, - ]; - }, [processName, totalCount, processEntityId, pushToQueryParams, eventsString]); - const rows = useMemo(() => { - return Object.entries(relatedEventsState.stats).map( - ([eventType, count]): EventCountsTableView => { - return { - name: eventType, - count, - }; - } - ); - }, [relatedEventsState]); - const columns = useMemo>>( - () => [ - { - field: 'count', - name: i18n.translate('xpack.securitySolution.endpoint.resolver.panel.table.row.count', { - defaultMessage: 'Count', - }), - width: '20%', - sortable: true, - }, - { - field: 'name', - name: i18n.translate('xpack.securitySolution.endpoint.resolver.panel.table.row.eventType', { - defaultMessage: 'Event Type', - }), - width: '80%', - sortable: true, - render(name: string) { - return ( - { - pushToQueryParams({ crumbId: event.entityId(processEvent), crumbEvent: name }); - }} - > - {name} - - ); - }, - }, - ], - [pushToQueryParams, processEvent] - ); - return ( - <> - - - items={rows} columns={columns} sorting /> - - ); -}); -EventCountsForProcess.displayName = 'EventCountsForProcess'; +/* + * 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, { memo, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiBasicTableColumn, EuiButtonEmpty, EuiSpacer, EuiInMemoryTable } from '@elastic/eui'; +import { FormattedMessage } from 'react-intl'; +import { CrumbInfo, StyledBreadcrumbs } from './panel_content_utilities'; + +import * as event from '../../../../common/endpoint/models/event'; +import { ResolverEvent, ResolverNodeStats } from '../../../../common/endpoint/types'; + +/** + * This view gives counts for all the related events of a process grouped by related event type. + * It should look something like: + * + * | Count | Event Type | + * | :--------------------- | :------------------------- | + * | 5 | DNS | + * | 12 | Registry | + * | 2 | Network | + * + */ +export const EventCountsForProcess = memo(function EventCountsForProcess({ + processEvent, + pushToQueryParams, + relatedStats, +}: { + processEvent: ResolverEvent; + pushToQueryParams: (queryStringKeyValuePair: CrumbInfo) => unknown; + relatedStats: ResolverNodeStats; +}) { + interface EventCountsTableView { + name: string; + count: number; + } + + const relatedEventsState = { stats: relatedStats.events.byCategory }; + const processName = processEvent && event.eventName(processEvent); + const processEntityId = event.entityId(processEvent); + /** + * totalCount: This will reflect the aggregated total by category for all related events + * e.g. [dns,file],[dns,file],[registry] will have an aggregate total of 5. This is to keep the + * total number consistent with the "broken out" totals we see elsewhere in the app. + * E.g. on the rleated list by type, the above would show as: + * 2 dns + * 2 file + * 1 registry + * So it would be extremely disorienting to show the user a "3" above that as a total. + */ + const totalCount = Object.values(relatedStats.events.byCategory).reduce( + (sum, val) => sum + val, + 0 + ); + const eventsString = i18n.translate( + 'xpack.securitySolution.endpoint.resolver.panel.processEventCounts.events', + { + defaultMessage: 'Events', + } + ); + const crumbs = useMemo(() => { + return [ + { + text: eventsString, + onClick: () => { + pushToQueryParams({ crumbId: '', crumbEvent: '' }); + }, + }, + { + text: processName, + onClick: () => { + pushToQueryParams({ crumbId: processEntityId, crumbEvent: '' }); + }, + }, + { + text: ( + <> + + + ), + onClick: () => { + pushToQueryParams({ crumbId: processEntityId, crumbEvent: '' }); + }, + }, + ]; + }, [processName, totalCount, processEntityId, pushToQueryParams, eventsString]); + const rows = useMemo(() => { + return Object.entries(relatedEventsState.stats).map( + ([eventType, count]): EventCountsTableView => { + return { + name: eventType, + count, + }; + } + ); + }, [relatedEventsState]); + const columns = useMemo>>( + () => [ + { + field: 'count', + name: i18n.translate('xpack.securitySolution.endpoint.resolver.panel.table.row.count', { + defaultMessage: 'Count', + }), + width: '20%', + sortable: true, + }, + { + field: 'name', + name: i18n.translate('xpack.securitySolution.endpoint.resolver.panel.table.row.eventType', { + defaultMessage: 'Event Type', + }), + width: '80%', + sortable: true, + render(name: string) { + return ( + { + pushToQueryParams({ crumbId: event.entityId(processEvent), crumbEvent: name }); + }} + > + {name} + + ); + }, + }, + ], + [pushToQueryParams, processEvent] + ); + return ( + <> + + + items={rows} columns={columns} sorting /> + + ); +}); +EventCountsForProcess.displayName = 'EventCountsForProcess'; diff --git a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/index.tsx similarity index 90% rename from x-pack/plugins/security_solution/public/resolver/view/panel.tsx rename to x-pack/plugins/security_solution/public/resolver/view/panels/index.tsx index f378ab36bac94..7e7e8b757baf7 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/index.tsx @@ -7,17 +7,17 @@ import React, { memo, useMemo, useContext, useLayoutEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import { EuiPanel } from '@elastic/eui'; -import * as selectors from '../store/selectors'; -import { useResolverDispatch } from './use_resolver_dispatch'; -import * as event from '../../../common/endpoint/models/event'; -import { ResolverEvent, ResolverNodeStats } from '../../../common/endpoint/types'; -import { SideEffectContext } from './side_effect_context'; -import { ProcessEventListNarrowedByType } from './panels/panel_content_related_list'; -import { EventCountsForProcess } from './panels/panel_content_related_counts'; -import { ProcessDetails } from './panels/panel_content_process_detail'; -import { ProcessListWithCounts } from './panels/panel_content_process_list'; -import { RelatedEventDetail } from './panels/panel_content_related_detail'; -import { useResolverQueryParams } from './use_resolver_query_params'; +import * as selectors from '../../store/selectors'; +import { useResolverDispatch } from '../use_resolver_dispatch'; +import * as event from '../../../../common/endpoint/models/event'; +import { ResolverEvent, ResolverNodeStats } from '../../../../common/endpoint/types'; +import { SideEffectContext } from '../side_effect_context'; +import { ProcessEventList } from './process_event_list'; +import { EventCountsForProcess } from './event_counts_for_process'; +import { ProcessDetails } from './process_details'; +import { ProcessListWithCounts } from './process_list_with_counts'; +import { RelatedEventDetail } from './related_event_detail'; +import { useResolverQueryParams } from '../use_resolver_query_params'; /** * The team decided to use this table to determine which breadcrumbs/view to display: @@ -145,7 +145,7 @@ const PanelContent = memo(function PanelContent() { */ if (crumbEvent && crumbEvent.length && uiSelectedEvent) { - return 'processEventListNarrowedByType'; + return 'processEventList'; } } @@ -179,9 +179,9 @@ const PanelContent = memo(function PanelContent() { ); } - if (panelToShow === 'processEventListNarrowedByType') { + if (panelToShow === 'processEventList') { return ( - void; -} - -const StyledRelatedLimitWarning = styled(RelatedEventLimitWarning)` - flex-flow: row wrap; - display: block; - align-items: baseline; - margin-top: 1em; - - & .euiCallOutHeader { - display: inline; - margin-right: 0.25em; - } - - & .euiText { - display: inline; - } - - & .euiText p { - display: inline; - } -`; - -const DisplayList = memo(function DisplayList({ - crumbs, - matchingEventEntries, - eventType, - processEntityId, -}: { - crumbs: Array<{ text: string | JSX.Element; onClick: () => void }>; - matchingEventEntries: MatchingEventEntry[]; - eventType: string; - processEntityId: string; -}) { - const relatedLookupsByCategory = useSelector(selectors.relatedEventInfoByEntityId); - const lookupsForThisNode = relatedLookupsByCategory(processEntityId); - const shouldShowLimitWarning = lookupsForThisNode?.shouldShowLimitForCategory(eventType); - const numberDisplayed = lookupsForThisNode?.numberActuallyDisplayedForCategory(eventType); - const numberMissing = lookupsForThisNode?.numberNotDisplayedForCategory(eventType); - - return ( - <> - - {shouldShowLimitWarning && typeof numberDisplayed !== 'undefined' && numberMissing ? ( - - ) : null} - - <> - {matchingEventEntries.map((eventView, index) => { - const { subject, descriptor = '' } = eventView.name; - return ( - - - - - - - - - - - - - - {index === matchingEventEntries.length - 1 ? null : } - - ); - })} - - - ); -}); - -export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarrowedByType({ - processEvent, - eventType, - relatedStats, - pushToQueryParams, -}: { - processEvent: ResolverEvent; - pushToQueryParams: (arg0: CrumbInfo) => unknown; - eventType: string; - relatedStats: ResolverNodeStats; -}) { - const processName = processEvent && event.eventName(processEvent); - const processEntityId = event.entityId(processEvent); - const totalCount = Object.values(relatedStats.events.byCategory).reduce( - (sum, val) => sum + val, - 0 - ); - const eventsString = i18n.translate( - 'xpack.securitySolution.endpoint.resolver.panel.processEventListByType.events', - { - defaultMessage: 'Events', - } - ); - const waitingString = i18n.translate( - 'xpack.securitySolution.endpoint.resolver.panel.processEventListByType.wait', - { - defaultMessage: 'Waiting For Events...', - } - ); - - const relatedsReadyMap = useSelector(selectors.relatedEventsReady); - const relatedsReady = relatedsReadyMap.get(processEntityId); - - const dispatch = useResolverDispatch(); - - useEffect(() => { - if (typeof relatedsReady === 'undefined') { - dispatch({ - type: 'appDetectedMissingEventData', - payload: processEntityId, - }); - } - }, [relatedsReady, dispatch, processEntityId]); - - const waitCrumbs = useMemo(() => { - return [ - { - text: eventsString, - onClick: () => { - pushToQueryParams({ crumbId: '', crumbEvent: '' }); - }, - }, - ]; - }, [pushToQueryParams, eventsString]); - - const relatedByCategory = useSelector(selectors.relatedEventsByCategory); - - /** - * A list entry will be displayed for each of these - */ - const matchingEventEntries: MatchingEventEntry[] = useMemo(() => { - const relateds = relatedByCategory(processEntityId)(eventType).map((resolverEvent) => { - const eventTime = event.eventTimestamp(resolverEvent); - const formattedDate = typeof eventTime === 'undefined' ? '' : formatDate(eventTime); - const entityId = event.eventId(resolverEvent); - - return { - formattedDate, - eventCategory: `${eventType}`, - eventType: `${event.ecsEventType(resolverEvent)}`, - name: event.descriptiveName(resolverEvent), - setQueryParams: () => { - pushToQueryParams({ - crumbId: entityId === undefined ? '' : String(entityId), - crumbEvent: processEntityId, - }); - }, - }; - }); - return relateds; - }, [relatedByCategory, eventType, processEntityId, pushToQueryParams]); - - const crumbs = useMemo(() => { - return [ - { - text: eventsString, - onClick: () => { - pushToQueryParams({ crumbId: '', crumbEvent: '' }); - }, - }, - { - text: processName, - onClick: () => { - pushToQueryParams({ crumbId: processEntityId, crumbEvent: '' }); - }, - }, - { - text: ( - <> - - - ), - onClick: () => { - pushToQueryParams({ crumbId: processEntityId, crumbEvent: 'all' }); - }, - }, - { - text: ( - <> - - - ), - onClick: () => {}, - }, - ]; - }, [ - eventType, - eventsString, - matchingEventEntries.length, - processEntityId, - processName, - pushToQueryParams, - totalCount, - ]); - - /** - * Wait here until the effect resolves... - */ - if (!relatedsReady) { - return ( - <> - - - -

{waitingString}

-
- - ); - } - - return ( - - ); -}); -ProcessEventListNarrowedByType.displayName = 'ProcessEventListNarrowedByType'; +/* + * 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, { memo, useMemo, useEffect, Fragment } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiTitle, EuiSpacer, EuiText, EuiButtonEmpty, EuiHorizontalRule } from '@elastic/eui'; +import { useSelector } from 'react-redux'; +import { FormattedMessage } from 'react-intl'; +import styled from 'styled-components'; +import { + CrumbInfo, + formatDate, + StyledBreadcrumbs, + BoldCode, + StyledTime, +} from './panel_content_utilities'; +import * as event from '../../../../common/endpoint/models/event'; +import { ResolverEvent, ResolverNodeStats } from '../../../../common/endpoint/types'; +import * as selectors from '../../store/selectors'; +import { useResolverDispatch } from '../use_resolver_dispatch'; +import { RelatedEventLimitWarning } from '../limit_warnings'; + +/** + * This view presents a list of related events of a given type for a given process. + * It will appear like: + * + * | | + * | :----------------------------------------------------- | + * | **registry deletion** @ *3:32PM..* *HKLM/software...* | + * | **file creation** @ *3:34PM..* *C:/directory/file.exe* | + */ + +interface MatchingEventEntry { + formattedDate: string; + eventType: string; + eventCategory: string; + name: { subject: string; descriptor?: string }; + setQueryParams: () => void; +} + +const StyledRelatedLimitWarning = styled(RelatedEventLimitWarning)` + flex-flow: row wrap; + display: block; + align-items: baseline; + margin-top: 1em; + + & .euiCallOutHeader { + display: inline; + margin-right: 0.25em; + } + + & .euiText { + display: inline; + } + + & .euiText p { + display: inline; + } +`; + +const DisplayList = memo(function DisplayList({ + crumbs, + matchingEventEntries, + eventType, + processEntityId, +}: { + crumbs: Array<{ text: string | JSX.Element; onClick: () => void }>; + matchingEventEntries: MatchingEventEntry[]; + eventType: string; + processEntityId: string; +}) { + const relatedLookupsByCategory = useSelector(selectors.relatedEventInfoByEntityId); + const lookupsForThisNode = relatedLookupsByCategory(processEntityId); + const shouldShowLimitWarning = lookupsForThisNode?.shouldShowLimitForCategory(eventType); + const numberDisplayed = lookupsForThisNode?.numberActuallyDisplayedForCategory(eventType); + const numberMissing = lookupsForThisNode?.numberNotDisplayedForCategory(eventType); + + return ( + <> + + {shouldShowLimitWarning && typeof numberDisplayed !== 'undefined' && numberMissing ? ( + + ) : null} + + <> + {matchingEventEntries.map((eventView, index) => { + const { subject, descriptor = '' } = eventView.name; + return ( + + + + + + + + + + + + + + {index === matchingEventEntries.length - 1 ? null : } + + ); + })} + + + ); +}); + +export const ProcessEventList = memo(function ProcessEventList({ + processEvent, + eventType, + relatedStats, + pushToQueryParams, +}: { + processEvent: ResolverEvent; + pushToQueryParams: (arg0: CrumbInfo) => unknown; + eventType: string; + relatedStats: ResolverNodeStats; +}) { + const processName = processEvent && event.eventName(processEvent); + const processEntityId = event.entityId(processEvent); + const totalCount = Object.values(relatedStats.events.byCategory).reduce( + (sum, val) => sum + val, + 0 + ); + const eventsString = i18n.translate( + 'xpack.securitySolution.endpoint.resolver.panel.processEventListByType.events', + { + defaultMessage: 'Events', + } + ); + const waitingString = i18n.translate( + 'xpack.securitySolution.endpoint.resolver.panel.processEventListByType.wait', + { + defaultMessage: 'Waiting For Events...', + } + ); + + const relatedsReadyMap = useSelector(selectors.relatedEventsReady); + const relatedsReady = relatedsReadyMap.get(processEntityId); + + const dispatch = useResolverDispatch(); + + useEffect(() => { + if (typeof relatedsReady === 'undefined') { + dispatch({ + type: 'appDetectedMissingEventData', + payload: processEntityId, + }); + } + }, [relatedsReady, dispatch, processEntityId]); + + const waitCrumbs = useMemo(() => { + return [ + { + text: eventsString, + onClick: () => { + pushToQueryParams({ crumbId: '', crumbEvent: '' }); + }, + }, + ]; + }, [pushToQueryParams, eventsString]); + + const relatedByCategory = useSelector(selectors.relatedEventsByCategory); + + /** + * A list entry will be displayed for each of these + */ + const matchingEventEntries: MatchingEventEntry[] = useMemo(() => { + const relateds = relatedByCategory(processEntityId)(eventType).map((resolverEvent) => { + const eventTime = event.eventTimestamp(resolverEvent); + const formattedDate = typeof eventTime === 'undefined' ? '' : formatDate(eventTime); + const entityId = event.eventId(resolverEvent); + + return { + formattedDate, + eventCategory: `${eventType}`, + eventType: `${event.ecsEventType(resolverEvent)}`, + name: event.descriptiveName(resolverEvent), + setQueryParams: () => { + pushToQueryParams({ + crumbId: entityId === undefined ? '' : String(entityId), + crumbEvent: processEntityId, + }); + }, + }; + }); + return relateds; + }, [relatedByCategory, eventType, processEntityId, pushToQueryParams]); + + const crumbs = useMemo(() => { + return [ + { + text: eventsString, + onClick: () => { + pushToQueryParams({ crumbId: '', crumbEvent: '' }); + }, + }, + { + text: processName, + onClick: () => { + pushToQueryParams({ crumbId: processEntityId, crumbEvent: '' }); + }, + }, + { + text: ( + <> + + + ), + onClick: () => { + pushToQueryParams({ crumbId: processEntityId, crumbEvent: 'all' }); + }, + }, + { + text: ( + <> + + + ), + onClick: () => {}, + }, + ]; + }, [ + eventType, + eventsString, + matchingEventEntries.length, + processEntityId, + processName, + pushToQueryParams, + totalCount, + ]); + + /** + * Wait here until the effect resolves... + */ + if (!relatedsReady) { + return ( + <> + + + +

{waitingString}

+
+ + ); + } + + return ( + + ); +}); +ProcessEventList.displayName = 'ProcessEventList'; diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/process_list_with_counts.tsx similarity index 99% rename from x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_list.tsx rename to x-pack/plugins/security_solution/public/resolver/view/panels/process_list_with_counts.tsx index 70422a6919e51..046c840470262 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_list.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/process_list_with_counts.tsx @@ -19,7 +19,7 @@ import * as selectors from '../../store/selectors'; import { CrumbInfo, formatter, StyledBreadcrumbs } from './panel_content_utilities'; import { useResolverDispatch } from '../use_resolver_dispatch'; import { SideEffectContext } from '../side_effect_context'; -import { CubeForProcess } from './process_cube_icon'; +import { CubeForProcess } from './cube_for_process'; import { SafeResolverEvent } from '../../../../common/endpoint/types'; import { LimitWarning } from '../limit_warnings'; diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_detail.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/related_event_detail.tsx similarity index 97% rename from x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_detail.tsx rename to x-pack/plugins/security_solution/public/resolver/view/panels/related_event_detail.tsx index 10e57a09b5da4..3579b1b2f69b8 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_detail.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/related_event_detail.tsx @@ -1,375 +1,375 @@ -/* - * 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, { memo, useMemo, useEffect, Fragment } from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiSpacer, EuiText, EuiDescriptionList, EuiTextColor, EuiTitle } from '@elastic/eui'; -import styled from 'styled-components'; -import { useSelector } from 'react-redux'; -import { FormattedMessage } from 'react-intl'; -import { - CrumbInfo, - formatDate, - StyledBreadcrumbs, - BoldCode, - StyledTime, -} from './panel_content_utilities'; -import * as event from '../../../../common/endpoint/models/event'; -import { ResolverEvent } from '../../../../common/endpoint/types'; -import * as selectors from '../../store/selectors'; -import { useResolverDispatch } from '../use_resolver_dispatch'; -import { PanelContentError } from './panel_content_error'; - -/** - * A helper function to turn objects into EuiDescriptionList entries. - * This reflects the strategy of more or less "dumping" metadata for related processes - * in description lists with little/no 'prettification'. This has the obvious drawback of - * data perhaps appearing inscrutable/daunting, but the benefit of presenting these fields - * to the user "as they occur" in ECS, which may help them with e.g. EQL queries. - * - * Given an object like: {a:{b: 1}, c: 'd'} it will yield title/description entries like so: - * {title: "a.b", description: "1"}, {title: "c", description: "d"} - * - * @param {object} obj The object to turn into `
` entries - */ -const objectToDescriptionListEntries = function* ( - obj: object, - prefix = '' -): Generator<{ title: string; description: string }> { - const nextPrefix = prefix.length ? `${prefix}.` : ''; - for (const [metaKey, metaValue] of Object.entries(obj)) { - if (typeof metaValue === 'number' || typeof metaValue === 'string') { - yield { title: nextPrefix + metaKey, description: `${metaValue}` }; - } else if (metaValue instanceof Array) { - yield { - title: nextPrefix + metaKey, - description: metaValue - .filter((arrayEntry) => { - return typeof arrayEntry === 'number' || typeof arrayEntry === 'string'; - }) - .join(','), - }; - } else if (typeof metaValue === 'object') { - yield* objectToDescriptionListEntries(metaValue, nextPrefix + metaKey); - } - } -}; - -// Adding some styles to prevent horizontal scrollbars, per request from UX review -const StyledDescriptionList = memo(styled(EuiDescriptionList)` - &.euiDescriptionList.euiDescriptionList--column dt.euiDescriptionList__title.desc-title { - max-width: 8em; - } - &.euiDescriptionList.euiDescriptionList--column dd.euiDescriptionList__description { - max-width: calc(100% - 8.5em); - overflow-wrap: break-word; - } -`); - -// Styling subtitles, per UX review: -const StyledFlexTitle = memo(styled('h3')` - display: flex; - flex-flow: row; - font-size: 1.2em; -`); -const StyledTitleRule = memo(styled('hr')` - &.euiHorizontalRule.euiHorizontalRule--full.euiHorizontalRule--marginSmall.override { - display: block; - flex: 1; - margin-left: 0.5em; - } -`); - -const TitleHr = memo(() => { - return ( - - ); -}); -TitleHr.displayName = 'TitleHR'; - -/** - * This view presents a detailed view of all the available data for a related event, split and titled by the "section" - * it appears in the underlying ResolverEvent - */ -export const RelatedEventDetail = memo(function RelatedEventDetail({ - relatedEventId, - parentEvent, - pushToQueryParams, - countForParent, -}: { - relatedEventId: string; - parentEvent: ResolverEvent; - pushToQueryParams: (queryStringKeyValuePair: CrumbInfo) => unknown; - countForParent: number | undefined; -}) { - const processName = (parentEvent && event.eventName(parentEvent)) || '*'; - const processEntityId = parentEvent && event.entityId(parentEvent); - const totalCount = countForParent || 0; - const eventsString = i18n.translate( - 'xpack.securitySolution.endpoint.resolver.panel.relatedEventDetail.events', - { - defaultMessage: 'Events', - } - ); - const naString = i18n.translate( - 'xpack.securitySolution.endpoint.resolver.panel.relatedEventDetail.NA', - { - defaultMessage: 'N/A', - } - ); - - const relatedsReadyMap = useSelector(selectors.relatedEventsReady); - const relatedsReady = relatedsReadyMap.get(processEntityId!); - const dispatch = useResolverDispatch(); - - /** - * If we don't have the related events for the parent yet, use this effect - * to request them. - */ - useEffect(() => { - if (typeof relatedsReady === 'undefined') { - dispatch({ - type: 'appDetectedMissingEventData', - payload: processEntityId, - }); - } - }, [relatedsReady, dispatch, processEntityId]); - - const relatedEventsForThisProcess = useSelector(selectors.relatedEventsByEntityId).get( - processEntityId! - ); - - const [relatedEventToShowDetailsFor, countBySameCategory, relatedEventCategory] = useMemo(() => { - if (!relatedEventsForThisProcess) { - return [undefined, 0]; - } - const specificEvent = relatedEventsForThisProcess.events.find( - (evt) => event.eventId(evt) === relatedEventId - ); - // For breadcrumbs: - const specificCategory = specificEvent && event.primaryEventCategory(specificEvent); - const countOfCategory = relatedEventsForThisProcess.events.reduce((sumtotal, evt) => { - return event.primaryEventCategory(evt) === specificCategory ? sumtotal + 1 : sumtotal; - }, 0); - return [specificEvent, countOfCategory, specificCategory || naString]; - }, [relatedEventsForThisProcess, naString, relatedEventId]); - - const [sections, formattedDate] = useMemo(() => { - if (!relatedEventToShowDetailsFor) { - // This could happen if user relaods from URL param and requests an eventId that no longer exists - return [[], naString]; - } - // Assuming these details (agent, ecs, process) aren't as helpful, can revisit - const { - agent, - ecs, - process, - ...relevantData - } = relatedEventToShowDetailsFor as ResolverEvent & { - // Type this with various unknown keys so that ts will let us delete those keys - ecs: unknown; - process: unknown; - }; - let displayDate = ''; - const sectionData: Array<{ - sectionTitle: string; - entries: Array<{ title: string; description: string }>; - }> = Object.entries(relevantData) - .map(([sectionTitle, val]) => { - if (sectionTitle === '@timestamp') { - displayDate = formatDate(val); - return { sectionTitle: '', entries: [] }; - } - if (typeof val !== 'object') { - return { sectionTitle, entries: [{ title: sectionTitle, description: `${val}` }] }; - } - return { sectionTitle, entries: [...objectToDescriptionListEntries(val)] }; - }) - .filter((v) => v.sectionTitle !== '' && v.entries.length); - return [sectionData, displayDate]; - }, [relatedEventToShowDetailsFor, naString]); - - const waitCrumbs = useMemo(() => { - return [ - { - text: eventsString, - onClick: () => { - pushToQueryParams({ crumbId: '', crumbEvent: '' }); - }, - }, - ]; - }, [pushToQueryParams, eventsString]); - - const { subject = '', descriptor = '' } = relatedEventToShowDetailsFor - ? event.descriptiveName(relatedEventToShowDetailsFor) - : {}; - const crumbs = useMemo(() => { - return [ - { - text: eventsString, - onClick: () => { - pushToQueryParams({ crumbId: '', crumbEvent: '' }); - }, - }, - { - text: processName, - onClick: () => { - pushToQueryParams({ crumbId: processEntityId!, crumbEvent: '' }); - }, - }, - { - text: ( - <> - - - ), - onClick: () => { - pushToQueryParams({ crumbId: processEntityId!, crumbEvent: 'all' }); - }, - }, - { - text: ( - <> - - - ), - onClick: () => { - pushToQueryParams({ - crumbId: processEntityId!, - crumbEvent: relatedEventCategory || 'all', - }); - }, - }, - { - text: relatedEventToShowDetailsFor ? ( - - ) : ( - naString - ), - onClick: () => {}, - }, - ]; - }, [ - processName, - processEntityId, - eventsString, - pushToQueryParams, - totalCount, - countBySameCategory, - naString, - relatedEventCategory, - relatedEventToShowDetailsFor, - subject, - descriptor, - ]); - - /** - * If the ship hasn't come in yet, wait on the dock - */ - if (!relatedsReady) { - const waitingString = i18n.translate( - 'xpack.securitySolution.endpoint.resolver.panel.relatedDetail.wait', - { - defaultMessage: 'Waiting For Events...', - } - ); - return ( - <> - - - -

{waitingString}

-
- - ); - } - - /** - * Could happen if user e.g. loads a URL with a bad crumbEvent - */ - if (!relatedEventToShowDetailsFor) { - const errString = i18n.translate( - 'xpack.securitySolution.endpoint.resolver.panel.relatedDetail.missing', - { - defaultMessage: 'Related event not found.', - } - ); - return ( - - ); - } - - return ( - <> - - - - - - - - - - - - - - - - {sections.map(({ sectionTitle, entries }, index) => { - return ( - - {index === 0 ? null : } - - - - {sectionTitle} - - - - - - - {index === sections.length - 1 ? null : } - - ); - })} - - ); -}); +/* + * 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, { memo, useMemo, useEffect, Fragment } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiSpacer, EuiText, EuiDescriptionList, EuiTextColor, EuiTitle } from '@elastic/eui'; +import styled from 'styled-components'; +import { useSelector } from 'react-redux'; +import { FormattedMessage } from 'react-intl'; +import { + CrumbInfo, + formatDate, + StyledBreadcrumbs, + BoldCode, + StyledTime, +} from './panel_content_utilities'; +import * as event from '../../../../common/endpoint/models/event'; +import { ResolverEvent } from '../../../../common/endpoint/types'; +import * as selectors from '../../store/selectors'; +import { useResolverDispatch } from '../use_resolver_dispatch'; +import { PanelContentError } from './panel_content_error'; + +/** + * A helper function to turn objects into EuiDescriptionList entries. + * This reflects the strategy of more or less "dumping" metadata for related processes + * in description lists with little/no 'prettification'. This has the obvious drawback of + * data perhaps appearing inscrutable/daunting, but the benefit of presenting these fields + * to the user "as they occur" in ECS, which may help them with e.g. EQL queries. + * + * Given an object like: {a:{b: 1}, c: 'd'} it will yield title/description entries like so: + * {title: "a.b", description: "1"}, {title: "c", description: "d"} + * + * @param {object} obj The object to turn into `
` entries + */ +const objectToDescriptionListEntries = function* ( + obj: object, + prefix = '' +): Generator<{ title: string; description: string }> { + const nextPrefix = prefix.length ? `${prefix}.` : ''; + for (const [metaKey, metaValue] of Object.entries(obj)) { + if (typeof metaValue === 'number' || typeof metaValue === 'string') { + yield { title: nextPrefix + metaKey, description: `${metaValue}` }; + } else if (metaValue instanceof Array) { + yield { + title: nextPrefix + metaKey, + description: metaValue + .filter((arrayEntry) => { + return typeof arrayEntry === 'number' || typeof arrayEntry === 'string'; + }) + .join(','), + }; + } else if (typeof metaValue === 'object') { + yield* objectToDescriptionListEntries(metaValue, nextPrefix + metaKey); + } + } +}; + +// Adding some styles to prevent horizontal scrollbars, per request from UX review +const StyledDescriptionList = memo(styled(EuiDescriptionList)` + &.euiDescriptionList.euiDescriptionList--column dt.euiDescriptionList__title.desc-title { + max-width: 8em; + } + &.euiDescriptionList.euiDescriptionList--column dd.euiDescriptionList__description { + max-width: calc(100% - 8.5em); + overflow-wrap: break-word; + } +`); + +// Styling subtitles, per UX review: +const StyledFlexTitle = memo(styled('h3')` + display: flex; + flex-flow: row; + font-size: 1.2em; +`); +const StyledTitleRule = memo(styled('hr')` + &.euiHorizontalRule.euiHorizontalRule--full.euiHorizontalRule--marginSmall.override { + display: block; + flex: 1; + margin-left: 0.5em; + } +`); + +const TitleHr = memo(() => { + return ( + + ); +}); +TitleHr.displayName = 'TitleHR'; + +/** + * This view presents a detailed view of all the available data for a related event, split and titled by the "section" + * it appears in the underlying ResolverEvent + */ +export const RelatedEventDetail = memo(function RelatedEventDetail({ + relatedEventId, + parentEvent, + pushToQueryParams, + countForParent, +}: { + relatedEventId: string; + parentEvent: ResolverEvent; + pushToQueryParams: (queryStringKeyValuePair: CrumbInfo) => unknown; + countForParent: number | undefined; +}) { + const processName = (parentEvent && event.eventName(parentEvent)) || '*'; + const processEntityId = parentEvent && event.entityId(parentEvent); + const totalCount = countForParent || 0; + const eventsString = i18n.translate( + 'xpack.securitySolution.endpoint.resolver.panel.relatedEventDetail.events', + { + defaultMessage: 'Events', + } + ); + const naString = i18n.translate( + 'xpack.securitySolution.endpoint.resolver.panel.relatedEventDetail.NA', + { + defaultMessage: 'N/A', + } + ); + + const relatedsReadyMap = useSelector(selectors.relatedEventsReady); + const relatedsReady = relatedsReadyMap.get(processEntityId!); + const dispatch = useResolverDispatch(); + + /** + * If we don't have the related events for the parent yet, use this effect + * to request them. + */ + useEffect(() => { + if (typeof relatedsReady === 'undefined') { + dispatch({ + type: 'appDetectedMissingEventData', + payload: processEntityId, + }); + } + }, [relatedsReady, dispatch, processEntityId]); + + const relatedEventsForThisProcess = useSelector(selectors.relatedEventsByEntityId).get( + processEntityId! + ); + + const [relatedEventToShowDetailsFor, countBySameCategory, relatedEventCategory] = useMemo(() => { + if (!relatedEventsForThisProcess) { + return [undefined, 0]; + } + const specificEvent = relatedEventsForThisProcess.events.find( + (evt) => event.eventId(evt) === relatedEventId + ); + // For breadcrumbs: + const specificCategory = specificEvent && event.primaryEventCategory(specificEvent); + const countOfCategory = relatedEventsForThisProcess.events.reduce((sumtotal, evt) => { + return event.primaryEventCategory(evt) === specificCategory ? sumtotal + 1 : sumtotal; + }, 0); + return [specificEvent, countOfCategory, specificCategory || naString]; + }, [relatedEventsForThisProcess, naString, relatedEventId]); + + const [sections, formattedDate] = useMemo(() => { + if (!relatedEventToShowDetailsFor) { + // This could happen if user relaods from URL param and requests an eventId that no longer exists + return [[], naString]; + } + // Assuming these details (agent, ecs, process) aren't as helpful, can revisit + const { + agent, + ecs, + process, + ...relevantData + } = relatedEventToShowDetailsFor as ResolverEvent & { + // Type this with various unknown keys so that ts will let us delete those keys + ecs: unknown; + process: unknown; + }; + let displayDate = ''; + const sectionData: Array<{ + sectionTitle: string; + entries: Array<{ title: string; description: string }>; + }> = Object.entries(relevantData) + .map(([sectionTitle, val]) => { + if (sectionTitle === '@timestamp') { + displayDate = formatDate(val); + return { sectionTitle: '', entries: [] }; + } + if (typeof val !== 'object') { + return { sectionTitle, entries: [{ title: sectionTitle, description: `${val}` }] }; + } + return { sectionTitle, entries: [...objectToDescriptionListEntries(val)] }; + }) + .filter((v) => v.sectionTitle !== '' && v.entries.length); + return [sectionData, displayDate]; + }, [relatedEventToShowDetailsFor, naString]); + + const waitCrumbs = useMemo(() => { + return [ + { + text: eventsString, + onClick: () => { + pushToQueryParams({ crumbId: '', crumbEvent: '' }); + }, + }, + ]; + }, [pushToQueryParams, eventsString]); + + const { subject = '', descriptor = '' } = relatedEventToShowDetailsFor + ? event.descriptiveName(relatedEventToShowDetailsFor) + : {}; + const crumbs = useMemo(() => { + return [ + { + text: eventsString, + onClick: () => { + pushToQueryParams({ crumbId: '', crumbEvent: '' }); + }, + }, + { + text: processName, + onClick: () => { + pushToQueryParams({ crumbId: processEntityId!, crumbEvent: '' }); + }, + }, + { + text: ( + <> + + + ), + onClick: () => { + pushToQueryParams({ crumbId: processEntityId!, crumbEvent: 'all' }); + }, + }, + { + text: ( + <> + + + ), + onClick: () => { + pushToQueryParams({ + crumbId: processEntityId!, + crumbEvent: relatedEventCategory || 'all', + }); + }, + }, + { + text: relatedEventToShowDetailsFor ? ( + + ) : ( + naString + ), + onClick: () => {}, + }, + ]; + }, [ + processName, + processEntityId, + eventsString, + pushToQueryParams, + totalCount, + countBySameCategory, + naString, + relatedEventCategory, + relatedEventToShowDetailsFor, + subject, + descriptor, + ]); + + /** + * If the ship hasn't come in yet, wait on the dock + */ + if (!relatedsReady) { + const waitingString = i18n.translate( + 'xpack.securitySolution.endpoint.resolver.panel.relatedDetail.wait', + { + defaultMessage: 'Waiting For Events...', + } + ); + return ( + <> + + + +

{waitingString}

+
+ + ); + } + + /** + * Could happen if user e.g. loads a URL with a bad crumbEvent + */ + if (!relatedEventToShowDetailsFor) { + const errString = i18n.translate( + 'xpack.securitySolution.endpoint.resolver.panel.relatedDetail.missing', + { + defaultMessage: 'Related event not found.', + } + ); + return ( + + ); + } + + return ( + <> + + + + + + + + + + + + + + + + {sections.map(({ sectionTitle, entries }, index) => { + return ( + + {index === 0 ? null : } + + + + {sectionTitle} + + + + + + + {index === sections.length - 1 ? null : } + + ); + })} + + ); +}); diff --git a/x-pack/plugins/security_solution/public/resolver/view/styles.tsx b/x-pack/plugins/security_solution/public/resolver/view/styles.tsx index 4cdb29b283f1e..dfc2f970f1e6f 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/styles.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/styles.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import styled from 'styled-components'; -import { Panel } from './panel'; +import { Panel } from './panels'; /** * The top level DOM element for Resolver From ccf8e2b045869f0274534fd6350b896055e8313f Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 6 Aug 2020 22:45:55 +0200 Subject: [PATCH 05/19] [uptime] Ping Redirects (#65292) --- .../uptime/common/runtime_types/ping/ping.ts | 2 +- .../__snapshots__/expanded_row.test.tsx.snap | 26 +++-- .../monitor/ping_list/expanded_row.tsx | 8 +- .../monitor/ping_list/ping_list.tsx | 1 + .../monitor/ping_list/ping_redirects.tsx | 97 +++++++++++++++++++ .../status_bar/monitor_redirects.tsx | 58 +++++++++++ .../status_details/status_bar/status_bar.tsx | 2 + .../__tests__/get_latest_monitor.test.ts | 2 +- .../server/lib/requests/get_latest_monitor.ts | 2 +- .../rest/fixtures/monitor_latest_status.json | 28 +++++- .../functional/apps/uptime/certificates.ts | 2 +- x-pack/test/functional/apps/uptime/index.ts | 4 + .../test/functional/apps/uptime/locations.ts | 5 +- .../functional/apps/uptime/ping_redirects.ts | 74 ++++++++++++++ .../functional/page_objects/uptime_page.ts | 3 +- .../services/uptime/certificates.ts | 8 -- .../test/functional/services/uptime/common.ts | 8 ++ .../functional/services/uptime/monitor.ts | 28 +++++- 18 files changed, 332 insertions(+), 26 deletions(-) create mode 100644 x-pack/plugins/uptime/public/components/monitor/ping_list/ping_redirects.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/monitor_redirects.tsx create mode 100644 x-pack/test/functional/apps/uptime/ping_redirects.ts diff --git a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts index 5ed71acaf7739..0a4d6310927c4 100644 --- a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts +++ b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts @@ -143,7 +143,7 @@ export const PingType = t.intersection([ response: t.partial({ body: HttpResponseBodyType, bytes: t.number, - redirects: t.string, + redirects: t.array(t.string), status_code: t.number, }), version: t.string, diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/expanded_row.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/expanded_row.test.tsx.snap index 004de391a51a4..11bdf134bd0e8 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/expanded_row.test.tsx.snap +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/expanded_row.test.tsx.snap @@ -1,7 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`PingListExpandedRow doesn't render list items if the body field is undefined 1`] = ` - + + + + +
+ { }); } return ( - + + {ping?.http?.response?.redirects && ( + + + + )} diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx index 576810bba24fd..09782c1b76edb 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx @@ -237,6 +237,7 @@ export const PingListComponent = (props: Props) => { render: (item: Ping) => { return ( toggleDetails(item, expandedRows, setExpandedRows)} disabled={!item.error && !(item.http?.response?.body?.bytes ?? 0 > 0)} aria-label={ diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_redirects.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_redirects.tsx new file mode 100644 index 0000000000000..b3e59615cbce5 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_redirects.tsx @@ -0,0 +1,97 @@ +/* + * 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 styled from 'styled-components'; +import { EuiListGroup, EuiListGroupItemProps, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui'; +import { Ping } from '../../../../common/runtime_types/ping'; + +const ListGroup = styled(EuiListGroup)` + &&& { + a { + padding-left: 0; + } + } +`; + +interface Props { + monitorStatus: Ping | null; + showTitle?: boolean; +} + +export const PingRedirects: React.FC = ({ monitorStatus, showTitle }) => { + const monitorUrl = monitorStatus?.url?.full; + + const list = monitorStatus?.http?.response?.redirects; + + const listOfRedirects: EuiListGroupItemProps[] = [ + { + label: monitorUrl, + href: monitorUrl, + iconType: 'globe', + size: 's', + target: '_blank', + extraAction: { + color: 'subdued', + iconType: 'popout', + iconSize: 's', + alwaysShow: true, + 'aria-label': i18n.translate('xpack.uptime.monitorList.redirects.openWindow', { + defaultMessage: 'Link will open in new window.', + }), + }, + }, + ]; + + (list ?? []).forEach((url: string) => { + listOfRedirects.push({ + label: url, + href: url, + iconType: 'sortDown', + size: 's', + target: '_blank', + extraAction: { + color: 'subdued', + iconType: 'popout', + iconSize: 's', + 'aria-label': i18n.translate('xpack.uptime.monitorList.redirects.openWindow', { + defaultMessage: 'Link will open in new window.', + }), + alwaysShow: true, + }, + }); + }); + + const Panel = showTitle ? EuiPanel : 'div'; + + return list ? ( + + {showTitle && ( + +

+ {i18n.translate('xpack.uptime.monitorList.redirects.title', { + defaultMessage: 'Redirects', + })} +

+
+ )} + + { + + {i18n.translate('xpack.uptime.monitorList.redirects.description', { + defaultMessage: 'Heartbeat followed {number} redirects while executing ping.', + values: { + number: list?.length ?? 0, + }, + })} + + } + + +
+ ) : null; +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/monitor_redirects.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/monitor_redirects.tsx new file mode 100644 index 0000000000000..5129db9c2135b --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/monitor_redirects.tsx @@ -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 React, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiPopover } from '@elastic/eui'; +import styled from 'styled-components'; +import { Ping } from '../../../../../common/runtime_types'; +import { PingRedirects } from '../../ping_list/ping_redirects'; +import { MonListDescription, MonListTitle } from './status_bar'; + +interface Props { + monitorStatus: Ping | null; +} + +const RedirectBtn = styled.span` + cursor: pointer; +`; + +export const MonitorRedirects: React.FC = ({ monitorStatus }) => { + const list = monitorStatus?.http?.response?.redirects; + + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + const button = ( + + setIsPopoverOpen(!isPopoverOpen)} + data-test-subj="uptimeMonitorRedirectInfo" + > + {i18n.translate('xpack.uptime.monitorList.redirects.title.number', { + defaultMessage: '{number}', + values: { + number: list?.length ?? 0, + }, + })} + + + ); + + return list ? ( + <> + Redirects + setIsPopoverOpen(false)} + > + + + + ) : 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 index afcc8fae7a8ac..4ea383567d71c 100644 --- 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 @@ -23,6 +23,7 @@ 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'; +import { MonitorRedirects } from './monitor_redirects'; export const MonListTitle = styled(EuiDescriptionListTitle)` &&& { @@ -76,6 +77,7 @@ export const MonitorStatusBar: React.FC = () => { {MonitorIDLabel} {monitorId} +
); diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts index 01384ec145236..669033fc6524a 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts @@ -32,7 +32,7 @@ describe('getLatestMonitor', () => { }, }, size: 1, - _source: ['url', 'monitor', 'observer', '@timestamp', 'tls.*'], + _source: ['url', 'monitor', 'observer', '@timestamp', 'tls.*', 'http'], sort: { '@timestamp': { order: 'desc' }, }, diff --git a/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts b/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts index a58208fc2bb96..3b4aeaf92c508 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts @@ -45,7 +45,7 @@ export const getLatestMonitor: UMElasticsearchQueryFn { }); it('can navigate to cert page', async () => { - await uptimeService.cert.isUptimeDataMissing(); + await uptimeService.common.waitUntilDataIsLoaded(); await uptimeService.cert.hasViewCertButton(); await uptimeService.navigation.goToCertificates(); }); diff --git a/x-pack/test/functional/apps/uptime/index.ts b/x-pack/test/functional/apps/uptime/index.ts index 6b2b61cba2b64..261f685eeb9cc 100644 --- a/x-pack/test/functional/apps/uptime/index.ts +++ b/x-pack/test/functional/apps/uptime/index.ts @@ -56,6 +56,10 @@ export default ({ loadTestFile, getService }: FtrProviderContext) => { loadTestFile(require.resolve('./certificates')); }); + describe('with generated data but no data reset', () => { + loadTestFile(require.resolve('./ping_redirects')); + }); + describe('with real-world data', () => { before(async () => { await esArchiver.unload(ARCHIVE); diff --git a/x-pack/test/functional/apps/uptime/locations.ts b/x-pack/test/functional/apps/uptime/locations.ts index 8aefca6a70195..6bfa19c6ef578 100644 --- a/x-pack/test/functional/apps/uptime/locations.ts +++ b/x-pack/test/functional/apps/uptime/locations.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import moment from 'moment'; import { makeChecksWithStatus } from '../../../api_integration/apis/uptime/rest/helper/make_checks'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -40,8 +39,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }; describe('Observer location', () => { - const start = moment().subtract('15', 'm').toISOString(); - const end = moment().toISOString(); + const start = '~ 15 minutes ago'; + const end = 'now'; before(async () => { await addMonitorWithNoLocation(); diff --git a/x-pack/test/functional/apps/uptime/ping_redirects.ts b/x-pack/test/functional/apps/uptime/ping_redirects.ts new file mode 100644 index 0000000000000..b87e8c1748c82 --- /dev/null +++ b/x-pack/test/functional/apps/uptime/ping_redirects.ts @@ -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 { makeChecksWithStatus } from '../../../api_integration/apis/uptime/rest/helper/make_checks'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const { uptime: uptimePage, header } = getPageObjects(['uptime', 'header']); + const uptime = getService('uptime'); + const esArchiver = getService('esArchiver'); + + const archive = 'uptime/blank'; + + const monitor = () => uptime.monitor; + + describe('Ping redirects', () => { + const start = '~ 15 minutes ago'; + const end = 'now'; + + const MONITOR_ID = 'redirect-testing-id'; + + before(async () => { + await esArchiver.loadIfNeeded(archive); + }); + + after('unload', async () => { + await esArchiver.unload(archive); + }); + + beforeEach(async () => { + await makeChecksWithStatus( + getService('legacyEs'), + MONITOR_ID, + 5, + 2, + 10000, + { + http: { + rtt: { total: { us: 157784 } }, + response: { + status_code: 200, + redirects: ['http://localhost:3000/first', 'https://www.washingtonpost.com/'], + body: { + bytes: 642102, + hash: '597a8cfb33ff8e09bff16283306553c3895282aaf5386e1843d466d44979e28a', + }, + }, + }, + }, + 'up' + ); + await delay(1000); + }); + + it('loads and goes to details page', async () => { + await uptime.navigation.goToUptime(); + await uptimePage.loadDataAndGoToMonitorPage(start, end, MONITOR_ID); + }); + + it('display redirect info in detail panel', async () => { + await header.waitUntilLoadingHasFinished(); + await monitor().hasRedirectInfo(); + }); + + it('displays redirects in ping list expand row', async () => { + await monitor().hasRedirectInfoInPingList(); + }); + }); +}; diff --git a/x-pack/test/functional/page_objects/uptime_page.ts b/x-pack/test/functional/page_objects/uptime_page.ts index 074a2d598be8a..8102d8b95680e 100644 --- a/x-pack/test/functional/page_objects/uptime_page.ts +++ b/x-pack/test/functional/page_objects/uptime_page.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../ftr_provider_context'; export function UptimePageProvider({ getPageObjects, getService }: FtrProviderContext) { - const pageObjects = getPageObjects(['common', 'timePicker']); + const pageObjects = getPageObjects(['common', 'timePicker', 'header']); const { common: commonService, monitor, navigation } = getService('uptime'); const retry = getService('retry'); @@ -42,6 +42,7 @@ export function UptimePageProvider({ getPageObjects, getService }: FtrProviderCo } public async loadDataAndGoToMonitorPage(dateStart: string, dateEnd: string, monitorId: string) { + await pageObjects.header.waitUntilLoadingHasFinished(); await this.setDateRange(dateStart, dateEnd); await navigation.goToMonitor(monitorId); } diff --git a/x-pack/test/functional/services/uptime/certificates.ts b/x-pack/test/functional/services/uptime/certificates.ts index 06de9be5af7e9..ab43604786282 100644 --- a/x-pack/test/functional/services/uptime/certificates.ts +++ b/x-pack/test/functional/services/uptime/certificates.ts @@ -24,14 +24,6 @@ export function UptimeCertProvider({ getService, getPageObjects }: FtrProviderCo }; return { - async isUptimeDataMissing() { - return retry.tryForTime(60 * 1000, async () => { - if (await testSubjects.exists('data-missing', { timeout: 0 })) { - await refreshApp(); - } - await testSubjects.missingOrFail('data-missing'); - }); - }, async hasViewCertButton() { return retry.tryForTime(15000, async () => { await testSubjects.existOrFail('uptimeCertificatesLink'); diff --git a/x-pack/test/functional/services/uptime/common.ts b/x-pack/test/functional/services/uptime/common.ts index 5f544b5e46010..13c9ead89d09d 100644 --- a/x-pack/test/functional/services/uptime/common.ts +++ b/x-pack/test/functional/services/uptime/common.ts @@ -91,5 +91,13 @@ export function UptimeCommonProvider({ getService }: FtrProviderContext) { 5000 ); }, + async waitUntilDataIsLoaded() { + return retry.tryForTime(60 * 1000, async () => { + if (await testSubjects.exists('data-missing')) { + await testSubjects.click('superDatePickerApplyTimeButton'); + } + await testSubjects.missingOrFail('data-missing'); + }); + }, }; } diff --git a/x-pack/test/functional/services/uptime/monitor.ts b/x-pack/test/functional/services/uptime/monitor.ts index 593950fbb7619..c45454e731696 100644 --- a/x-pack/test/functional/services/uptime/monitor.ts +++ b/x-pack/test/functional/services/uptime/monitor.ts @@ -7,11 +7,13 @@ import expect from '@kbn/expect/expect.js'; import { FtrProviderContext } from '../../ftr_provider_context'; -export function UptimeMonitorProvider({ getService }: FtrProviderContext) { +export function UptimeMonitorProvider({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const retry = getService('retry'); const find = getService('find'); + const PageObjects = getPageObjects(['header']); + return { async locationMissingExists() { return await testSubjects.existOrFail('xpack.uptime.locationMap.locationMissing', { @@ -56,5 +58,29 @@ export function UptimeMonitorProvider({ getService }: FtrProviderContext) { async toggleToMapView() { await testSubjects.click('uptimeMonitorToggleMapBtn'); }, + async hasRedirectInfo() { + return retry.tryForTime(30000, async () => { + await testSubjects.existOrFail('uptimeMonitorRedirectInfo'); + }); + }, + async expandPingRow() { + return retry.tryForTime( + 60 * 3000, + async () => { + await testSubjects.existOrFail('uptimePingListExpandBtn', { timeout: 5000 }); + await testSubjects.click('uptimePingListExpandBtn'); + }, + async () => { + await testSubjects.click('superDatePickerApplyTimeButton'); + await PageObjects.header.waitUntilLoadingHasFinished(); + } + ); + }, + async hasRedirectInfoInPingList() { + await this.expandPingRow(); + return retry.tryForTime(60 * 1000, async () => { + await testSubjects.existOrFail('uptimeMonitorPingListRedirectInfo'); + }); + }, }; } From 979bdaa56f4aa42f9e962cb803133aaea80d6977 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Thu, 6 Aug 2020 19:37:38 -0400 Subject: [PATCH 06/19] [Security Solution][Tech Debt] - Cleans up error formatter to not return duplicate error messages (#74600) ## Summary Using the `formatErrors` util would result in duplicate error messages sometimes. Was noticing this in particular when using union types, where the type validation would check every item in a union and report an error for each one. This resulted in large, repeating errors. Used `uniq` to filter out duplicates. Updated unit tests. --- .../lists/common/schemas/types/comment.test.ts | 3 --- .../schemas/types/default_comments_array.test.ts | 2 -- .../types/default_update_comments_array.test.ts | 2 -- .../lists/common/schemas/types/entries.test.ts | 7 ------- .../types/non_empty_entries_array.test.ts | 4 ---- .../types/non_empty_nested_entries_array.test.ts | 15 --------------- .../common/schemas/types/update_comment.test.ts | 2 -- .../request/create_rules_bulk_schema.test.ts | 1 - .../request/update_rules_bulk_schema.test.ts | 1 - .../common/format_errors.test.ts | 16 ++++++++++++++++ .../security_solution/common/format_errors.ts | 4 +++- .../routes/export_timelines_route.test.ts | 2 +- .../routes/import_timelines_route.test.ts | 10 ++-------- 13 files changed, 22 insertions(+), 47 deletions(-) diff --git a/x-pack/plugins/lists/common/schemas/types/comment.test.ts b/x-pack/plugins/lists/common/schemas/types/comment.test.ts index 081bb9b4bae54..9b6f0e76bdd54 100644 --- a/x-pack/plugins/lists/common/schemas/types/comment.test.ts +++ b/x-pack/plugins/lists/common/schemas/types/comment.test.ts @@ -60,7 +60,6 @@ describe('Comment', () => { expect(getPaths(left(message.errors))).toEqual([ 'Invalid value "undefined" supplied to "({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)"', - 'Invalid value "undefined" supplied to "({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)"', ]); expect(message.schema).toEqual({}); }); @@ -200,7 +199,6 @@ describe('Comment', () => { expect(getPaths(left(message.errors))).toEqual([ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', - 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', ]); expect(message.schema).toEqual({}); }); @@ -232,7 +230,6 @@ describe('Comment', () => { expect(getPaths(left(message.errors))).toEqual([ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', - 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', ]); expect(message.schema).toEqual({}); }); diff --git a/x-pack/plugins/lists/common/schemas/types/default_comments_array.test.ts b/x-pack/plugins/lists/common/schemas/types/default_comments_array.test.ts index ee2dc0cf2a478..0f5ed2ee4a98b 100644 --- a/x-pack/plugins/lists/common/schemas/types/default_comments_array.test.ts +++ b/x-pack/plugins/lists/common/schemas/types/default_comments_array.test.ts @@ -39,7 +39,6 @@ describe('default_comments_array', () => { expect(getPaths(left(message.errors))).toEqual([ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', - 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', ]); expect(message.schema).toEqual({}); }); @@ -51,7 +50,6 @@ describe('default_comments_array', () => { expect(getPaths(left(message.errors))).toEqual([ 'Invalid value "some string" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', - 'Invalid value "some string" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', ]); expect(message.schema).toEqual({}); }); diff --git a/x-pack/plugins/lists/common/schemas/types/default_update_comments_array.test.ts b/x-pack/plugins/lists/common/schemas/types/default_update_comments_array.test.ts index 25c84af8c9ee3..a0f6a2b2a6eaa 100644 --- a/x-pack/plugins/lists/common/schemas/types/default_update_comments_array.test.ts +++ b/x-pack/plugins/lists/common/schemas/types/default_update_comments_array.test.ts @@ -39,7 +39,6 @@ describe('default_update_comments_array', () => { expect(getPaths(left(message.errors))).toEqual([ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', - 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', ]); expect(message.schema).toEqual({}); }); @@ -51,7 +50,6 @@ describe('default_update_comments_array', () => { expect(getPaths(left(message.errors))).toEqual([ 'Invalid value "some string" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', - 'Invalid value "some string" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', ]); expect(message.schema).toEqual({}); }); diff --git a/x-pack/plugins/lists/common/schemas/types/entries.test.ts b/x-pack/plugins/lists/common/schemas/types/entries.test.ts index f5c022c7a394f..0537b0b9c6c6a 100644 --- a/x-pack/plugins/lists/common/schemas/types/entries.test.ts +++ b/x-pack/plugins/lists/common/schemas/types/entries.test.ts @@ -61,17 +61,10 @@ describe('Entries', () => { const message = pipe(decoded, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "operator"', - 'Invalid value "nested" supplied to "type"', - 'Invalid value "undefined" supplied to "value"', 'Invalid value "undefined" supplied to "operator"', 'Invalid value "nested" supplied to "type"', 'Invalid value "undefined" supplied to "value"', 'Invalid value "undefined" supplied to "list"', - 'Invalid value "undefined" supplied to "operator"', - 'Invalid value "nested" supplied to "type"', - 'Invalid value "undefined" supplied to "operator"', - 'Invalid value "nested" supplied to "type"', ]); expect(message.schema).toEqual({}); }); diff --git a/x-pack/plugins/lists/common/schemas/types/non_empty_entries_array.test.ts b/x-pack/plugins/lists/common/schemas/types/non_empty_entries_array.test.ts index 42d476a9fefb2..d81509d080056 100644 --- a/x-pack/plugins/lists/common/schemas/types/non_empty_entries_array.test.ts +++ b/x-pack/plugins/lists/common/schemas/types/non_empty_entries_array.test.ts @@ -125,10 +125,6 @@ describe('non_empty_entries_array', () => { expect(getPaths(left(message.errors))).toEqual([ 'Invalid value "1" supplied to "NonEmptyEntriesArray"', - 'Invalid value "1" supplied to "NonEmptyEntriesArray"', - 'Invalid value "1" supplied to "NonEmptyEntriesArray"', - 'Invalid value "1" supplied to "NonEmptyEntriesArray"', - 'Invalid value "1" supplied to "NonEmptyEntriesArray"', ]); expect(message.schema).toEqual({}); }); diff --git a/x-pack/plugins/lists/common/schemas/types/non_empty_nested_entries_array.test.ts b/x-pack/plugins/lists/common/schemas/types/non_empty_nested_entries_array.test.ts index 7dbc3465610c0..2e54590368959 100644 --- a/x-pack/plugins/lists/common/schemas/types/non_empty_nested_entries_array.test.ts +++ b/x-pack/plugins/lists/common/schemas/types/non_empty_nested_entries_array.test.ts @@ -86,19 +86,6 @@ describe('non_empty_nested_entries_array', () => { 'Invalid value "undefined" supplied to "operator"', 'Invalid value "nested" supplied to "type"', 'Invalid value "undefined" supplied to "value"', - 'Invalid value "undefined" supplied to "operator"', - 'Invalid value "nested" supplied to "type"', - 'Invalid value "undefined" supplied to "value"', - 'Invalid value "undefined" supplied to "operator"', - 'Invalid value "nested" supplied to "type"', - 'Invalid value "undefined" supplied to "operator"', - 'Invalid value "nested" supplied to "type"', - 'Invalid value "undefined" supplied to "value"', - 'Invalid value "undefined" supplied to "operator"', - 'Invalid value "nested" supplied to "type"', - 'Invalid value "undefined" supplied to "value"', - 'Invalid value "undefined" supplied to "operator"', - 'Invalid value "nested" supplied to "type"', ]); expect(message.schema).toEqual({}); }); @@ -123,8 +110,6 @@ describe('non_empty_nested_entries_array', () => { expect(getPaths(left(message.errors))).toEqual([ 'Invalid value "1" supplied to "NonEmptyNestedEntriesArray"', - 'Invalid value "1" supplied to "NonEmptyNestedEntriesArray"', - 'Invalid value "1" supplied to "NonEmptyNestedEntriesArray"', ]); expect(message.schema).toEqual({}); }); diff --git a/x-pack/plugins/lists/common/schemas/types/update_comment.test.ts b/x-pack/plugins/lists/common/schemas/types/update_comment.test.ts index ac4d0304cbb8e..ba07421fe60f4 100644 --- a/x-pack/plugins/lists/common/schemas/types/update_comment.test.ts +++ b/x-pack/plugins/lists/common/schemas/types/update_comment.test.ts @@ -110,7 +110,6 @@ describe('CommentsUpdate', () => { expect(getPaths(left(message.errors))).toEqual([ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', - 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', ]); expect(message.schema).toEqual({}); }); @@ -142,7 +141,6 @@ describe('CommentsUpdate', () => { expect(getPaths(left(message.errors))).toEqual([ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', - 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', ]); expect(message.schema).toEqual({}); }); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_bulk_schema.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_bulk_schema.test.ts index 00854f1ed5526..d335cafdb7885 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_bulk_schema.test.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_bulk_schema.test.ts @@ -127,7 +127,6 @@ describe('create_rules_bulk_schema', () => { const output = foldLeftRight(checked); expect(formatErrors(output.errors)).toEqual([ 'Invalid value "undefined" supplied to "risk_score"', - 'Invalid value "undefined" supplied to "risk_score"', ]); expect(output.schema).toEqual({}); }); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/update_rules_bulk_schema.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/update_rules_bulk_schema.test.ts index 4cb38889045fc..33a22d9a5f805 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/update_rules_bulk_schema.test.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/update_rules_bulk_schema.test.ts @@ -123,7 +123,6 @@ describe('update_rules_bulk_schema', () => { const output = foldLeftRight(checked); expect(formatErrors(output.errors)).toEqual([ 'Invalid value "undefined" supplied to "risk_score"', - 'Invalid value "undefined" supplied to "risk_score"', ]); expect(output.schema).toEqual({}); }); diff --git a/x-pack/plugins/security_solution/common/format_errors.test.ts b/x-pack/plugins/security_solution/common/format_errors.test.ts index c8cd72b72816b..06bdf67097764 100644 --- a/x-pack/plugins/security_solution/common/format_errors.test.ts +++ b/x-pack/plugins/security_solution/common/format_errors.test.ts @@ -41,6 +41,22 @@ describe('utils', () => { expect(output).toEqual(['some error 1', 'some error 2']); }); + test('it filters out duplicate error messages', () => { + const validationError1: t.ValidationError = { + value: 'Some existing error 1', + context: [], + message: 'some error 1', + }; + const validationError2: t.ValidationError = { + value: 'Some existing error 1', + context: [], + message: 'some error 1', + }; + const errors: t.Errors = [validationError1, validationError2]; + const output = formatErrors(errors); + expect(output).toEqual(['some error 1']); + }); + test('will use message before context if it is set', () => { const context: t.Context = ([{ key: 'some string key' }] as unknown) as t.Context; const validationError1: t.ValidationError = { diff --git a/x-pack/plugins/security_solution/common/format_errors.ts b/x-pack/plugins/security_solution/common/format_errors.ts index ba963f34f2983..4e1f5e4796152 100644 --- a/x-pack/plugins/security_solution/common/format_errors.ts +++ b/x-pack/plugins/security_solution/common/format_errors.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; import { isObject } from 'lodash/fp'; export const formatErrors = (errors: t.Errors): string[] => { - return errors.map((error) => { + const err = errors.map((error) => { if (error.message != null) { return error.message; } else { @@ -26,4 +26,6 @@ export const formatErrors = (errors: t.Errors): string[] => { return `Invalid value "${value}" supplied to "${suppliedValue}"`; } }); + + return [...new Set(err)]; }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/export_timelines_route.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/export_timelines_route.test.ts index a6f0ce232fa7b..5a976ee7521af 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/export_timelines_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/export_timelines_route.test.ts @@ -110,7 +110,7 @@ describe('export timelines', () => { const result = server.validate(request); expect(result.badRequest.mock.calls[0][0]).toEqual( - 'Invalid value "someId" supplied to "ids",Invalid value "someId" supplied to "ids",Invalid value "{"ids":"someId"}" supplied to "(Partial<{ ids: (Array | null) }> | null)"' + 'Invalid value "someId" supplied to "ids",Invalid value "{"ids":"someId"}" supplied to "(Partial<{ ids: (Array | null) }> | null)"' ); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.test.ts index 2ad6c5d6fff60..ff76045db90cb 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.test.ts @@ -494,10 +494,7 @@ describe('import timelines', () => { const result = server.validate(request); expect(result.badRequest).toHaveBeenCalledWith( - [ - 'Invalid value "undefined" supplied to "file"', - 'Invalid value "undefined" supplied to "file"', - ].join(',') + 'Invalid value "undefined" supplied to "file"' ); }); }); @@ -923,10 +920,7 @@ describe('import timeline templates', () => { const result = server.validate(request); expect(result.badRequest).toHaveBeenCalledWith( - [ - 'Invalid value "undefined" supplied to "file"', - 'Invalid value "undefined" supplied to "file"', - ].join(',') + 'Invalid value "undefined" supplied to "file"' ); }); }); From fbd79ea72677bb4d7aca1c5fc809c2f710e05071 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Thu, 6 Aug 2020 19:41:18 -0400 Subject: [PATCH 07/19] skip query of detections page when we do not have .siem-signals index (#74580) * skip query of detections page when we do not have .siem-signals index * review I --- .../timelines/containers/helpers.test.ts | 54 +++++++++++++++++++ .../public/timelines/containers/helpers.ts | 17 ++++++ .../public/timelines/containers/index.tsx | 7 ++- 3 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/timelines/containers/helpers.test.ts create mode 100644 x-pack/plugins/security_solution/public/timelines/containers/helpers.ts diff --git a/x-pack/plugins/security_solution/public/timelines/containers/helpers.test.ts b/x-pack/plugins/security_solution/public/timelines/containers/helpers.test.ts new file mode 100644 index 0000000000000..043b5fe39a8bf --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/containers/helpers.test.ts @@ -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 { TimelineId } from '../../../common/types/timeline'; +import { skipQueryForDetectionsPage } from './helpers'; + +describe('skipQueryForDetectionsPage', () => { + test('Make sure to NOT skip the query when it is not a timeline from a detection pages', () => { + expect(skipQueryForDetectionsPage(TimelineId.active, ['auditbeat-*', 'filebeat-*'])).toBe( + false + ); + expect( + skipQueryForDetectionsPage(TimelineId.hostsPageEvents, ['auditbeat-*', 'filebeat-*']) + ).toBe(false); + expect( + skipQueryForDetectionsPage(TimelineId.hostsPageExternalAlerts, ['auditbeat-*', 'filebeat-*']) + ).toBe(false); + expect( + skipQueryForDetectionsPage(TimelineId.networkPageExternalAlerts, [ + 'auditbeat-*', + 'filebeat-*', + ]) + ).toBe(false); + }); + + test('Make sure to SKIP the query when it is a timeline from a detection pages without the siem-signals', () => { + expect( + skipQueryForDetectionsPage(TimelineId.detectionsPage, ['auditbeat-*', 'filebeat-*']) + ).toBe(true); + expect( + skipQueryForDetectionsPage(TimelineId.detectionsRulesDetailsPage, [ + 'auditbeat-*', + 'filebeat-*', + ]) + ).toBe(true); + }); + + test('Make sure to NOT skip the query when it is a timeline from a detection pages with the siem-signals', () => { + expect( + skipQueryForDetectionsPage(TimelineId.detectionsPage, [ + 'auditbeat-*', + '.siem-signals-rainbow-butterfly', + ]) + ).toBe(false); + expect( + skipQueryForDetectionsPage(TimelineId.detectionsRulesDetailsPage, [ + '.siem-signals-rainbow-butterfly', + ]) + ).toBe(false); + }); +}); diff --git a/x-pack/plugins/security_solution/public/timelines/containers/helpers.ts b/x-pack/plugins/security_solution/public/timelines/containers/helpers.ts new file mode 100644 index 0000000000000..aef6f4df6f41b --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/containers/helpers.ts @@ -0,0 +1,17 @@ +/* + * 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 { TimelineId } from '../../../common/types/timeline'; + +export const detectionsTimelineIds = [ + TimelineId.detectionsPage, + TimelineId.detectionsRulesDetailsPage, +]; + +export const skipQueryForDetectionsPage = (id: string, defaultIndex: string[]) => + id != null && + detectionsTimelineIds.some((timelineId) => timelineId === id) && + !defaultIndex.some((di) => di.toLowerCase().startsWith('.siem-signals')); diff --git a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx index 562999108b4b0..de7175f0a7f97 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx @@ -11,7 +11,6 @@ import { Query } from 'react-apollo'; import { compose, Dispatch } from 'redux'; import { connect, ConnectedProps } from 'react-redux'; -import { TimelineId } from '../../../common/types/timeline'; import { DEFAULT_INDEX_KEY } from '../../../common/constants'; import { IIndexPattern } from '../../../../../../src/plugins/data/common/index_patterns'; import { @@ -28,8 +27,7 @@ import { QueryTemplate, QueryTemplateProps } from '../../common/containers/query import { EventType } from '../../timelines/store/timeline/model'; import { timelineQuery } from './index.gql_query'; import { timelineActions } from '../../timelines/store/timeline'; - -const timelineIds = [TimelineId.detectionsPage, TimelineId.detectionsRulesDetailsPage]; +import { detectionsTimelineIds, skipQueryForDetectionsPage } from './helpers'; export interface TimelineArgs { events: TimelineItem[]; @@ -130,6 +128,7 @@ class TimelineQueryComponent extends QueryTemplate< query={timelineQuery} fetchPolicy="network-only" notifyOnNetworkStatusChange + skip={skipQueryForDetectionsPage(id, defaultIndex)} variables={variables} > {({ data, loading, fetchMore, refetch }) => { @@ -202,7 +201,7 @@ const makeMapStateToProps = () => { const mapDispatchToProps = (dispatch: Dispatch) => ({ clearSignalsState: ({ id }: { id?: string }) => { - if (id != null && timelineIds.some((timelineId) => timelineId === id)) { + if (id != null && detectionsTimelineIds.some((timelineId) => timelineId === id)) { dispatch(timelineActions.clearEventsLoading({ id })); dispatch(timelineActions.clearEventsDeleted({ id })); } From 5d9f329a36d8be7fe8601498beadb1e8f7cfd072 Mon Sep 17 00:00:00 2001 From: Sandra Gonzales Date: Fri, 7 Aug 2020 08:03:13 -0500 Subject: [PATCH 08/19] [Ingest Manager] Integration tests for updating a package (#74593) * add integration tests for updating a package's assets * update to update tests and change to dataset to data_stream * add datastream test --- .../services/epm/elasticsearch/ilm/install.ts | 1 - .../apis/epm/data_stream.ts | 130 ++++++++ .../apis/epm/index.js | 2 + .../apis/epm/install_remove_assets.ts | 5 +- .../apis/epm/update_assets.ts | 299 ++++++++++++++++++ .../0.1.0/dataset/test_logs/fields/ecs.yml | 3 + .../0.1.0/dataset/test_metrics/fields/ecs.yml | 3 + .../visualization/sample_visualization.json | 2 +- .../elasticsearch/ilm_policy/all_assets.json | 15 + .../elasticsearch/ingest_pipeline/default.yml | 7 + .../0.2.0/dataset/test_logs/fields/ecs.yml | 6 + .../0.2.0/dataset/test_logs/fields/fields.yml | 16 + .../0.2.0/dataset/test_logs/manifest.yml | 9 + .../0.2.0/dataset/test_logs2/fields/ecs.yml | 3 + .../dataset/test_logs2/fields/fields.yml | 16 + .../0.2.0/dataset/test_logs2/manifest.yml | 3 + .../0.2.0/dataset/test_metrics/fields/ecs.yml | 3 + .../dataset/test_metrics/fields/fields.yml | 16 + .../0.2.0/dataset/test_metrics/manifest.yml | 3 + .../all_assets/0.2.0/docs/README.md | 3 + .../0.2.0/img/logo_overrides_64_color.svg | 7 + .../kibana/dashboard/sample_dashboard.json | 16 + .../0.2.0/kibana/search/sample_search2.json | 24 ++ .../visualization/sample_visualization.json | 11 + .../all_assets/0.2.0/manifest.yml | 20 ++ .../elasticsearch/ilm_policy/all_assets.json | 15 + .../elasticsearch/ingest_pipeline/default.yml | 7 + .../0.1.0/dataset/test_logs/fields/ecs.yml | 3 + .../0.1.0/dataset/test_logs/fields/fields.yml | 16 + .../0.1.0/dataset/test_logs/manifest.yml | 9 + .../0.1.0/dataset/test_metrics/fields/ecs.yml | 3 + .../dataset/test_metrics/fields/fields.yml | 16 + .../0.1.0/dataset/test_metrics/manifest.yml | 3 + .../datastreams/0.1.0/docs/README.md | 3 + .../datastreams/0.1.0/manifest.yml | 20 ++ .../elasticsearch/ilm_policy/all_assets.json | 15 + .../elasticsearch/ingest_pipeline/default.yml | 7 + .../0.2.0/dataset/test_logs/fields/ecs.yml | 6 + .../0.2.0/dataset/test_logs/fields/fields.yml | 16 + .../0.2.0/dataset/test_logs/manifest.yml | 9 + .../0.2.0/dataset/test_metrics/fields/ecs.yml | 6 + .../dataset/test_metrics/fields/fields.yml | 16 + .../0.2.0/dataset/test_metrics/manifest.yml | 3 + .../datastreams/0.2.0/docs/README.md | 3 + .../datastreams/0.2.0/manifest.yml | 20 ++ 45 files changed, 816 insertions(+), 3 deletions(-) create mode 100644 x-pack/test/ingest_manager_api_integration/apis/epm/data_stream.ts create mode 100644 x-pack/test/ingest_manager_api_integration/apis/epm/update_assets.ts create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_logs/fields/ecs.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_metrics/fields/ecs.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/fields/ecs.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/fields/fields.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/manifest.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs2/fields/ecs.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs2/fields/fields.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs2/manifest.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_metrics/fields/ecs.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_metrics/fields/fields.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_metrics/manifest.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/docs/README.md create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/img/logo_overrides_64_color.svg create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/dashboard/sample_dashboard.json create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/search/sample_search2.json create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/visualization/sample_visualization.json create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/manifest.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/fields/ecs.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/fields/fields.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/manifest.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_metrics/fields/ecs.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_metrics/fields/fields.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_metrics/manifest.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/docs/README.md create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/manifest.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/fields/ecs.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/fields/fields.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/manifest.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_metrics/fields/ecs.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_metrics/fields/fields.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_metrics/manifest.yml create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/docs/README.md create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/manifest.yml diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/ilm/install.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/ilm/install.ts index 9590167657d98..c5253e4902cab 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/ilm/install.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/ilm/install.ts @@ -16,7 +16,6 @@ export async function installILMPolicy(paths: string[], callCluster: CallESAsCur const { file } = Registry.pathParts(path); const name = file.substr(0, file.lastIndexOf('.')); try { - if (await policyExists(name, callCluster)) return; await callCluster('transport.request', { method: 'PUT', path: '/_ilm/policy/' + name, diff --git a/x-pack/test/ingest_manager_api_integration/apis/epm/data_stream.ts b/x-pack/test/ingest_manager_api_integration/apis/epm/data_stream.ts new file mode 100644 index 0000000000000..68a4812d4af40 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/epm/data_stream.ts @@ -0,0 +1,130 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { skipIfNoDockerRegistry } from '../../helpers'; + +export default function (providerContext: FtrProviderContext) { + const { getService } = providerContext; + const supertest = getService('supertest'); + const es = getService('es'); + const pkgName = 'datastreams'; + const pkgVersion = '0.1.0'; + const pkgUpdateVersion = '0.2.0'; + const pkgKey = `${pkgName}-${pkgVersion}`; + const pkgUpdateKey = `${pkgName}-${pkgUpdateVersion}`; + const logsTemplateName = `logs-${pkgName}.test_logs`; + const metricsTemplateName = `metrics-${pkgName}.test_metrics`; + + const uninstallPackage = async (pkg: string) => { + await supertest.delete(`/api/ingest_manager/epm/packages/${pkg}`).set('kbn-xsrf', 'xxxx'); + }; + const installPackage = async (pkg: string) => { + await supertest + .post(`/api/ingest_manager/epm/packages/${pkg}`) + .set('kbn-xsrf', 'xxxx') + .send({ force: true }); + }; + + describe('datastreams', async () => { + skipIfNoDockerRegistry(providerContext); + before(async () => { + await installPackage(pkgKey); + await es.transport.request({ + method: 'POST', + path: `/${logsTemplateName}-default/_doc`, + body: { + '@timestamp': '2015-01-01', + logs_test_name: 'test', + data_stream: { + dataset: `${pkgName}.test_logs`, + namespace: 'default', + type: 'logs', + }, + }, + }); + await es.transport.request({ + method: 'POST', + path: `/${metricsTemplateName}-default/_doc`, + body: { + '@timestamp': '2015-01-01', + logs_test_name: 'test', + data_stream: { + dataset: `${pkgName}.test_metrics`, + namespace: 'default', + type: 'metrics', + }, + }, + }); + }); + after(async () => { + await uninstallPackage(pkgUpdateKey); + await es.transport.request({ + method: 'DELETE', + path: `/_data_stream/${logsTemplateName}-default`, + }); + await es.transport.request({ + method: 'DELETE', + path: `/_data_stream/${metricsTemplateName}-default`, + }); + }); + describe('get datastreams after data sent', async () => { + skipIfNoDockerRegistry(providerContext); + let resLogsDatastream: any; + let resMetricsDatastream: any; + before(async () => { + resLogsDatastream = await es.transport.request({ + method: 'GET', + path: `/_data_stream/${logsTemplateName}-default`, + }); + resMetricsDatastream = await es.transport.request({ + method: 'GET', + path: `/_data_stream/${metricsTemplateName}-default`, + }); + }); + it('should list the logs datastream', async function () { + expect(resLogsDatastream.body.data_streams.length).equal(1); + expect(resLogsDatastream.body.data_streams[0].indices.length).equal(1); + expect(resLogsDatastream.body.data_streams[0].indices[0].index_name).equal( + `.ds-${logsTemplateName}-default-000001` + ); + }); + it('should list the metrics datastream', async function () { + expect(resMetricsDatastream.body.data_streams.length).equal(1); + expect(resMetricsDatastream.body.data_streams[0].indices.length).equal(1); + expect(resMetricsDatastream.body.data_streams[0].indices[0].index_name).equal( + `.ds-${metricsTemplateName}-default-000001` + ); + }); + }); + describe('rollover datastream when mappings are not compatible', async () => { + skipIfNoDockerRegistry(providerContext); + let resLogsDatastream: any; + let resMetricsDatastream: any; + before(async () => { + await installPackage(pkgUpdateKey); + resLogsDatastream = await es.transport.request({ + method: 'GET', + path: `/_data_stream/${logsTemplateName}-default`, + }); + resMetricsDatastream = await es.transport.request({ + method: 'GET', + path: `/_data_stream/${metricsTemplateName}-default`, + }); + }); + it('should have rolled over logs datastream', async function () { + expect(resLogsDatastream.body.data_streams[0].indices.length).equal(2); + expect(resLogsDatastream.body.data_streams[0].indices[1].index_name).equal( + `.ds-${logsTemplateName}-default-000002` + ); + }); + it('should have not rolled over metrics datastream', async function () { + expect(resMetricsDatastream.body.data_streams[0].indices.length).equal(1); + }); + }); + }); +} diff --git a/x-pack/test/ingest_manager_api_integration/apis/epm/index.js b/x-pack/test/ingest_manager_api_integration/apis/epm/index.js index 1582f72dd1cd8..0f32d2b4ae703 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/epm/index.js +++ b/x-pack/test/ingest_manager_api_integration/apis/epm/index.js @@ -13,5 +13,7 @@ export default function loadTests({ loadTestFile }) { loadTestFile(require.resolve('./install_overrides')); loadTestFile(require.resolve('./install_remove_assets')); loadTestFile(require.resolve('./install_update')); + loadTestFile(require.resolve('./update_assets')); + loadTestFile(require.resolve('./data_stream')); }); } diff --git a/x-pack/test/ingest_manager_api_integration/apis/epm/install_remove_assets.ts b/x-pack/test/ingest_manager_api_integration/apis/epm/install_remove_assets.ts index 35058de0684b2..03d0b6abb4802 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/epm/install_remove_assets.ts +++ b/x-pack/test/ingest_manager_api_integration/apis/epm/install_remove_assets.ts @@ -23,7 +23,10 @@ export default function (providerContext: FtrProviderContext) { await supertest.delete(`/api/ingest_manager/epm/packages/${pkg}`).set('kbn-xsrf', 'xxxx'); }; const installPackage = async (pkg: string) => { - await supertest.post(`/api/ingest_manager/epm/packages/${pkg}`).set('kbn-xsrf', 'xxxx'); + await supertest + .post(`/api/ingest_manager/epm/packages/${pkg}`) + .set('kbn-xsrf', 'xxxx') + .send({ force: true }); }; describe('installs and uninstalls all assets', async () => { diff --git a/x-pack/test/ingest_manager_api_integration/apis/epm/update_assets.ts b/x-pack/test/ingest_manager_api_integration/apis/epm/update_assets.ts new file mode 100644 index 0000000000000..59ad7a9744ae1 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/epm/update_assets.ts @@ -0,0 +1,299 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { skipIfNoDockerRegistry } from '../../helpers'; + +export default function (providerContext: FtrProviderContext) { + const { getService } = providerContext; + const kibanaServer = getService('kibanaServer'); + const supertest = getService('supertest'); + const es = getService('es'); + const pkgName = 'all_assets'; + const pkgVersion = '0.1.0'; + const pkgUpdateVersion = '0.2.0'; + const pkgKey = `${pkgName}-${pkgVersion}`; + const pkgUpdateKey = `${pkgName}-${pkgUpdateVersion}`; + const logsTemplateName = `logs-${pkgName}.test_logs`; + const logsTemplateName2 = `logs-${pkgName}.test_logs2`; + const metricsTemplateName = `metrics-${pkgName}.test_metrics`; + + const uninstallPackage = async (pkg: string) => { + await supertest.delete(`/api/ingest_manager/epm/packages/${pkg}`).set('kbn-xsrf', 'xxxx'); + }; + const installPackage = async (pkg: string) => { + await supertest + .post(`/api/ingest_manager/epm/packages/${pkg}`) + .set('kbn-xsrf', 'xxxx') + .send({ force: true }); + }; + + describe('updates all assets when updating a package to a different version', async () => { + skipIfNoDockerRegistry(providerContext); + before(async () => { + await installPackage(pkgKey); + await installPackage(pkgUpdateKey); + }); + after(async () => { + await uninstallPackage(pkgUpdateKey); + }); + it('should have updated the ILM policy', async function () { + const resPolicy = await es.transport.request({ + method: 'GET', + path: `/_ilm/policy/all_assets`, + }); + expect(resPolicy.body.all_assets.policy).eql({ + phases: { + hot: { + min_age: '1ms', + actions: { + rollover: { + max_size: '50gb', + max_age: '31d', + }, + }, + }, + }, + }); + }); + it('should have updated the index templates', async function () { + const resLogsTemplate = await es.transport.request({ + method: 'GET', + path: `/_index_template/${logsTemplateName}`, + }); + expect(resLogsTemplate.statusCode).equal(200); + expect( + resLogsTemplate.body.index_templates[0].index_template.template.mappings.properties + ).eql({ + '@timestamp': { + type: 'date', + }, + logs_test_name: { + type: 'text', + }, + new_field_name: { + ignore_above: 1024, + type: 'keyword', + }, + data_stream: { + properties: { + dataset: { + type: 'constant_keyword', + }, + namespace: { + type: 'constant_keyword', + }, + type: { + type: 'constant_keyword', + }, + }, + }, + }); + const resMetricsTemplate = await es.transport.request({ + method: 'GET', + path: `/_index_template/${metricsTemplateName}`, + }); + expect(resMetricsTemplate.statusCode).equal(200); + expect( + resMetricsTemplate.body.index_templates[0].index_template.template.mappings.properties + ).eql({ + '@timestamp': { + type: 'date', + }, + metrics_test_name2: { + ignore_above: 1024, + type: 'keyword', + }, + data_stream: { + properties: { + dataset: { + type: 'constant_keyword', + }, + namespace: { + type: 'constant_keyword', + }, + type: { + type: 'constant_keyword', + }, + }, + }, + }); + }); + it('should have installed the new index template', async function () { + const resLogsTemplate = await es.transport.request({ + method: 'GET', + path: `/_index_template/${logsTemplateName2}`, + }); + expect(resLogsTemplate.statusCode).equal(200); + expect( + resLogsTemplate.body.index_templates[0].index_template.template.mappings.properties + ).eql({ + '@timestamp': { + type: 'date', + }, + test_logs2: { + ignore_above: 1024, + type: 'keyword', + }, + data_stream: { + properties: { + dataset: { + type: 'constant_keyword', + }, + namespace: { + type: 'constant_keyword', + }, + type: { + type: 'constant_keyword', + }, + }, + }, + }); + }); + it('should have installed the new versionized pipeline', async function () { + const res = await es.transport.request({ + method: 'GET', + path: `/_ingest/pipeline/${logsTemplateName}-${pkgUpdateVersion}`, + }); + expect(res.statusCode).equal(200); + }); + it('should have removed the old versionized pipelines', async function () { + let res; + try { + res = await es.transport.request({ + method: 'GET', + path: `/_ingest/pipeline/${logsTemplateName}-${pkgVersion}`, + }); + } catch (err) { + res = err; + } + expect(res.statusCode).equal(404); + }); + it('should have updated the template components', async function () { + const res = await es.transport.request({ + method: 'GET', + path: `/_component_template/${logsTemplateName}-mappings`, + }); + expect(res.statusCode).equal(200); + expect(res.body.component_templates[0].component_template.template.mappings).eql({ + dynamic: true, + properties: { '@timestamp': { type: 'date' } }, + }); + const resSettings = await es.transport.request({ + method: 'GET', + path: `/_component_template/${logsTemplateName}-settings`, + }); + expect(res.statusCode).equal(200); + expect(resSettings.body.component_templates[0].component_template.template.settings).eql({ + index: { lifecycle: { name: 'reference2' } }, + }); + }); + it('should have updated the index patterns', async function () { + const resIndexPatternLogs = await kibanaServer.savedObjects.get({ + type: 'index-pattern', + id: 'logs-*', + }); + const fields = JSON.parse(resIndexPatternLogs.attributes.fields); + const updated = fields.filter((field: { name: string }) => field.name === 'new_field_name'); + expect(!!updated.length).equal(true); + const resIndexPatternMetrics = await kibanaServer.savedObjects.get({ + type: 'index-pattern', + id: 'metrics-*', + }); + const fieldsMetrics = JSON.parse(resIndexPatternMetrics.attributes.fields); + const updatedMetrics = fieldsMetrics.filter( + (field: { name: string }) => field.name === 'metrics_test_name2' + ); + expect(!!updatedMetrics.length).equal(true); + }); + it('should have updated the kibana assets', async function () { + const resDashboard = await kibanaServer.savedObjects.get({ + type: 'dashboard', + id: 'sample_dashboard', + }); + expect(resDashboard.id).equal('sample_dashboard'); + let resDashboard2; + try { + resDashboard2 = await kibanaServer.savedObjects.get({ + type: 'dashboard', + id: 'sample_dashboard2', + }); + } catch (err) { + resDashboard2 = err; + } + expect(resDashboard2.response.data.statusCode).equal(404); + const resVis = await kibanaServer.savedObjects.get({ + type: 'visualization', + id: 'sample_visualization', + }); + expect(resVis.attributes.description).equal('sample visualization 0.2.0'); + let resSearch; + try { + resSearch = await kibanaServer.savedObjects.get({ + type: 'search', + id: 'sample_search', + }); + } catch (err) { + resSearch = err; + } + expect(resSearch.response.data.statusCode).equal(404); + const resSearch2 = await kibanaServer.savedObjects.get({ + type: 'search', + id: 'sample_search2', + }); + expect(resSearch2.id).equal('sample_search2'); + }); + it('should have updated the saved object', async function () { + const res = await kibanaServer.savedObjects.get({ + type: 'epm-packages', + id: 'all_assets', + }); + expect(res.attributes).eql({ + installed_kibana: [ + { + id: 'sample_dashboard', + type: 'dashboard', + }, + { + id: 'sample_search2', + type: 'search', + }, + { + id: 'sample_visualization', + type: 'visualization', + }, + ], + installed_es: [ + { + id: 'logs-all_assets.test_logs-0.2.0', + type: 'ingest_pipeline', + }, + { + id: 'logs-all_assets.test_logs', + type: 'index_template', + }, + { + id: 'logs-all_assets.test_logs2', + type: 'index_template', + }, + { + id: 'metrics-all_assets.test_metrics', + type: 'index_template', + }, + ], + es_index_patterns: { + test_logs: 'logs-all_assets.test_logs-*', + test_metrics: 'metrics-all_assets.test_metrics-*', + }, + name: 'all_assets', + version: '0.2.0', + internal: false, + removable: true, + }); + }); + }); +} diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_logs/fields/ecs.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_logs/fields/ecs.yml new file mode 100644 index 0000000000000..3d88fe5dfefb6 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_logs/fields/ecs.yml @@ -0,0 +1,3 @@ +- name: logs_test_name + title: logs_test_title + type: keyword \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_metrics/fields/ecs.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_metrics/fields/ecs.yml new file mode 100644 index 0000000000000..a30e3c7a87856 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_metrics/fields/ecs.yml @@ -0,0 +1,3 @@ +- name: metrics_test_name + title: metrics_test_title + type: keyword \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/visualization/sample_visualization.json b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/visualization/sample_visualization.json index e814b83bbf324..917479fd7d120 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/visualization/sample_visualization.json +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/visualization/sample_visualization.json @@ -1,6 +1,6 @@ { "attributes": { - "description": "sample visualization", + "description": "sample visualization update", "title": "sample vis title", "uiStateJSON": "{}", "version": 1, diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json new file mode 100644 index 0000000000000..d8bab8a75f680 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json @@ -0,0 +1,15 @@ +{ + "policy": { + "phases": { + "hot": { + "min_age": "1ms", + "actions": { + "rollover": { + "max_size": "50gb", + "max_age": "31d" + } + } + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml new file mode 100644 index 0000000000000..580db049d0d5d --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml @@ -0,0 +1,7 @@ +--- +description: Pipeline for parsing test logs + plugins. +processors: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/fields/ecs.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/fields/ecs.yml new file mode 100644 index 0000000000000..7df52cc11fd20 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/fields/ecs.yml @@ -0,0 +1,6 @@ +- name: logs_test_name + title: logs_test_title + type: text +- name: new_field_name + title: new_field_title + type: keyword diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/fields/fields.yml new file mode 100644 index 0000000000000..6e003ed0ad147 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/fields/fields.yml @@ -0,0 +1,16 @@ +- name: data_stream.type + type: constant_keyword + description: > + Data stream type. +- name: data_stream.dataset + type: constant_keyword + description: > + Data stream dataset. +- name: data_stream.namespace + type: constant_keyword + description: > + Data stream namespace. +- name: '@timestamp' + type: date + description: > + Event timestamp. diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/manifest.yml new file mode 100644 index 0000000000000..8a53f9e26e827 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/manifest.yml @@ -0,0 +1,9 @@ +title: Test Dataset + +type: logs + +elasticsearch: + index_template.mappings: + dynamic: true + index_template.settings: + index.lifecycle.name: reference2 \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs2/fields/ecs.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs2/fields/ecs.yml new file mode 100644 index 0000000000000..c5819deb1ee37 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs2/fields/ecs.yml @@ -0,0 +1,3 @@ +- name: test_logs2 + title: test_logs2 + type: keyword \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs2/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs2/fields/fields.yml new file mode 100644 index 0000000000000..6e003ed0ad147 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs2/fields/fields.yml @@ -0,0 +1,16 @@ +- name: data_stream.type + type: constant_keyword + description: > + Data stream type. +- name: data_stream.dataset + type: constant_keyword + description: > + Data stream dataset. +- name: data_stream.namespace + type: constant_keyword + description: > + Data stream namespace. +- name: '@timestamp' + type: date + description: > + Event timestamp. diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs2/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs2/manifest.yml new file mode 100644 index 0000000000000..e12f454657ea2 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs2/manifest.yml @@ -0,0 +1,3 @@ +title: Test Dataset + +type: logs \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_metrics/fields/ecs.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_metrics/fields/ecs.yml new file mode 100644 index 0000000000000..9529c3a8eaf1a --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_metrics/fields/ecs.yml @@ -0,0 +1,3 @@ +- name: metrics_test_name2 + title: metrics_test_title2 + type: keyword \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_metrics/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_metrics/fields/fields.yml new file mode 100644 index 0000000000000..6e003ed0ad147 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_metrics/fields/fields.yml @@ -0,0 +1,16 @@ +- name: data_stream.type + type: constant_keyword + description: > + Data stream type. +- name: data_stream.dataset + type: constant_keyword + description: > + Data stream dataset. +- name: data_stream.namespace + type: constant_keyword + description: > + Data stream namespace. +- name: '@timestamp' + type: date + description: > + Event timestamp. diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_metrics/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_metrics/manifest.yml new file mode 100644 index 0000000000000..6bc20442bd432 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_metrics/manifest.yml @@ -0,0 +1,3 @@ +title: Test Dataset + +type: metrics \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/docs/README.md b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/docs/README.md new file mode 100644 index 0000000000000..2617f1fcabe11 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/docs/README.md @@ -0,0 +1,3 @@ +# Test package + +For testing that a package installs its elasticsearch assets when installed for the first time (not updating) and removing the package diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/img/logo_overrides_64_color.svg b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/img/logo_overrides_64_color.svg new file mode 100644 index 0000000000000..b03007a76ffcc --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/img/logo_overrides_64_color.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/dashboard/sample_dashboard.json b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/dashboard/sample_dashboard.json new file mode 100644 index 0000000000000..ef08d69324210 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/dashboard/sample_dashboard.json @@ -0,0 +1,16 @@ +{ + "attributes": { + "description": "Sample dashboard", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false}", + "panelsJSON": "[{\"embeddableConfig\":{},\"gridData\":{\"h\":12,\"i\":\"1\",\"w\":24,\"x\":0,\"y\":0},\"panelIndex\":\"1\",\"panelRefName\":\"panel_0\",\"version\":\"7.3.0\"},{\"embeddableConfig\":{\"columns\":[\"kafka.log.class\",\"kafka.log.trace.class\",\"kafka.log.trace.full\"],\"sort\":[\"@timestamp\",\"desc\"]},\"gridData\":{\"h\":12,\"i\":\"2\",\"w\":24,\"x\":24,\"y\":0},\"panelIndex\":\"2\",\"panelRefName\":\"panel_1\",\"version\":\"7.3.0\"},{\"embeddableConfig\":{\"columns\":[\"log.level\",\"kafka.log.component\",\"message\"],\"sort\":[\"@timestamp\",\"desc\"]},\"gridData\":{\"h\":20,\"i\":\"3\",\"w\":48,\"x\":0,\"y\":20},\"panelIndex\":\"3\",\"panelRefName\":\"panel_2\",\"version\":\"7.3.0\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":8,\"i\":\"4\",\"w\":48,\"x\":0,\"y\":12},\"panelIndex\":\"4\",\"panelRefName\":\"panel_3\",\"version\":\"7.3.0\"}]", + "timeRestore": false, + "title": "[Logs Sample] Overview ECS", + "version": 1 + }, + "id": "sample_dashboard", + "type": "dashboard" +} \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/search/sample_search2.json b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/search/sample_search2.json new file mode 100644 index 0000000000000..aa5cea19208a4 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/search/sample_search2.json @@ -0,0 +1,24 @@ +{ + "attributes": { + "columns": [ + "log.level", + "kafka.log.component", + "message" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"dataset.name\",\"negate\":false,\"params\":{\"query\":\"kafka.log\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"log\"},\"query\":{\"match\":{\"dataset.name\":{\"query\":\"kafka.log\",\"type\":\"phrase\"}}}}],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"version\":true}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "All logs [Logs Kafka] ECS", + "version": 1 + }, + "id": "sample_search2", + "type": "search" +} \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/visualization/sample_visualization.json b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/visualization/sample_visualization.json new file mode 100644 index 0000000000000..626f1f787f421 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/visualization/sample_visualization.json @@ -0,0 +1,11 @@ +{ + "attributes": { + "description": "sample visualization 0.2.0", + "title": "sample vis title", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1},\"schema\":\"segment\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Log Level\",\"field\":\"log.level\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"group\",\"type\":\"terms\"}],\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"labels\":{\"show\":true,\"truncate\":100},\"position\":\"bottom\",\"scale\":{\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"@timestamp per day\"},\"type\":\"category\"}],\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"legendPosition\":\"right\",\"seriesParams\":[{\"data\":{\"id\":\"1\",\"label\":\"Count\"},\"drawLinesBetweenPoints\":true,\"mode\":\"stacked\",\"show\":\"true\",\"showCircles\":true,\"type\":\"histogram\",\"valueAxis\":\"ValueAxis-1\"}],\"times\":[],\"type\":\"histogram\",\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"filter\":false,\"rotate\":0,\"show\":true,\"truncate\":100},\"name\":\"LeftAxis-1\",\"position\":\"left\",\"scale\":{\"mode\":\"normal\",\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"Count\"},\"type\":\"value\"}]},\"title\":\"Log levels over time [Logs Kafka] ECS\",\"type\":\"histogram\"}" + }, + "id": "sample_visualization", + "type": "visualization" +} \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/manifest.yml new file mode 100644 index 0000000000000..70da51a14bce8 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/manifest.yml @@ -0,0 +1,20 @@ +format_version: 1.0.0 +name: all_assets +title: All Assets Updated +description: tests that all assets are updated +version: 0.2.0 +categories: [] +release: beta +type: integration +license: basic + +requirement: + elasticsearch: + versions: '>7.7.0' + kibana: + versions: '>7.7.0' + +icons: + - src: '/img/logo_overrides_64_color.svg' + size: '16x16' + type: 'image/svg+xml' diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json new file mode 100644 index 0000000000000..7cf62e890f865 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json @@ -0,0 +1,15 @@ +{ + "policy": { + "phases": { + "hot": { + "min_age": "0ms", + "actions": { + "rollover": { + "max_size": "50gb", + "max_age": "30d" + } + } + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml new file mode 100644 index 0000000000000..580db049d0d5d --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml @@ -0,0 +1,7 @@ +--- +description: Pipeline for parsing test logs + plugins. +processors: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/fields/ecs.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/fields/ecs.yml new file mode 100644 index 0000000000000..3d88fe5dfefb6 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/fields/ecs.yml @@ -0,0 +1,3 @@ +- name: logs_test_name + title: logs_test_title + type: keyword \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/fields/fields.yml new file mode 100644 index 0000000000000..6e003ed0ad147 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/fields/fields.yml @@ -0,0 +1,16 @@ +- name: data_stream.type + type: constant_keyword + description: > + Data stream type. +- name: data_stream.dataset + type: constant_keyword + description: > + Data stream dataset. +- name: data_stream.namespace + type: constant_keyword + description: > + Data stream namespace. +- name: '@timestamp' + type: date + description: > + Event timestamp. diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/manifest.yml new file mode 100644 index 0000000000000..8cd522e2845bb --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/manifest.yml @@ -0,0 +1,9 @@ +title: Test Dataset + +type: logs + +elasticsearch: + index_template.mappings: + dynamic: false + index_template.settings: + index.lifecycle.name: reference \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_metrics/fields/ecs.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_metrics/fields/ecs.yml new file mode 100644 index 0000000000000..a30e3c7a87856 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_metrics/fields/ecs.yml @@ -0,0 +1,3 @@ +- name: metrics_test_name + title: metrics_test_title + type: keyword \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_metrics/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_metrics/fields/fields.yml new file mode 100644 index 0000000000000..6e003ed0ad147 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_metrics/fields/fields.yml @@ -0,0 +1,16 @@ +- name: data_stream.type + type: constant_keyword + description: > + Data stream type. +- name: data_stream.dataset + type: constant_keyword + description: > + Data stream dataset. +- name: data_stream.namespace + type: constant_keyword + description: > + Data stream namespace. +- name: '@timestamp' + type: date + description: > + Event timestamp. diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_metrics/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_metrics/manifest.yml new file mode 100644 index 0000000000000..6bc20442bd432 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_metrics/manifest.yml @@ -0,0 +1,3 @@ +title: Test Dataset + +type: metrics \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/docs/README.md b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/docs/README.md new file mode 100644 index 0000000000000..34b1f08a55cbe --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/docs/README.md @@ -0,0 +1,3 @@ +# Test package + +For testing that datastream rolls over when mappings are not compatible diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/manifest.yml new file mode 100644 index 0000000000000..0ab43760b7ee8 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/manifest.yml @@ -0,0 +1,20 @@ +format_version: 1.0.0 +name: datastreams +title: datastream test +description: This is a test package for testing that datastreams rollover when mappings are incompatible +version: 0.1.0 +categories: [] +release: beta +type: integration +license: basic + +requirement: + elasticsearch: + versions: '>7.7.0' + kibana: + versions: '>7.7.0' + +icons: + - src: '/img/logo_overrides_64_color.svg' + size: '16x16' + type: 'image/svg+xml' diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json new file mode 100644 index 0000000000000..d8bab8a75f680 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json @@ -0,0 +1,15 @@ +{ + "policy": { + "phases": { + "hot": { + "min_age": "1ms", + "actions": { + "rollover": { + "max_size": "50gb", + "max_age": "31d" + } + } + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml new file mode 100644 index 0000000000000..580db049d0d5d --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml @@ -0,0 +1,7 @@ +--- +description: Pipeline for parsing test logs + plugins. +processors: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/fields/ecs.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/fields/ecs.yml new file mode 100644 index 0000000000000..7df52cc11fd20 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/fields/ecs.yml @@ -0,0 +1,6 @@ +- name: logs_test_name + title: logs_test_title + type: text +- name: new_field_name + title: new_field_title + type: keyword diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/fields/fields.yml new file mode 100644 index 0000000000000..6e003ed0ad147 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/fields/fields.yml @@ -0,0 +1,16 @@ +- name: data_stream.type + type: constant_keyword + description: > + Data stream type. +- name: data_stream.dataset + type: constant_keyword + description: > + Data stream dataset. +- name: data_stream.namespace + type: constant_keyword + description: > + Data stream namespace. +- name: '@timestamp' + type: date + description: > + Event timestamp. diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/manifest.yml new file mode 100644 index 0000000000000..8a53f9e26e827 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/manifest.yml @@ -0,0 +1,9 @@ +title: Test Dataset + +type: logs + +elasticsearch: + index_template.mappings: + dynamic: true + index_template.settings: + index.lifecycle.name: reference2 \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_metrics/fields/ecs.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_metrics/fields/ecs.yml new file mode 100644 index 0000000000000..8fb3ccd3de8fd --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_metrics/fields/ecs.yml @@ -0,0 +1,6 @@ +- name: metrics_test_name + title: metrics_test_title + type: keyword +- name: metrics_test_name2 + title: metrics_test_title2 + type: keyword \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_metrics/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_metrics/fields/fields.yml new file mode 100644 index 0000000000000..6e003ed0ad147 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_metrics/fields/fields.yml @@ -0,0 +1,16 @@ +- name: data_stream.type + type: constant_keyword + description: > + Data stream type. +- name: data_stream.dataset + type: constant_keyword + description: > + Data stream dataset. +- name: data_stream.namespace + type: constant_keyword + description: > + Data stream namespace. +- name: '@timestamp' + type: date + description: > + Event timestamp. diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_metrics/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_metrics/manifest.yml new file mode 100644 index 0000000000000..6bc20442bd432 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_metrics/manifest.yml @@ -0,0 +1,3 @@ +title: Test Dataset + +type: metrics \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/docs/README.md b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/docs/README.md new file mode 100644 index 0000000000000..34b1f08a55cbe --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/docs/README.md @@ -0,0 +1,3 @@ +# Test package + +For testing that datastream rolls over when mappings are not compatible diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/manifest.yml new file mode 100644 index 0000000000000..1aa1410bd0aef --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/manifest.yml @@ -0,0 +1,20 @@ +format_version: 1.0.0 +name: datastreams +title: datastream test +description: This is a test package for testing that datastreams rollover when mappings are incompatible +version: 0.2.0 +categories: [] +release: beta +type: integration +license: basic + +requirement: + elasticsearch: + versions: '>7.7.0' + kibana: + versions: '>7.7.0' + +icons: + - src: '/img/logo_overrides_64_color.svg' + size: '16x16' + type: 'image/svg+xml' From 7dc33f9ba8f93873dcd15509da125587c1730bb4 Mon Sep 17 00:00:00 2001 From: Robert Austin Date: Fri, 7 Aug 2020 09:15:35 -0400 Subject: [PATCH 09/19] [Resolver] UI tests for the panel and bug fix (#74421) * Change the way the resolver simulator works * refactor resolver tree and data access layer mocks * Fix bug where timestamp and pid sometimes don't show in the node detail view * add a few tests for the panel (not done, but worth committing.) --- .../common/endpoint/types.ts | 9 + ...ildren.ts => no_ancestors_two_children.ts} | 48 ++--- ..._children_with_related_events_on_origin.ts | 94 +++++++++ .../{store => }/mocks/endpoint_event.ts | 21 +- .../{store => }/mocks/resolver_tree.ts | 48 +++-- .../resolver/store/data/selectors.test.ts | 2 +- .../resolver/store/mocks/related_event.ts | 36 ---- .../public/resolver/store/selectors.test.ts | 2 +- .../test_utilities/simulator/index.tsx | 194 ++++++++---------- .../resolver/view/clickthrough.test.tsx | 81 ++++---- .../public/resolver/view/panel.test.tsx | 59 ++++++ .../resolver/view/panels/process_details.tsx | 26 ++- .../view/panels/process_list_with_counts.tsx | 7 +- 13 files changed, 390 insertions(+), 237 deletions(-) rename x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/{one_ancestor_two_children.ts => no_ancestors_two_children.ts} (62%) create mode 100644 x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children_with_related_events_on_origin.ts rename x-pack/plugins/security_solution/public/resolver/{store => }/mocks/endpoint_event.ts (66%) rename x-pack/plugins/security_solution/public/resolver/{store => }/mocks/resolver_tree.ts (89%) delete mode 100644 x-pack/plugins/security_solution/public/resolver/store/mocks/related_event.ts create mode 100644 x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx diff --git a/x-pack/plugins/security_solution/common/endpoint/types.ts b/x-pack/plugins/security_solution/common/endpoint/types.ts index 61ce672405fd5..ffde47825b501 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types.ts @@ -182,6 +182,15 @@ export interface ResolverRelatedEvents { nextEvent: string | null; } +/** + * Safe version of `ResolverRelatedEvents` + */ +export interface SafeResolverRelatedEvents { + entityID: string; + events: SafeResolverEvent[]; + nextEvent: string | null; +} + /** * Response structure for the alerts route. */ diff --git a/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/one_ancestor_two_children.ts b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children.ts similarity index 62% rename from x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/one_ancestor_two_children.ts rename to x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children.ts index 94c176d343d17..b0407fa5d7c1d 100644 --- a/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/one_ancestor_two_children.ts +++ b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children.ts @@ -9,11 +9,8 @@ import { ResolverTree, ResolverEntityIndex, } from '../../../../common/endpoint/types'; -import { mockEndpointEvent } from '../../store/mocks/endpoint_event'; -import { - mockTreeWithNoAncestorsAnd2Children, - withRelatedEventsOnOrigin, -} from '../../store/mocks/resolver_tree'; +import { mockEndpointEvent } from '../../mocks/endpoint_event'; +import { mockTreeWithNoAncestorsAnd2Children } from '../../mocks/resolver_tree'; import { DataAccessLayer } from '../../types'; interface Metadata { @@ -43,24 +40,11 @@ interface Metadata { /** * A simple mock dataAccessLayer possible that returns a tree with 0 ancestors and 2 direct children. 1 related event is returned. The parameter to `entities` is ignored. */ -export function oneAncestorTwoChildren( - { withRelatedEvents }: { withRelatedEvents: Iterable<[string, string]> | null } = { - withRelatedEvents: null, - } -): { dataAccessLayer: DataAccessLayer; metadata: Metadata } { +export function noAncestorsTwoChildren(): { dataAccessLayer: DataAccessLayer; metadata: Metadata } { const metadata: Metadata = { databaseDocumentID: '_id', entityIDs: { origin: 'origin', firstChild: 'firstChild', secondChild: 'secondChild' }, }; - const baseTree = mockTreeWithNoAncestorsAnd2Children({ - originID: metadata.entityIDs.origin, - firstChildID: metadata.entityIDs.firstChild, - secondChildID: metadata.entityIDs.secondChild, - }); - const composedTree = withRelatedEvents - ? withRelatedEventsOnOrigin(baseTree, withRelatedEvents) - : baseTree; - return { metadata, dataAccessLayer: { @@ -70,17 +54,13 @@ export function oneAncestorTwoChildren( relatedEvents(entityID: string): Promise { return Promise.resolve({ entityID, - events: - /* Respond with the mocked related events when the origin's related events are fetched*/ withRelatedEvents && - entityID === metadata.entityIDs.origin - ? composedTree.relatedEvents.events - : [ - mockEndpointEvent({ - entityID, - name: 'event', - timestamp: 0, - }), - ], + events: [ + mockEndpointEvent({ + entityID, + name: 'event', + timestamp: 0, + }), + ], nextEvent: null, }); }, @@ -89,7 +69,13 @@ export function oneAncestorTwoChildren( * Fetch a ResolverTree for a entityID */ resolverTree(): Promise { - return Promise.resolve(composedTree); + return Promise.resolve( + mockTreeWithNoAncestorsAnd2Children({ + originID: metadata.entityIDs.origin, + firstChildID: metadata.entityIDs.firstChild, + secondChildID: metadata.entityIDs.secondChild, + }) + ); }, /** diff --git a/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children_with_related_events_on_origin.ts b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children_with_related_events_on_origin.ts new file mode 100644 index 0000000000000..01e75e3eefdbf --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children_with_related_events_on_origin.ts @@ -0,0 +1,94 @@ +/* + * 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 { DataAccessLayer } from '../../types'; +import { mockTreeWithNoAncestorsAndTwoChildrenAndRelatedEventsOnOrigin } from '../../mocks/resolver_tree'; +import { + ResolverRelatedEvents, + ResolverTree, + ResolverEntityIndex, +} from '../../../../common/endpoint/types'; + +interface Metadata { + /** + * The `_id` of the document being analyzed. + */ + databaseDocumentID: string; + /** + * A record of entityIDs to be used in tests assertions. + */ + entityIDs: { + /** + * The entityID of the node related to the document being analyzed. + */ + origin: 'origin'; + /** + * The entityID of the first child of the origin. + */ + firstChild: 'firstChild'; + /** + * The entityID of the second child of the origin. + */ + secondChild: 'secondChild'; + }; +} + +export function noAncestorsTwoChildrenWithRelatedEventsOnOrigin(): { + dataAccessLayer: DataAccessLayer; + metadata: Metadata; +} { + const metadata: Metadata = { + databaseDocumentID: '_id', + entityIDs: { origin: 'origin', firstChild: 'firstChild', secondChild: 'secondChild' }, + }; + const tree = mockTreeWithNoAncestorsAndTwoChildrenAndRelatedEventsOnOrigin({ + originID: metadata.entityIDs.origin, + firstChildID: metadata.entityIDs.firstChild, + secondChildID: metadata.entityIDs.secondChild, + }); + + return { + metadata, + dataAccessLayer: { + /** + * Fetch related events for an entity ID + */ + relatedEvents(entityID: string): Promise { + /** + * Respond with the mocked related events when the origin's related events are fetched. + **/ + const events = entityID === metadata.entityIDs.origin ? tree.relatedEvents.events : []; + + return Promise.resolve({ + entityID, + events, + nextEvent: null, + } as ResolverRelatedEvents); + }, + + /** + * Fetch a ResolverTree for a entityID + */ + resolverTree(): Promise { + return Promise.resolve(tree); + }, + + /** + * Get an array of index patterns that contain events. + */ + indexPatterns(): string[] { + return ['index pattern']; + }, + + /** + * Get entities matching a document. + */ + entities(): Promise { + return Promise.resolve([{ entity_id: metadata.entityIDs.origin }]); + }, + }, + }; +} diff --git a/x-pack/plugins/security_solution/public/resolver/store/mocks/endpoint_event.ts b/x-pack/plugins/security_solution/public/resolver/mocks/endpoint_event.ts similarity index 66% rename from x-pack/plugins/security_solution/public/resolver/store/mocks/endpoint_event.ts rename to x-pack/plugins/security_solution/public/resolver/mocks/endpoint_event.ts index 709f2faf13b00..c822fdf647c16 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/mocks/endpoint_event.ts +++ b/x-pack/plugins/security_solution/public/resolver/mocks/endpoint_event.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EndpointEvent } from '../../../../common/endpoint/types'; +import { EndpointEvent } from '../../../common/endpoint/types'; /** * Simple mock endpoint event that works for tree layouts. @@ -28,10 +28,29 @@ export function mockEndpointEvent({ type: lifecycleType ? lifecycleType : 'start', category: 'process', }, + agent: { + id: 'agent.id', + version: 'agent.version', + type: 'agent.type', + }, + ecs: { + version: 'ecs.version', + }, + user: { + name: 'user.name', + domain: 'user.domain', + }, process: { entity_id: entityID, + executable: 'executable', + args: 'args', name, + pid: 0, + hash: { + md5: 'hash.md5', + }, parent: { + pid: 0, entity_id: parentEntityId, }, }, diff --git a/x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts b/x-pack/plugins/security_solution/public/resolver/mocks/resolver_tree.ts similarity index 89% rename from x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts rename to x-pack/plugins/security_solution/public/resolver/mocks/resolver_tree.ts index 21d0309501aa8..5d2cbb2eab0dc 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts +++ b/x-pack/plugins/security_solution/public/resolver/mocks/resolver_tree.ts @@ -5,8 +5,7 @@ */ import { mockEndpointEvent } from './endpoint_event'; -import { mockRelatedEvent } from './related_event'; -import { ResolverTree, ResolverEvent } from '../../../../common/endpoint/types'; +import { ResolverTree, ResolverEvent, SafeResolverEvent } from '../../../common/endpoint/types'; export function mockTreeWith2AncestorsAndNoChildren({ originID, @@ -125,11 +124,11 @@ type RelatedEventType = string; * @param treeToAddRelatedEventsTo the ResolverTree to modify * @param relatedEventsToAddByCategoryAndType Iterable of `[category, type]` pairs describing related events. e.g. [['dns','info'],['registry','access']] */ -export function withRelatedEventsOnOrigin( +function withRelatedEventsOnOrigin( treeToAddRelatedEventsTo: ResolverTree, relatedEventsToAddByCategoryAndType: Iterable<[RelatedEventCategory, RelatedEventType]> ): ResolverTree { - const events = []; + const events: SafeResolverEvent[] = []; const byCategory: Record = {}; const stats = { totalAlerts: 0, @@ -139,14 +138,18 @@ export function withRelatedEventsOnOrigin( }, }; for (const [category, type] of relatedEventsToAddByCategoryAndType) { - events.push( - mockRelatedEvent({ - entityID: treeToAddRelatedEventsTo.entityID, - timestamp: 1, - category, + events.push({ + '@timestamp': 1, + event: { + kind: 'event', type, - }) - ); + category, + id: 'xyz', + }, + process: { + entity_id: treeToAddRelatedEventsTo.entityID, + }, + }); stats.events.total++; stats.events.byCategory[category] = stats.events.byCategory[category] ? stats.events.byCategory[category] + 1 @@ -156,7 +159,7 @@ export function withRelatedEventsOnOrigin( ...treeToAddRelatedEventsTo, stats, relatedEvents: { - events, + events: events as ResolverEvent[], nextEvent: null, }, }; @@ -309,3 +312,24 @@ export function mockTreeWithNoProcessEvents(): ResolverTree { }, }; } + +export function mockTreeWithNoAncestorsAndTwoChildrenAndRelatedEventsOnOrigin({ + originID, + firstChildID, + secondChildID, +}: { + originID: string; + firstChildID: string; + secondChildID: string; +}) { + const baseTree = mockTreeWithNoAncestorsAnd2Children({ + originID, + firstChildID, + secondChildID, + }); + const withRelatedEvents: Array<[string, string]> = [ + ['registry', 'access'], + ['registry', 'access'], + ]; + return withRelatedEventsOnOrigin(baseTree, withRelatedEvents); +} diff --git a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts index 6786a93f1d9ca..15a981d460730 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts @@ -15,7 +15,7 @@ import { mockTreeWith1AncestorAnd2ChildrenAndAllNodesHave2GraphableEvents, mockTreeWithAllProcessesTerminated, mockTreeWithNoProcessEvents, -} from '../mocks/resolver_tree'; +} from '../../mocks/resolver_tree'; import { uniquePidForProcess } from '../../models/process_event'; import { EndpointEvent } from '../../../../common/endpoint/types'; diff --git a/x-pack/plugins/security_solution/public/resolver/store/mocks/related_event.ts b/x-pack/plugins/security_solution/public/resolver/store/mocks/related_event.ts deleted file mode 100644 index 1e0c460a3a711..0000000000000 --- a/x-pack/plugins/security_solution/public/resolver/store/mocks/related_event.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import { EndpointEvent } from '../../../../common/endpoint/types'; - -/** - * Simple mock related event. - */ -export function mockRelatedEvent({ - entityID, - timestamp, - category, - type, - id, -}: { - entityID: string; - timestamp: number; - category: string; - type: string; - id?: string; -}): EndpointEvent { - return { - '@timestamp': timestamp, - event: { - kind: 'event', - type, - category, - id: id ?? 'xyz', - }, - process: { - entity_id: entityID, - }, - } as EndpointEvent; -} diff --git a/x-pack/plugins/security_solution/public/resolver/store/selectors.test.ts b/x-pack/plugins/security_solution/public/resolver/store/selectors.test.ts index dfbc6bd290686..f113e861d3ce9 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/selectors.test.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/selectors.test.ts @@ -12,7 +12,7 @@ import * as selectors from './selectors'; import { mockTreeWith2AncestorsAndNoChildren, mockTreeWithNoAncestorsAnd2Children, -} from './mocks/resolver_tree'; +} from '../mocks/resolver_tree'; import { SafeResolverEvent } from '../../../common/endpoint/types'; describe('resolver selectors', () => { diff --git a/x-pack/plugins/security_solution/public/resolver/test_utilities/simulator/index.tsx b/x-pack/plugins/security_solution/public/resolver/test_utilities/simulator/index.tsx index ed30643ed871e..6f44c5aee7cac 100644 --- a/x-pack/plugins/security_solution/public/resolver/test_utilities/simulator/index.tsx +++ b/x-pack/plugins/security_solution/public/resolver/test_utilities/simulator/index.tsx @@ -113,83 +113,21 @@ export class Simulator { } /** - * Return a promise that resolves after the `store`'s next state transition. - * Used by `mapStateTransitions` + * Yield the result of `mapper` over and over, once per event-loop cycle. + * After 10 times, quit. + * Use this to continually check a value. See `toYieldEqualTo`. */ - private stateTransitioned(): Promise { - // keep track of the resolve function of the promise that has been returned. - let resolveState: (() => void) | null = null; - - const promise: Promise = new Promise((resolve) => { - // Immediately expose the resolve function in the outer scope. It will be resolved when the next state transition occurs. - resolveState = resolve; - }); - - // Subscribe to the store - const unsubscribe = this.store.subscribe(() => { - // Once a state transition occurs, unsubscribe. - unsubscribe(); - // Resolve the promise. The null assertion is safe here as Promise initializers run immediately (according to spec and node/browser implementations.) - // NB: the state is not resolved here. Code using the simulator should not rely on state or selectors of state. - resolveState!(); - }); - - // Return the promise that will be resolved on the next state transition, allowing code to `await` for the next state transition. - return promise; - } - - /** - * This will yield the return value of `mapper` after each state transition. If no state transition occurs for 10 event loops in a row, this will give up. - */ - public async *mapStateTransitions(mapper: () => R): AsyncIterable { - // Yield the value before any state transitions have occurred. - yield mapper(); - - /** Increment this each time an event loop completes without a state transition. - * If this value hits `10`, end the loop. - * - * Code will test assertions after each state transition. If the assertion hasn't passed and no further state transitions occur, - * then the jest timeout will happen. The timeout doesn't give a useful message about the assertion. - * By short-circuiting this function, code that uses it can short circuit the test timeout and print a useful error message. - * - * NB: the logic to short-circuit the loop is here because knowledge of state is a concern of the simulator, not tests. - */ + public async *map(mapper: () => R): AsyncIterable { let timeoutCount = 0; - while (true) { - /** - * `await` a race between the next state transition and a timeout that happens after `0`ms. - * If the timeout wins, no `dispatch` call caused a state transition in the last loop. - * If this keeps happening, assume that Resolver isn't going to do anything else. - * - * If Resolver adds intentional delay logic (e.g. waiting before making a request), this code might have to change. - * In that case, Resolver should use the side effect context to schedule future work. This code could then subscribe to some event published by the side effect context. That way, this code will be aware of Resolver's intention to do work. - */ - const timedOut: boolean = await Promise.race([ - (async (): Promise => { - await this.stateTransitioned(); - // If a state transition occurs, return false for `timedOut` - return false; - })(), - new Promise((resolve) => { - setTimeout(() => { - // If a timeout occurs, resolve `timedOut` as true - return resolve(true); - }, 0); - }), - ]); - - if (timedOut) { - // If a timout occurred, note it. - timeoutCount++; - if (timeoutCount === 10) { - // if 10 timeouts happen in a row, end the loop early - return; - } - } else { - // If a state transition occurs, reset the timeout count and yield the value - timeoutCount = 0; - yield mapper(); - } + while (timeoutCount < 10) { + timeoutCount++; + yield mapper(); + await new Promise((resolve) => { + setTimeout(() => { + this.wrapper.update(); + resolve(); + }, 0); + }); } } @@ -198,25 +136,22 @@ export class Simulator { * returns a `ReactWrapper` even if nothing is found, as that is how `enzyme` does things. */ public processNodeElements(options: ProcessNodeElementSelectorOptions = {}): ReactWrapper { - return this.findInDOM(processNodeElementSelector(options)); + return this.domNodes(processNodeElementSelector(options)); } /** - * true if a process node element is found for the entityID and if it has an [aria-selected] attribute. + * Return the node element with the given `entityID`. */ - public processNodeElementLooksSelected(entityID: string): boolean { - return this.processNodeElements({ entityID, selected: true }).length === 1; + public selectedProcessNode(entityID: string): ReactWrapper { + return this.processNodeElements({ entityID, selected: true }); } /** - * true if a process node element is found for the entityID and if it *does not have* an [aria-selected] attribute. + * Return the node element with the given `entityID`. It will only be returned if it is not selected. */ - public processNodeElementLooksUnselected(entityID: string): boolean { - // find the process node, then exclude it if its selected. - return ( - this.processNodeElements({ entityID }).not( - processNodeElementSelector({ entityID, selected: true }) - ).length === 1 + public unselectedProcessNode(entityID: string): ReactWrapper { + return this.processNodeElements({ entityID }).not( + processNodeElementSelector({ entityID, selected: true }) ); } @@ -234,11 +169,8 @@ export class Simulator { * @param entityID The entity ID of the proocess node to select in */ public processNodeRelatedEventButton(entityID: string): ReactWrapper { - return this.processNodeElements({ entityID }).findWhere( - (wrapper) => - // Filter out React components - typeof wrapper.type() === 'string' && - wrapper.prop('data-test-subj') === 'resolver:submenu:button' + return this.domNodes( + `${processNodeElementSelector({ entityID })} [data-test-subj="resolver:submenu:button"]` ); } @@ -256,42 +188,98 @@ export class Simulator { * The element that shows when Resolver is waiting for the graph data. */ public graphLoadingElement(): ReactWrapper { - return this.findInDOM('[data-test-subj="resolver:graph:loading"]'); + return this.domNodes('[data-test-subj="resolver:graph:loading"]'); } /** * The element that shows if Resolver couldn't draw the graph. */ public graphErrorElement(): ReactWrapper { - return this.findInDOM('[data-test-subj="resolver:graph:error"]'); + return this.domNodes('[data-test-subj="resolver:graph:error"]'); } /** * The element where nodes get drawn. */ public graphElement(): ReactWrapper { - return this.findInDOM('[data-test-subj="resolver:graph"]'); + return this.domNodes('[data-test-subj="resolver:graph"]'); + } + + /** + * An element with a list of all nodes. + */ + public nodeListElement(): ReactWrapper { + return this.domNodes('[data-test-subj="resolver:node-list"]'); + } + + /** + * Return the items in the node list (the default panel view.) + */ + public nodeListItems(): ReactWrapper { + return this.domNodes('[data-test-subj="resolver:node-list:item"]'); + } + + /** + * The element containing the details for the selected node. + */ + public nodeDetailElement(): ReactWrapper { + return this.domNodes('[data-test-subj="resolver:node-detail"]'); + } + + /** + * The details of the selected node are shown in a description list. This returns the title elements of the description list. + */ + private nodeDetailEntryTitle(): ReactWrapper { + return this.domNodes('[data-test-subj="resolver:node-detail:entry-title"]'); } /** - * The outer panel container. + * The details of the selected node are shown in a description list. This returns the description elements of the description list. */ - public panelElement(): ReactWrapper { - return this.findInDOM('[data-test-subj="resolver:panel"]'); + private nodeDetailEntryDescription(): ReactWrapper { + return this.domNodes('[data-test-subj="resolver:node-detail:entry-description"]'); } /** - * The panel content element (which may include tables, lists, other data depending on the view). + * Return DOM nodes that match `enzymeSelector`. */ - public panelContentElement(): ReactWrapper { - return this.findInDOM('[data-test-subj^="resolver:panel:"]'); + private domNodes(enzymeSelector: string): ReactWrapper { + return this.wrapper + .find(enzymeSelector) + .filterWhere((wrapper) => typeof wrapper.type() === 'string'); } /** - * Like `this.wrapper.find` but only returns DOM nodes. + * The titles and descriptions (as text) from the node detail panel. */ - private findInDOM(selector: string): ReactWrapper { - return this.wrapper.find(selector).filterWhere((wrapper) => typeof wrapper.type() === 'string'); + public nodeDetailDescriptionListEntries(): Array<[string, string]> { + const titles = this.nodeDetailEntryTitle(); + const descriptions = this.nodeDetailEntryDescription(); + const entries: Array<[string, string]> = []; + for (let index = 0; index < Math.min(titles.length, descriptions.length); index++) { + const title = titles.at(index).text(); + const description = descriptions.at(index).text(); + + // Exclude timestamp since we can't currently calculate the expected description for it from tests + if (title !== '@timestamp') { + entries.push([title, description]); + } + } + return entries; + } + + /** + * Resolve the wrapper returned by `wrapperFactory` only once it has at least 1 element in it. + */ + public async resolveWrapper( + wrapperFactory: () => ReactWrapper, + predicate: (wrapper: ReactWrapper) => boolean = (wrapper) => wrapper.length > 0 + ): Promise { + for await (const wrapper of this.map(wrapperFactory)) { + if (predicate(wrapper)) { + return wrapper; + } + } } } diff --git a/x-pack/plugins/security_solution/public/resolver/view/clickthrough.test.tsx b/x-pack/plugins/security_solution/public/resolver/view/clickthrough.test.tsx index c819491dd28f0..98ea235d3524f 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/clickthrough.test.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/clickthrough.test.tsx @@ -4,10 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { oneAncestorTwoChildren } from '../data_access_layer/mocks/one_ancestor_two_children'; +import { noAncestorsTwoChildren } from '../data_access_layer/mocks/no_ancestors_two_children'; import { Simulator } from '../test_utilities/simulator'; // Extend jest with a custom matcher import '../test_utilities/extend_jest'; +import { noAncestorsTwoChildrenWithRelatedEventsOnOrigin } from '../data_access_layer/mocks/no_ancestors_two_children_with_related_events_on_origin'; let simulator: Simulator; let databaseDocumentID: string; @@ -16,10 +17,10 @@ let entityIDs: { origin: string; firstChild: string; secondChild: string }; // the resolver component instance ID, used by the react code to distinguish piece of global state from those used by other resolver instances const resolverComponentInstanceID = 'resolverComponentInstanceID'; -describe('Resolver, when analyzing a tree that has 1 ancestor and 2 children', () => { +describe('Resolver, when analyzing a tree that has no ancestors and 2 children', () => { beforeEach(async () => { // create a mock data access layer - const { metadata: dataAccessLayerMetadata, dataAccessLayer } = oneAncestorTwoChildren(); + const { metadata: dataAccessLayerMetadata, dataAccessLayer } = noAncestorsTwoChildren(); // save a reference to the entity IDs exposed by the mock data layer entityIDs = dataAccessLayerMetadata.entityIDs; @@ -40,7 +41,7 @@ describe('Resolver, when analyzing a tree that has 1 ancestor and 2 children', ( * * For example, there might be no loading element at one point, and 1 graph element at one point, but never a single time when there is both 1 graph element and 0 loading elements. */ - simulator.mapStateTransitions(() => ({ + simulator.map(() => ({ graphElements: simulator.graphElement().length, graphLoadingElements: simulator.graphLoadingElement().length, graphErrorElements: simulator.graphErrorElement().length, @@ -55,22 +56,23 @@ describe('Resolver, when analyzing a tree that has 1 ancestor and 2 children', ( // Combining assertions here for performance. Unfortunately, Enzyme + jsdom + React is slow. it(`should have 3 nodes, with the entityID's 'origin', 'firstChild', and 'secondChild'. 'origin' should be selected.`, async () => { - expect(simulator.processNodeElementLooksSelected(entityIDs.origin)).toBe(true); - - expect(simulator.processNodeElementLooksUnselected(entityIDs.firstChild)).toBe(true); - expect(simulator.processNodeElementLooksUnselected(entityIDs.secondChild)).toBe(true); - - expect(simulator.processNodeElements().length).toBe(3); + await expect( + simulator.map(() => ({ + selectedOriginCount: simulator.selectedProcessNode(entityIDs.origin).length, + unselectedFirstChildCount: simulator.unselectedProcessNode(entityIDs.firstChild).length, + unselectedSecondChildCount: simulator.unselectedProcessNode(entityIDs.secondChild).length, + processNodeCount: simulator.processNodeElements().length, + })) + ).toYieldEqualTo({ + selectedOriginCount: 1, + unselectedFirstChildCount: 1, + unselectedSecondChildCount: 1, + processNodeCount: 3, + }); }); - it(`should have the default "process list" panel present`, async () => { - expect(simulator.panelElement().length).toBe(1); - expect(simulator.panelContentElement().length).toBe(1); - const testSubjectName = simulator - .panelContentElement() - .getDOMNode() - .getAttribute('data-test-subj'); - expect(testSubjectName).toMatch(/process-list/g); + it(`should show the node list`, async () => { + await expect(simulator.map(() => simulator.nodeListElement().length)).toYieldEqualTo(1); }); describe("when the second child node's first button has been clicked", () => { @@ -82,42 +84,37 @@ describe('Resolver, when analyzing a tree that has 1 ancestor and 2 children', ( .first() .simulate('click'); }); - it('should render the second child node as selected, and the first child not as not selected, and the query string should indicate that the second child is selected', async () => { + it('should render the second child node as selected, and the origin as not selected, and the query string should indicate that the second child is selected', async () => { await expect( - simulator.mapStateTransitions(function value() { - return { - // the query string has a key showing that the second child is selected - queryStringSelectedNode: simulator.queryStringValues().selectedNode, - // the second child is rendered in the DOM, and shows up as selected - secondChildLooksSelected: simulator.processNodeElementLooksSelected( - entityIDs.secondChild - ), - // the origin is in the DOM, but shows up as unselected - originLooksUnselected: simulator.processNodeElementLooksUnselected(entityIDs.origin), - }; - }) + simulator.map(() => ({ + // the query string has a key showing that the second child is selected + queryStringSelectedNode: simulator.queryStringValues().selectedNode, + // the second child is rendered in the DOM, and shows up as selected + selectedSecondChildNodeCount: simulator.selectedProcessNode(entityIDs.secondChild) + .length, + // the origin is in the DOM, but shows up as unselected + unselectedOriginNodeCount: simulator.unselectedProcessNode(entityIDs.origin).length, + })) ).toYieldEqualTo({ // Just the second child should be marked as selected in the query string queryStringSelectedNode: [entityIDs.secondChild], // The second child is rendered and has `[aria-selected]` - secondChildLooksSelected: true, + selectedSecondChildNodeCount: 1, // The origin child is rendered and doesn't have `[aria-selected]` - originLooksUnselected: true, + unselectedOriginNodeCount: 1, }); }); }); }); }); -describe('Resolver, when analyzing a tree that has some related events', () => { +describe('Resolver, when analyzing a tree that has two related events for the origin', () => { beforeEach(async () => { // create a mock data access layer with related events - const { metadata: dataAccessLayerMetadata, dataAccessLayer } = oneAncestorTwoChildren({ - withRelatedEvents: [ - ['registry', 'access'], - ['registry', 'access'], - ], - }); + const { + metadata: dataAccessLayerMetadata, + dataAccessLayer, + } = noAncestorsTwoChildrenWithRelatedEventsOnOrigin(); // save a reference to the entity IDs exposed by the mock data layer entityIDs = dataAccessLayerMetadata.entityIDs; @@ -132,7 +129,7 @@ describe('Resolver, when analyzing a tree that has some related events', () => { describe('when it has loaded', () => { beforeEach(async () => { await expect( - simulator.mapStateTransitions(() => ({ + simulator.map(() => ({ graphElements: simulator.graphElement().length, graphLoadingElements: simulator.graphLoadingElement().length, graphErrorElements: simulator.graphErrorElement().length, @@ -148,7 +145,7 @@ describe('Resolver, when analyzing a tree that has some related events', () => { it('should render a related events button', async () => { await expect( - simulator.mapStateTransitions(() => ({ + simulator.map(() => ({ relatedEventButtons: simulator.processNodeRelatedEventButton(entityIDs.origin).length, })) ).toYieldEqualTo({ diff --git a/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx b/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx new file mode 100644 index 0000000000000..78e5fd79bea13 --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { noAncestorsTwoChildren } from '../data_access_layer/mocks/no_ancestors_two_children'; +import { Simulator } from '../test_utilities/simulator'; +// Extend jest with a custom matcher +import '../test_utilities/extend_jest'; + +describe('Resolver: when analyzing a tree with no ancestors and two children', () => { + let simulator: Simulator; + let databaseDocumentID: string; + + // the resolver component instance ID, used by the react code to distinguish piece of global state from those used by other resolver instances + const resolverComponentInstanceID = 'resolverComponentInstanceID'; + + beforeEach(async () => { + // create a mock data access layer + const { metadata: dataAccessLayerMetadata, dataAccessLayer } = noAncestorsTwoChildren(); + + // save a reference to the `_id` supported by the mock data layer + databaseDocumentID = dataAccessLayerMetadata.databaseDocumentID; + + // create a resolver simulator, using the data access layer and an arbitrary component instance ID + simulator = new Simulator({ databaseDocumentID, dataAccessLayer, resolverComponentInstanceID }); + }); + + it('should show the node list', async () => { + await expect(simulator.map(() => simulator.nodeListElement().length)).toYieldEqualTo(1); + }); + + it('should have 3 nodes in the node list', async () => { + await expect(simulator.map(() => simulator.nodeListItems().length)).toYieldEqualTo(3); + }); + describe('when there is an item in the node list and it has been clicked', () => { + beforeEach(async () => { + const nodeListItems = await simulator.resolveWrapper(() => simulator.nodeListItems()); + expect(nodeListItems && nodeListItems.length).toBeTruthy(); + if (nodeListItems) { + nodeListItems.first().find('button').simulate('click'); + } + }); + it('should show the details for the first node', async () => { + await expect( + simulator.map(() => simulator.nodeDetailDescriptionListEntries()) + ).toYieldEqualTo([ + ['process.executable', 'executable'], + ['process.pid', '0'], + ['user.name', 'user.name'], + ['user.domain', 'user.domain'], + ['process.parent.pid', '0'], + ['process.hash.md5', 'hash.md5'], + ['process.args', 'args'], + ]); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/process_details.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/process_details.tsx index 03d9e4c2d5a2b..112a3400c4947 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/process_details.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/process_details.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { memo, useMemo } from 'react'; +import React, { memo, useMemo, HTMLAttributes } from 'react'; import { useSelector } from 'react-redux'; import { i18n } from '@kbn/i18n'; import { @@ -16,6 +16,7 @@ import { } from '@elastic/eui'; import styled from 'styled-components'; import { FormattedMessage } from 'react-intl'; +import { EuiDescriptionListProps } from '@elastic/eui/src/components/description_list/description_list'; import * as selectors from '../../store/selectors'; import * as event from '../../../../common/endpoint/models/event'; import { CrumbInfo, formatDate, StyledBreadcrumbs } from './panel_content_utilities'; @@ -51,9 +52,9 @@ export const ProcessDetails = memo(function ProcessDetails({ const processName = event.eventName(processEvent); const entityId = event.entityId(processEvent); const isProcessTerminated = useSelector(selectors.isProcessTerminated)(entityId); - const processInfoEntry = useMemo(() => { + const processInfoEntry: EuiDescriptionListProps['listItems'] = useMemo(() => { const eventTime = event.eventTimestamp(processEvent); - const dateTime = eventTime ? formatDate(eventTime) : ''; + const dateTime = eventTime === undefined ? null : formatDate(eventTime); const createdEntry = { title: '@timestamp', @@ -95,7 +96,7 @@ export const ProcessDetails = memo(function ProcessDetails({ description: argsForProcess(processEvent), }; - // This is the data in {title, description} form for the EUIDescriptionList to display + // This is the data in {title, description} form for the EuiDescriptionList to display const processDescriptionListData = [ createdEntry, pathEntry, @@ -107,7 +108,7 @@ export const ProcessDetails = memo(function ProcessDetails({ commandLineEntry, ] .filter((entry) => { - return entry.description; + return entry.description !== undefined; }) .map((entry) => { return { @@ -172,13 +173,24 @@ export const ProcessDetails = memo(function ProcessDetails({ + } + descriptionProps={ + { 'data-test-subj': 'resolver:node-detail:entry-description' } as HTMLAttributes< + HTMLElement + > + } compressed listItems={processInfoEntry} /> ); }); -ProcessDetails.displayName = 'ProcessDetails'; diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/process_list_with_counts.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/process_list_with_counts.tsx index 046c840470262..11f005f8acbcd 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/process_list_with_counts.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/process_list_with_counts.tsx @@ -150,7 +150,7 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({ const processTableView: ProcessTableView[] = useMemo( () => [...processNodePositions.keys()].map((processEvent) => { - let dateTime; + let dateTime: Date | undefined; const eventTime = event.timestampSafeVersion(processEvent); const name = event.processNameSafeVersion(processEvent); if (eventTime) { @@ -186,13 +186,15 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({ const children = useSelector(selectors.hasMoreChildren); const ancestors = useSelector(selectors.hasMoreAncestors); const showWarning = children === true || ancestors === true; + const rowProps = useMemo(() => ({ 'data-test-subj': 'resolver:node-list:item' }), []); return ( <> {showWarning && } - data-test-subj="resolver:panel:process-list" + rowProps={rowProps} + data-test-subj="resolver:node-list" items={processTableView} columns={columns} sorting @@ -200,4 +202,3 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({ ); }); -ProcessListWithCounts.displayName = 'ProcessListWithCounts'; From c6c300e8f82d138f28a080a7a34a78ebc27d9776 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Fri, 7 Aug 2020 11:21:44 -0400 Subject: [PATCH 10/19] [Canvas][tech-debt] Add Typescript to apps directory (#73766) Co-authored-by: Elastic Machine --- .../export/__tests__/export_app.test.tsx | 8 +- .../export/export/export_app.component.tsx | 63 ++++++++++++++ .../public/apps/export/export/export_app.js | 59 ------------- .../public/apps/export/export/export_app.ts | 21 +++++ .../canvas/public/apps/export/export/index.js | 30 ------- .../export/index.ts} | 5 +- .../public/apps/export/{index.js => index.ts} | 0 .../apps/export/{routes.js => routes.ts} | 13 ++- .../{home_app.js => home_app.component.tsx} | 10 ++- .../home/home_app/{index.js => home_app.ts} | 8 +- .../home_app/index.ts} | 5 +- .../public/apps/home/{index.js => index.ts} | 0 .../public/apps/home/{routes.js => routes.ts} | 0 .../canvas/public/apps/{index.js => index.ts} | 1 + .../apps/workpad/{index.js => index.ts} | 0 .../apps/workpad/{routes.js => routes.ts} | 24 ++++-- .../public/apps/workpad/workpad_app/index.js | 41 --------- .../public/apps/workpad/workpad_app/index.ts | 8 ++ .../workpad_app/workpad_app.component.tsx | 83 +++++++++++++++++++ .../apps/workpad/workpad_app/workpad_app.js | 81 ------------------ .../apps/workpad/workpad_app/workpad_app.ts | 32 +++++++ .../workpad_header.component.tsx | 12 ++- .../workpad_header/workpad_header.tsx | 29 +------ x-pack/plugins/canvas/types/canvas.ts | 4 + 24 files changed, 274 insertions(+), 263 deletions(-) create mode 100644 x-pack/plugins/canvas/public/apps/export/export/export_app.component.tsx delete mode 100644 x-pack/plugins/canvas/public/apps/export/export/export_app.js create mode 100644 x-pack/plugins/canvas/public/apps/export/export/export_app.ts delete mode 100644 x-pack/plugins/canvas/public/apps/export/export/index.js rename x-pack/plugins/canvas/public/apps/{workpad/workpad_app/load_workpad.js => export/export/index.ts} (67%) rename x-pack/plugins/canvas/public/apps/export/{index.js => index.ts} (100%) rename x-pack/plugins/canvas/public/apps/export/{routes.js => routes.ts} (79%) rename x-pack/plugins/canvas/public/apps/home/home_app/{home_app.js => home_app.component.tsx} (79%) rename x-pack/plugins/canvas/public/apps/home/home_app/{index.js => home_app.ts} (69%) rename x-pack/plugins/canvas/public/apps/{export/export/load_workpad.js => home/home_app/index.ts} (69%) rename x-pack/plugins/canvas/public/apps/home/{index.js => index.ts} (100%) rename x-pack/plugins/canvas/public/apps/home/{routes.js => routes.ts} (100%) rename x-pack/plugins/canvas/public/apps/{index.js => index.ts} (88%) rename x-pack/plugins/canvas/public/apps/workpad/{index.js => index.ts} (100%) rename x-pack/plugins/canvas/public/apps/workpad/{routes.js => routes.ts} (82%) delete mode 100644 x-pack/plugins/canvas/public/apps/workpad/workpad_app/index.js create mode 100644 x-pack/plugins/canvas/public/apps/workpad/workpad_app/index.ts create mode 100644 x-pack/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.component.tsx delete mode 100644 x-pack/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.js create mode 100644 x-pack/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.ts diff --git a/x-pack/plugins/canvas/public/apps/export/export/__tests__/export_app.test.tsx b/x-pack/plugins/canvas/public/apps/export/export/__tests__/export_app.test.tsx index b0a8d1e990e75..1bb58919b7fa6 100644 --- a/x-pack/plugins/canvas/public/apps/export/export/__tests__/export_app.test.tsx +++ b/x-pack/plugins/canvas/public/apps/export/export/__tests__/export_app.test.tsx @@ -6,8 +6,8 @@ import React from 'react'; import { mount } from 'enzyme'; -// @ts-expect-error untyped local -import { ExportApp } from '../export_app'; +import { ExportApp } from '../export_app.component'; +import { CanvasWorkpad } from '../../../../../types'; jest.mock('style-it', () => ({ it: (css: string, Component: any) => Component, @@ -23,7 +23,7 @@ jest.mock('../../../../components/link', () => ({ describe('', () => { test('renders as expected', () => { - const sampleWorkpad = { + const sampleWorkpad = ({ id: 'my-workpad-abcd', css: '', pages: [ @@ -34,7 +34,7 @@ describe('', () => { elements: [3, 4, 5, 6], }, ], - }; + } as any) as CanvasWorkpad; const page1 = mount( {}} /> diff --git a/x-pack/plugins/canvas/public/apps/export/export/export_app.component.tsx b/x-pack/plugins/canvas/public/apps/export/export/export_app.component.tsx new file mode 100644 index 0000000000000..03121e749d0dc --- /dev/null +++ b/x-pack/plugins/canvas/public/apps/export/export/export_app.component.tsx @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FC, useEffect } from 'react'; +import PropTypes from 'prop-types'; +// @ts-expect-error untyped library +import Style from 'style-it'; +// @ts-expect-error untyped local +import { WorkpadPage } from '../../../components/workpad_page'; +import { Link } from '../../../components/link'; +import { CanvasWorkpad } from '../../../../types'; + +interface Props { + workpad: CanvasWorkpad; + selectedPageIndex: number; + initializeWorkpad: () => void; +} + +export const ExportApp: FC = ({ workpad, selectedPageIndex, initializeWorkpad }) => { + const { id, pages, height, width } = workpad; + const activePage = pages[selectedPageIndex]; + const pageElementCount = activePage.elements.length; + + useEffect(() => initializeWorkpad()); + + return ( +
+
+
+ + Edit Workpad + +
+ {Style.it( + workpad.css, +
+ {}} + unregisterLayout={() => {}} + /> +
+ )} +
+
+ ); +}; + +ExportApp.propTypes = { + workpad: PropTypes.shape({ + id: PropTypes.string.isRequired, + pages: PropTypes.array.isRequired, + }).isRequired, + selectedPageIndex: PropTypes.number.isRequired, + initializeWorkpad: PropTypes.func.isRequired, +}; diff --git a/x-pack/plugins/canvas/public/apps/export/export/export_app.js b/x-pack/plugins/canvas/public/apps/export/export/export_app.js deleted file mode 100644 index 1d02d85cae0b3..0000000000000 --- a/x-pack/plugins/canvas/public/apps/export/export/export_app.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import PropTypes from 'prop-types'; -import Style from 'style-it'; -import { WorkpadPage } from '../../../components/workpad_page'; -import { Link } from '../../../components/link'; - -export class ExportApp extends React.PureComponent { - static propTypes = { - workpad: PropTypes.shape({ - id: PropTypes.string.isRequired, - pages: PropTypes.array.isRequired, - }).isRequired, - selectedPageIndex: PropTypes.number.isRequired, - initializeWorkpad: PropTypes.func.isRequired, - }; - - componentDidMount() { - this.props.initializeWorkpad(); - } - - render() { - const { workpad, selectedPageIndex } = this.props; - const { pages, height, width } = workpad; - const activePage = pages[selectedPageIndex]; - const pageElementCount = activePage.elements.length; - - return ( -
-
-
- - Edit Workpad - -
- {Style.it( - workpad.css, -
- {}} - unregisterLayout={() => {}} - /> -
- )} -
-
- ); - } -} diff --git a/x-pack/plugins/canvas/public/apps/export/export/export_app.ts b/x-pack/plugins/canvas/public/apps/export/export/export_app.ts new file mode 100644 index 0000000000000..b47d1950ec2b7 --- /dev/null +++ b/x-pack/plugins/canvas/public/apps/export/export/export_app.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 { connect } from 'react-redux'; +import { initializeWorkpad } from '../../../state/actions/workpad'; +import { getWorkpad, getSelectedPageIndex } from '../../../state/selectors/workpad'; +import { ExportApp as Component } from './export_app.component'; +import { State } from '../../../../types'; + +export const ExportApp = connect( + (state: State) => ({ + workpad: getWorkpad(state), + selectedPageIndex: getSelectedPageIndex(state), + }), + (dispatch) => ({ + initializeWorkpad: () => dispatch(initializeWorkpad()), + }) +)(Component); diff --git a/x-pack/plugins/canvas/public/apps/export/export/index.js b/x-pack/plugins/canvas/public/apps/export/export/index.js deleted file mode 100644 index 95c46d9e1c8ae..0000000000000 --- a/x-pack/plugins/canvas/public/apps/export/export/index.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { connect } from 'react-redux'; -import { compose, branch, renderComponent } from 'recompose'; -import { initializeWorkpad } from '../../../state/actions/workpad'; -import { getWorkpad, getSelectedPageIndex } from '../../../state/selectors/workpad'; -import { LoadWorkpad } from './load_workpad'; -import { ExportApp as Component } from './export_app'; - -const mapStateToProps = (state) => ({ - workpad: getWorkpad(state), - selectedPageIndex: getSelectedPageIndex(state), -}); - -const mapDispatchToProps = (dispatch) => ({ - initializeWorkpad() { - dispatch(initializeWorkpad()); - }, -}); - -const branches = [branch(({ workpad }) => workpad == null, renderComponent(LoadWorkpad))]; - -export const ExportApp = compose( - connect(mapStateToProps, mapDispatchToProps), - ...branches -)(Component); diff --git a/x-pack/plugins/canvas/public/apps/workpad/workpad_app/load_workpad.js b/x-pack/plugins/canvas/public/apps/export/export/index.ts similarity index 67% rename from x-pack/plugins/canvas/public/apps/workpad/workpad_app/load_workpad.js rename to x-pack/plugins/canvas/public/apps/export/export/index.ts index 388bf00723f82..81939d550a7ab 100644 --- a/x-pack/plugins/canvas/public/apps/workpad/workpad_app/load_workpad.js +++ b/x-pack/plugins/canvas/public/apps/export/export/index.ts @@ -4,6 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; - -export const LoadWorkpad = () =>
Load a workpad...
; +export { ExportApp } from './export_app'; +export { ExportApp as ExportAppComponent } from './export_app.component'; diff --git a/x-pack/plugins/canvas/public/apps/export/index.js b/x-pack/plugins/canvas/public/apps/export/index.ts similarity index 100% rename from x-pack/plugins/canvas/public/apps/export/index.js rename to x-pack/plugins/canvas/public/apps/export/index.ts diff --git a/x-pack/plugins/canvas/public/apps/export/routes.js b/x-pack/plugins/canvas/public/apps/export/routes.ts similarity index 79% rename from x-pack/plugins/canvas/public/apps/export/routes.js rename to x-pack/plugins/canvas/public/apps/export/routes.ts index 33e375115aa19..0b4f74149fb4f 100644 --- a/x-pack/plugins/canvas/public/apps/export/routes.js +++ b/x-pack/plugins/canvas/public/apps/export/routes.ts @@ -4,10 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Dispatch } from 'redux'; +// @ts-expect-error Untyped local import * as workpadService from '../../lib/workpad_service'; import { setWorkpad } from '../../state/actions/workpad'; +// @ts-expect-error Untyped local import { fetchAllRenderables } from '../../state/actions/elements'; +// @ts-expect-error Untyped local import { setPage } from '../../state/actions/pages'; +// @ts-expect-error Untyped local import { setAssets } from '../../state/actions/assets'; import { ExportApp } from './export'; @@ -18,7 +23,13 @@ export const routes = [ { name: 'exportWorkpad', path: '/pdf/:id/page/:page', - action: (dispatch) => async ({ params, router }) => { + action: (dispatch: Dispatch) => async ({ + params, + // @ts-expect-error Fix when Router is typed. + router, + }: { + params: { id: string; page: string }; + }) => { // load workpad if given a new id via url param const fetchedWorkpad = await workpadService.get(params.id); const pageNumber = parseInt(params.page, 10); diff --git a/x-pack/plugins/canvas/public/apps/home/home_app/home_app.js b/x-pack/plugins/canvas/public/apps/home/home_app/home_app.component.tsx similarity index 79% rename from x-pack/plugins/canvas/public/apps/home/home_app/home_app.js rename to x-pack/plugins/canvas/public/apps/home/home_app/home_app.component.tsx index bfa4abbf7c56d..3c2e989cc8e51 100644 --- a/x-pack/plugins/canvas/public/apps/home/home_app/home_app.js +++ b/x-pack/plugins/canvas/public/apps/home/home_app/home_app.component.tsx @@ -4,12 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { FC } from 'react'; import { EuiPage, EuiPageBody, EuiPageContent } from '@elastic/eui'; +// @ts-expect-error untyped local import { WorkpadManager } from '../../../components/workpad_manager'; +// @ts-expect-error untyped local import { setDocTitle } from '../../../lib/doc_title'; -export const HomeApp = ({ onLoad = () => {} }) => { +interface Props { + onLoad: () => void; +} + +export const HomeApp: FC = ({ onLoad = () => {} }) => { onLoad(); setDocTitle('Canvas'); return ( diff --git a/x-pack/plugins/canvas/public/apps/home/home_app/index.js b/x-pack/plugins/canvas/public/apps/home/home_app/home_app.ts similarity index 69% rename from x-pack/plugins/canvas/public/apps/home/home_app/index.js rename to x-pack/plugins/canvas/public/apps/home/home_app/home_app.ts index f78ee1f8a18af..ff9d1c1cc63ac 100644 --- a/x-pack/plugins/canvas/public/apps/home/home_app/index.js +++ b/x-pack/plugins/canvas/public/apps/home/home_app/home_app.ts @@ -6,12 +6,10 @@ import { connect } from 'react-redux'; import { resetWorkpad } from '../../../state/actions/workpad'; -import { HomeApp as Component } from './home_app'; +import { HomeApp as Component } from './home_app.component'; -const mapDispatchToProps = (dispatch) => ({ +export const HomeApp = connect(null, (dispatch) => ({ onLoad() { dispatch(resetWorkpad()); }, -}); - -export const HomeApp = connect(null, mapDispatchToProps)(Component); +}))(Component); diff --git a/x-pack/plugins/canvas/public/apps/export/export/load_workpad.js b/x-pack/plugins/canvas/public/apps/home/home_app/index.ts similarity index 69% rename from x-pack/plugins/canvas/public/apps/export/export/load_workpad.js rename to x-pack/plugins/canvas/public/apps/home/home_app/index.ts index 388bf00723f82..8ea92312e3e50 100644 --- a/x-pack/plugins/canvas/public/apps/export/export/load_workpad.js +++ b/x-pack/plugins/canvas/public/apps/home/home_app/index.ts @@ -4,6 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; - -export const LoadWorkpad = () =>
Load a workpad...
; +export { HomeApp } from './home_app'; +export { HomeApp as HomeAppComponent } from './home_app.component'; diff --git a/x-pack/plugins/canvas/public/apps/home/index.js b/x-pack/plugins/canvas/public/apps/home/index.ts similarity index 100% rename from x-pack/plugins/canvas/public/apps/home/index.js rename to x-pack/plugins/canvas/public/apps/home/index.ts diff --git a/x-pack/plugins/canvas/public/apps/home/routes.js b/x-pack/plugins/canvas/public/apps/home/routes.ts similarity index 100% rename from x-pack/plugins/canvas/public/apps/home/routes.js rename to x-pack/plugins/canvas/public/apps/home/routes.ts diff --git a/x-pack/plugins/canvas/public/apps/index.js b/x-pack/plugins/canvas/public/apps/index.ts similarity index 88% rename from x-pack/plugins/canvas/public/apps/index.js rename to x-pack/plugins/canvas/public/apps/index.ts index c014349ca18da..8b3d378e23f80 100644 --- a/x-pack/plugins/canvas/public/apps/index.js +++ b/x-pack/plugins/canvas/public/apps/index.ts @@ -8,6 +8,7 @@ import * as home from './home'; import * as workpad from './workpad'; import * as exp from './export'; +// @ts-expect-error Router and routes are not yet strongly typed export const routes = [].concat(workpad.routes, home.routes, exp.routes); export const apps = [workpad.WorkpadApp, home.HomeApp, exp.ExportApp]; diff --git a/x-pack/plugins/canvas/public/apps/workpad/index.js b/x-pack/plugins/canvas/public/apps/workpad/index.ts similarity index 100% rename from x-pack/plugins/canvas/public/apps/workpad/index.js rename to x-pack/plugins/canvas/public/apps/workpad/index.ts diff --git a/x-pack/plugins/canvas/public/apps/workpad/routes.js b/x-pack/plugins/canvas/public/apps/workpad/routes.ts similarity index 82% rename from x-pack/plugins/canvas/public/apps/workpad/routes.js rename to x-pack/plugins/canvas/public/apps/workpad/routes.ts index a330020b741ac..d83f85f717305 100644 --- a/x-pack/plugins/canvas/public/apps/workpad/routes.js +++ b/x-pack/plugins/canvas/public/apps/workpad/routes.ts @@ -4,17 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ErrorStrings } from '../../../i18n'; +import { Dispatch } from 'redux'; +// @ts-expect-error import * as workpadService from '../../lib/workpad_service'; import { notifyService } from '../../services'; import { getBaseBreadcrumb, getWorkpadBreadcrumb, setBreadcrumb } from '../../lib/breadcrumbs'; +// @ts-expect-error import { getDefaultWorkpad } from '../../state/defaults'; import { setWorkpad } from '../../state/actions/workpad'; +// @ts-expect-error import { setAssets, resetAssets } from '../../state/actions/assets'; +// @ts-expect-error import { setPage } from '../../state/actions/pages'; import { getWorkpad } from '../../state/selectors/workpad'; +// @ts-expect-error import { setZoomScale } from '../../state/actions/transient'; +import { ErrorStrings } from '../../../i18n'; import { WorkpadApp } from './workpad_app'; +import { State } from '../../../types'; const { workpadRoutes: strings } = ErrorStrings; @@ -25,7 +32,8 @@ export const routes = [ { name: 'createWorkpad', path: '/create', - action: (dispatch) => async ({ router }) => { + // @ts-expect-error Fix when Router is typed. + action: (dispatch: Dispatch) => async ({ router }) => { const newWorkpad = getDefaultWorkpad(); try { await workpadService.create(newWorkpad); @@ -46,7 +54,13 @@ export const routes = [ { name: 'loadWorkpad', path: '/:id(/page/:page)', - action: (dispatch, getState) => async ({ params, router }) => { + action: (dispatch: Dispatch, getState: () => State) => async ({ + params, + // @ts-expect-error Fix when Router is typed. + router, + }: { + params: { id: string; page?: string }; + }) => { // load workpad if given a new id via url param const state = getState(); const currentWorkpad = getWorkpad(state); @@ -70,10 +84,10 @@ export const routes = [ // fetch the workpad again, to get changes const workpad = getWorkpad(getState()); - const pageNumber = parseInt(params.page, 10); + const pageNumber = params.page ? parseInt(params.page, 10) : null; // no page provided, append current page to url - if (isNaN(pageNumber)) { + if (!pageNumber || isNaN(pageNumber)) { return router.redirectTo('loadWorkpad', { id: workpad.id, page: workpad.page + 1 }); } diff --git a/x-pack/plugins/canvas/public/apps/workpad/workpad_app/index.js b/x-pack/plugins/canvas/public/apps/workpad/workpad_app/index.js deleted file mode 100644 index ac50cd3fb99b6..0000000000000 --- a/x-pack/plugins/canvas/public/apps/workpad/workpad_app/index.js +++ /dev/null @@ -1,41 +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 { connect } from 'react-redux'; -import { compose, branch, renderComponent } from 'recompose'; -import { selectToplevelNodes } from '../../../state/actions/transient'; -import { canUserWrite, getAppReady } from '../../../state/selectors/app'; -import { getWorkpad, isWriteable } from '../../../state/selectors/workpad'; -import { LoadWorkpad } from './load_workpad'; -import { WorkpadApp as Component } from './workpad_app'; -import { withElementsLoadedTelemetry } from './workpad_telemetry'; - -export { WORKPAD_CONTAINER_ID } from './workpad_app'; - -const mapStateToProps = (state) => { - const appReady = getAppReady(state); - - return { - isWriteable: isWriteable(state) && canUserWrite(state), - appReady: typeof appReady === 'object' ? appReady : { ready: appReady }, - workpad: getWorkpad(state), - }; -}; - -const mapDispatchToProps = (dispatch) => ({ - deselectElement(ev) { - ev && ev.stopPropagation(); - dispatch(selectToplevelNodes([])); - }, -}); - -const branches = [branch(({ workpad }) => workpad == null, renderComponent(LoadWorkpad))]; - -export const WorkpadApp = compose( - connect(mapStateToProps, mapDispatchToProps), - ...branches, - withElementsLoadedTelemetry -)(Component); diff --git a/x-pack/plugins/canvas/public/apps/workpad/workpad_app/index.ts b/x-pack/plugins/canvas/public/apps/workpad/workpad_app/index.ts new file mode 100644 index 0000000000000..a00bf855ba376 --- /dev/null +++ b/x-pack/plugins/canvas/public/apps/workpad/workpad_app/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 { WorkpadApp } from './workpad_app'; +export { WorkpadApp as WorkpadAppComponent } from './workpad_app.component'; diff --git a/x-pack/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.component.tsx b/x-pack/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.component.tsx new file mode 100644 index 0000000000000..791f40f0219cd --- /dev/null +++ b/x-pack/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.component.tsx @@ -0,0 +1,83 @@ +/* + * 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, MouseEventHandler, useRef } from 'react'; +import PropTypes from 'prop-types'; +import { Sidebar } from '../../../components/sidebar'; +import { Toolbar } from '../../../components/toolbar'; +// @ts-expect-error Untyped local +import { Workpad } from '../../../components/workpad'; +import { WorkpadHeader } from '../../../components/workpad_header'; +import { CANVAS_LAYOUT_STAGE_CONTENT_SELECTOR } from '../../../../common/lib/constants'; +import { CommitFn } from '../../../../types'; + +export const WORKPAD_CONTAINER_ID = 'canvasWorkpadContainer'; + +interface Props { + deselectElement?: MouseEventHandler; + isWriteable: boolean; +} + +export const WorkpadApp: FC = ({ deselectElement, isWriteable }) => { + const interactivePageLayout = useRef(null); // future versions may enable editing on multiple pages => use array then + + const registerLayout = (newLayout: CommitFn) => { + if (interactivePageLayout.current !== newLayout) { + interactivePageLayout.current = newLayout; + } + }; + + const unregisterLayout = (oldLayout: CommitFn) => { + if (interactivePageLayout.current === oldLayout) { + interactivePageLayout.current = null; + } + }; + + const commit = interactivePageLayout.current || (() => {}); + + return ( +
+
+
+
+
+ +
+ +
+ {/* NOTE: canvasWorkpadContainer is used for exporting */} +
+ +
+
+
+ + {isWriteable && ( +
+ +
+ )} +
+ +
+ +
+
+
+ ); +}; + +WorkpadApp.propTypes = { + isWriteable: PropTypes.bool.isRequired, + deselectElement: PropTypes.func, +}; diff --git a/x-pack/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.js b/x-pack/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.js deleted file mode 100644 index fc3ac9922355a..0000000000000 --- a/x-pack/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.js +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import PropTypes from 'prop-types'; -import { Sidebar } from '../../../components/sidebar'; -import { Toolbar } from '../../../components/toolbar'; -import { Workpad } from '../../../components/workpad'; -import { WorkpadHeader } from '../../../components/workpad_header'; -import { CANVAS_LAYOUT_STAGE_CONTENT_SELECTOR } from '../../../../common/lib/constants'; - -export const WORKPAD_CONTAINER_ID = 'canvasWorkpadContainer'; - -export class WorkpadApp extends React.PureComponent { - static propTypes = { - isWriteable: PropTypes.bool.isRequired, - deselectElement: PropTypes.func, - }; - - interactivePageLayout = null; // future versions may enable editing on multiple pages => use array then - - registerLayout(newLayout) { - if (this.interactivePageLayout !== newLayout) { - this.interactivePageLayout = newLayout; - } - } - - unregisterLayout(oldLayout) { - if (this.interactivePageLayout === oldLayout) { - this.interactivePageLayout = null; - } - } - - render() { - const { isWriteable, deselectElement } = this.props; - - return ( -
-
-
-
-
- {})} /> -
- -
- {/* NOTE: canvasWorkpadContainer is used for exporting */} -
- -
-
-
- - {isWriteable && ( -
- -
- )} -
- -
- -
-
-
- ); - } -} diff --git a/x-pack/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.ts b/x-pack/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.ts new file mode 100644 index 0000000000000..46f2efaf5e7d2 --- /dev/null +++ b/x-pack/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.ts @@ -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 { MouseEventHandler } from 'react'; +import { Dispatch } from 'redux'; +import { connect } from 'react-redux'; +// @ts-expect-error untyped local +import { selectToplevelNodes } from '../../../state/actions/transient'; +import { canUserWrite } from '../../../state/selectors/app'; +import { getWorkpad, isWriteable } from '../../../state/selectors/workpad'; +import { WorkpadApp as Component } from './workpad_app.component'; +import { withElementsLoadedTelemetry } from './workpad_telemetry'; +import { State } from '../../../../types'; + +export { WORKPAD_CONTAINER_ID } from './workpad_app.component'; + +const mapDispatchToProps = (dispatch: Dispatch): { deselectElement: MouseEventHandler } => ({ + deselectElement: (ev) => { + ev.stopPropagation(); + dispatch(selectToplevelNodes([])); + }, +}); + +export const WorkpadApp = connect( + (state: State) => ({ + isWriteable: isWriteable(state) && canUserWrite(state), + workpad: getWorkpad(state), + }), + mapDispatchToProps +)(withElementsLoadedTelemetry(Component)); diff --git a/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx index eb4b451896b46..b1e87ca67f5e5 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx @@ -18,22 +18,25 @@ import { EditMenu } from './edit_menu'; import { ElementMenu } from './element_menu'; import { ShareMenu } from './share_menu'; import { ViewMenu } from './view_menu'; +import { CommitFn } from '../../../types'; const { WorkpadHeader: strings } = ComponentStrings; export interface Props { isWriteable: boolean; - toggleWriteable: () => void; canUserWrite: boolean; - commit: (type: string, payload: any) => any; + commit: CommitFn; + onSetWriteable?: (writeable: boolean) => void; } export const WorkpadHeader: FunctionComponent = ({ isWriteable, canUserWrite, - toggleWriteable, commit, + onSetWriteable = () => {}, }) => { + const toggleWriteable = () => onSetWriteable(!isWriteable); + const keyHandler = (action: string) => { if (action === 'EDITING') { toggleWriteable(); @@ -145,6 +148,7 @@ export const WorkpadHeader: FunctionComponent = ({ WorkpadHeader.propTypes = { isWriteable: PropTypes.bool, - toggleWriteable: PropTypes.func, + commit: PropTypes.func.isRequired, + onSetWriteable: PropTypes.func, canUserWrite: PropTypes.bool, }; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.tsx b/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.tsx index 1f630040b0c36..0661aa4be4313 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.tsx @@ -10,37 +10,16 @@ import { canUserWrite } from '../../state/selectors/app'; import { getSelectedPage, isWriteable } from '../../state/selectors/workpad'; import { setWriteable } from '../../state/actions/workpad'; import { State } from '../../../types'; -import { WorkpadHeader as Component, Props as ComponentProps } from './workpad_header.component'; +import { WorkpadHeader as Component } from './workpad_header.component'; -interface StateProps { - isWriteable: boolean; - canUserWrite: boolean; - selectedPage: string; -} - -interface DispatchProps { - setWriteable: (isWorkpadWriteable: boolean) => void; -} - -const mapStateToProps = (state: State): StateProps => ({ +const mapStateToProps = (state: State) => ({ isWriteable: isWriteable(state) && canUserWrite(state), canUserWrite: canUserWrite(state), selectedPage: getSelectedPage(state), }); const mapDispatchToProps = (dispatch: Dispatch) => ({ - setWriteable: (isWorkpadWriteable: boolean) => dispatch(setWriteable(isWorkpadWriteable)), -}); - -const mergeProps = ( - stateProps: StateProps, - dispatchProps: DispatchProps, - ownProps: ComponentProps -): ComponentProps => ({ - ...stateProps, - ...dispatchProps, - ...ownProps, - toggleWriteable: () => dispatchProps.setWriteable(!stateProps.isWriteable), + onSetWriteable: (isWorkpadWriteable: boolean) => dispatch(setWriteable(isWorkpadWriteable)), }); -export const WorkpadHeader = connect(mapStateToProps, mapDispatchToProps, mergeProps)(Component); +export const WorkpadHeader = connect(mapStateToProps, mapDispatchToProps)(Component); diff --git a/x-pack/plugins/canvas/types/canvas.ts b/x-pack/plugins/canvas/types/canvas.ts index cc07f498f1eec..6b3f9ad3e8043 100644 --- a/x-pack/plugins/canvas/types/canvas.ts +++ b/x-pack/plugins/canvas/types/canvas.ts @@ -76,3 +76,7 @@ export interface CanvasWorkpadBoundingBox { top: number; bottom: number; } + +export type LayoutState = any; + +export type CommitFn = (type: string, payload: any) => LayoutState; From 37ce10158ad8911bbbd951a5b5e329858beec091 Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Mon, 10 Aug 2020 09:08:36 +0200 Subject: [PATCH 11/19] RFC: encryption key rotation support for the `encryptedSavedObjects` plugin (#72828) --- rfcs/text/0012_encryption_key_rotation.md | 119 ++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 rfcs/text/0012_encryption_key_rotation.md diff --git a/rfcs/text/0012_encryption_key_rotation.md b/rfcs/text/0012_encryption_key_rotation.md new file mode 100644 index 0000000000000..d984d1157a0a1 --- /dev/null +++ b/rfcs/text/0012_encryption_key_rotation.md @@ -0,0 +1,119 @@ +- Start Date: 2020-07-22 +- RFC PR: [#72828](https://github.com/elastic/kibana/pull/72828) +- Kibana Issue: (leave this empty) + +# Summary + +This RFC proposes a way of the encryption key (`xpack.encryptedSavedObjects.encryptionKey`) rotation that would allow administrators to seamlessly change existing encryption key without any data loss and manual intervention. + +# Basic example + +When administrators decide to rotate encryption key they will have to generate a new one and move the old key(s) to the `keyRotation` section in the `kibana.yml`: + +```yaml +xpack.encryptedSavedObjects: + encryptionKey: "NEW-encryption-key" + keyRotation: + decryptionOnlyKeys: ["OLD-encryption-key-1", "OLD-encryption-key-2"] +``` + +Before old decryption-only key is disposed administrators may want to call a dedicated and _protected_ API endpoint that will go through all registered Saved Objects with encrypted attributes and try to re-encrypt them with the primary encryption key: + +```http request +POST https://localhost:5601/api/encrypted_saved_objects/rotate_key?conflicts=abort +Content-Type: application/json +Kbn-Xsrf: true +``` + +# Motivation + +Today when encryption key changes we can no longer decrypt Saved Objects attributes that were previously encrypted with the `EncryptedSavedObjects` plugin. We handle this case in two different ways depending on whether consumers explicitly requested decryption or not: + +* If consumers explicitly request decryption via `getDecryptedAsInternalUser()` we abort operation and throw exception. +* If consumers fetch Saved Objects with encrypted attributes that should be automatically decrypted (the ones with `dangerouslyExposeValue: true` marker) via standard Saved Objects APIs we don't abort operation, but rather strip all encrypted attributes from the response and record decryption error in the `error` Saved Object field. +* If Kibana tries to migrate encrypted Saved Objects at the start up time we abort operation and throw exception. + +In both of these cases we throw or record error with the specific type to allow consumers to gracefully handle this scenario and either drop Saved Objects with unrecoverable encrypted attributes or facilitate the process of re-entering and re-encryption of the new values. + +This approach works reasonably well in some scenarios, but it may become very troublesome if we have to deal with lots of Saved Objects. Moreover, we'd like to recommend our users to periodically rotate encryption keys even if they aren't compromised. Hence, we need to provide a way of seamless migration of the existing encrypted Saved Objects to a new encryption key. + +There are two main scenarios we'd like to cover in this RFC: + +## Encryption key is not available + +Administrators may lose existing encryption key or explicitly decide to not use it if it was compromised and users can no longer trust encrypted content that may have been tampered with. In this scenario encrypted portion of the existing Saved Objects is considered lost, and the only way to recover from this state is a manual intervention described previously. That means `EncryptedSavedObjects` plugin consumers __should__ continue supporting this scenario even after we implement a proper encryption key rotation mechanism described in this RFC. + +## Encryption key is available, but needs to be rotated + +In this scenario a new encryption key (primary encryption key) will be generated, and we will use it to encrypt new or updated Saved Objects. We will still need to know the old encryption key to decrypt existing attributes, but we will no longer use this key to encrypt any of the new or existing Saved Objects. It's also should be possible to have multiple old decryption-only keys. + +The old old decryption-only keys should be eventually disposed and users should have a way to make sure all existing Saved Objects are re-encrypted with the new primary encryption key. + +__NOTE:__ users can get into a state when different Saved Objects are encrypted with different encryption keys even if they didn't intend to rotate the encryption key. We anticipate that it can happen during initial Elastic Stack HA setup, when by mistake or intentionally different Kibana instances were using different encryption keys. Key rotation mechanism can help to fix this issue without a data loss. + +# Detailed design + +The core idea is that when the encryption key needs to be rotated then a new key is generated and becomes a primary one, and the old one moves to the `keyRotation` section: + +```yaml +xpack.encryptedSavedObjects: + encryptionKey: "NEW-encryption-key" + keyRotation: + decryptionOnlyKeys: ["OLD-encryption-key"] +``` + +As the name implies, the key from the `decryptionOnlyKeys` is only used to decrypt content that we cannot decrypt with the primary encryption key. It's allowed to have multiple decryption-only keys at the same time. When user creates a new Saved Object or updates the existing one then its content is always encrypted with the primary encryption key. Config schema won't allow having the same key in `encryptionKey` and `decryptionOnlyKeys`. + +Having multiple decryption keys at the same time brings one problem though: we need to figure out which key to use to decrypt specific Saved Object. If our encryption keys could have a unique ID that we would store together with the encrypted data (we cannot use encryption key hash for that for obvious reasons) we could know for sure which key to use, but we don't have such functionality right now and it may not be the easiest one to manage through `yml` configuration anyway. + +Instead, this RFC proposes to try available existing decryption keys one by one to decrypt Saved Object and always start from the primary one. This way we won't incur any penalty while decrypting Saved Objects that are already encrypted with the primary encryption key, but there will still be some cost when we have to perform multiple decryption attempts. See the [`Drawbacks`](#drawbacks) section for the details. + +Technically just having `decryptionOnlyKeys` would be enough to cover the majority of the use cases, but the old decryption-only keys should be eventually disposed. At this point administrators would like to make sure _all_ Saved Objects are encrypted with the new primary encryption key. Another reason to re-encrypt all existing Saved Objects with the new key at once is to preventively reduce the performance impact of the multiple decryption attempts. + +We'd like to make this process as simple as possible while meeting the following requirements: + +* It should not be required to restart Kibana to perform this type of migration since Saved Objects encrypted with the another encryption key can theoretically appear at any point in time. +* It should be possible to integrate this operation into other operational flows our users may have and any user-friendly key management UIs we may introduce in this future. +* Any possible failures that may happen during this operation shouldn't make Kibana nonfunctional. +* Ordinary users should not be able to trigger this migration since it may consume a considerable amount of computing resources. + +We think that the best option we have right now is a dedicated API endpoint that would trigger this migration: + +```http request +POST https://localhost:5601/api/encrypted_saved_objects/rotate_key?conflicts=abort +Content-Type: application/json +Kbn-Xsrf: true +``` + +This will be a protected endpoint and only user with enough privileges will be able to use it. + +Under the hood we'll scroll over all Saved Objects that are registered with `EncryptedSavedObjects` plugin and re-encrypt attributes only for those of them that can only be decrypted with any of the old decryption-only keys. Saved Objects that can be decrypted with the primary encryption key will be ignored. We'll also ignore the ones that cannot be decrypted with any of the available decryption keys at all, and presumably return their IDs in the response. + +As for any other encryption or decryption operation we'll record relevant bits in the audit logs. + +# Benefits + +* The concept of decryption-only keys is easy to grasp and allows Kibana to function even if it has a mix of Saved Objects encrypted with different encryption keys. +* Support of the key rotation out of the box decreases the chances of the data loss and makes `EncryptedSavedObjects` story more secure and approachable overall. + +# Drawbacks + +* Multiple decryption attempts affect performance. See [the performance test results](https://github.com/elastic/kibana/pull/72420#issue-453400211) for more details, but making two decryption attempts is basically twice as slow as with a single attempt. Although it's only relevant for the encrypted Saved Objects migration performed at the start up time and batch operations that trigger automatic decryption (only for the Saved Objects registered with `dangerouslyExposeValue: true` marker that nobody is using in Kibana right now), we may have more use cases in the future. +* Historically we supported Kibana features with either configuration or dedicated UI, but in this case we want to introduce an API endpoint that _should be_ used directly. We may have a key management UI in the future though. + +# Alternatives + +We cannot think of any better alternative for `decryptionOnlyKeys` at the moment, but instead of API endpoint for the batch re-encryption we could potentially use another `kibana.yml` config option. For example `keyRotation.mode: onWrite | onStart | both`, but it feels a bit hacky and cannot be really integrated with anything else. + +# Adoption strategy + +Adoption strategy is pretty straightforward since the feature is an enhancement and doesn't bring any BWC concerns. + +# How we teach this + +Key rotation is a well-known paradigm. We'll update `README.md` of the `EncryptedSavedObjects` plugin and create a dedicated section in the public Kibana documentation. + +# Unresolved questions + +* Is it reasonable to have this feature in Basic? +* Are there any other use-cases that are not covered by the proposal? From 0a65e172a1563c9b0e39d06c5a4c0a3a4af480ec Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 10 Aug 2020 09:30:51 +0200 Subject: [PATCH 12/19] [ES UI Shared] Added README (#72034) * Added readme to es-ui-shared * implement PR feedback; clarify terms and tighten grammar * added note about intended users of es ui shared modules --- src/plugins/es_ui_shared/README.md | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/plugins/es_ui_shared/README.md diff --git a/src/plugins/es_ui_shared/README.md b/src/plugins/es_ui_shared/README.md new file mode 100644 index 0000000000000..5a9091e2dd1eb --- /dev/null +++ b/src/plugins/es_ui_shared/README.md @@ -0,0 +1,32 @@ +## ES UI shared modules + +This plugin contains reusable code in the form of self-contained modules (or libraries). Each of these modules exports a set of functionality relevant to the domain of the module. + +**Please note**: Modules in ES UI shared are intended for use by the ES UI Management Team (elastic/es-ui@) only. Please reach out to us if there is something you would like to contribute or use in these modules. + +## Files and folders overview + +- `./public` | `./server`. Folders for grouping server or public code according to the Kibana plugin pattern. +- `./__packages_do_not_import__` is where actual functionality is kept. This enables modules more control over what functionality is directly exported and prevents parts of modules to be depended on externally in unintended ways. +- `./public/index.ts` | `./server/index.ts` These files export modules (simple JavaScript objects). For example, `Monaco` is the name of a module. In this way, modules namespace all of their exports and do not have to be concerned about name collisions from other modules. + +## Conventions for adding code + +When adding new functionality, look at the folders in `./__packages_do_not_import__` and consider whether your functionality falls into any of those modules. + +If it does not, you should create a module and expose it to public or server code (or both) following the conventions described above. + +### Example + +If I wanted to add functionality for calculating a Fibonacci sequence browser-side one would do the following: + +1. Create a folder `./__packages_do_not_import__/math`. The name of the folder should be a snake_case version of the module name. In this case `Math` -> `math`. Another case, `IndexManagement` -> `index_management`. +2. Write your function in `./__packages_do_not_import__/math/calculate_fibonacci.ts`, adding any relevant tests in the same folder. +3. Export functionality intended _for consumers_ from `./__packages_do_not_import__/math/index.ts`. +4. Create a folder `./public/math`. +5. Export all functionality from `./__packages_do_not_import__/math` in `./public/math/index.ts`. +6. In `./public/index.ts` import `./public/math` using `import * as Math from './public/math;`. The name (`Math`) given here is really important and will be what consumers depend on. +7. Add the `Math` module to the list of exported modules in `./public/index.ts`, e.g. `export { <...other modules>, Math }` +8. Use `Math` in your public side code elsewhere! + +This example assumes no other appropriate home for such a function exists. From ce025732a17bb1a1488e6d01abf73545d9a393ba Mon Sep 17 00:00:00 2001 From: Dmitry Lemeshko Date: Mon, 10 Aug 2020 10:09:30 +0200 Subject: [PATCH 13/19] add retry for checking Add button (#74551) Co-authored-by: Elastic Machine --- .../test/functional/apps/dashboard/_async_dashboard.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/x-pack/test/functional/apps/dashboard/_async_dashboard.ts b/x-pack/test/functional/apps/dashboard/_async_dashboard.ts index cc30a7a7e640f..8851c83dea1ff 100644 --- a/x-pack/test/functional/apps/dashboard/_async_dashboard.ts +++ b/x-pack/test/functional/apps/dashboard/_async_dashboard.ts @@ -27,8 +27,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'timePicker', ]); - // Flakky: https://github.com/elastic/kibana/issues/65949 - describe.skip('sample data dashboard', function describeIndexTests() { + describe('sample data dashboard', function describeIndexTests() { before(async () => { await PageObjects.common.sleep(5000); await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', { @@ -36,8 +35,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.home.addSampleDataSet('flights'); - const isInstalled = await PageObjects.home.isSampleDataSetInstalled('flights'); - expect(isInstalled).to.be(true); + await retry.tryForTime(10000, async () => { + const isInstalled = await PageObjects.home.isSampleDataSetInstalled('flights'); + expect(isInstalled).to.be(true); + }); + // add the range of the sample data so we can pick it in the quick pick list const SAMPLE_DATA_RANGE = `[ { From ad8502c8d9cbf35822ed187aae9ea31e5eca21ab Mon Sep 17 00:00:00 2001 From: spalger Date: Mon, 10 Aug 2020 01:25:29 -0700 Subject: [PATCH 14/19] update code-exploration docs --- docs/developer/architecture/code-exploration.asciidoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/developer/architecture/code-exploration.asciidoc b/docs/developer/architecture/code-exploration.asciidoc index bb7222020180c..d9502e4cb47ee 100644 --- a/docs/developer/architecture/code-exploration.asciidoc +++ b/docs/developer/architecture/code-exploration.asciidoc @@ -86,9 +86,9 @@ Contains the Discover application and the saved search embeddable. Embeddables are re-usable widgets that can be rendered in any environment or plugin. Developers can embed them directly in their plugin. End users can dynamically add them to any embeddable containers. -- {kib-repo}blob/{branch}/src/plugins/es_ui_shared[esUiShared] +- {kib-repo}blob/{branch}/src/plugins/es_ui_shared/README.md[esUiShared] -WARNING: Missing README. +This plugin contains reusable code in the form of self-contained modules (or libraries). Each of these modules exports a set of functionality relevant to the domain of the module. - {kib-repo}blob/{branch}/src/plugins/expressions/README.md[expressions] From f7f2988aa2d6a557ee2aa6aa184ea774bb57f345 Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Mon, 10 Aug 2020 13:04:57 +0200 Subject: [PATCH 15/19] [ML] Functional tests - stabilize DFA job type check (#74631) This PR stabilizes the data frame analytics job type assertion by adding a retry. --- .../ml/data_frame_analytics_creation.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts index a49febfe68f61..d8df2fb869ed7 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts @@ -46,14 +46,16 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( }, async assertJobTypeSelection(expectedSelection: string) { - const actualSelection = await testSubjects.getAttribute( - 'mlAnalyticsCreateJobWizardJobTypeSelect', - 'value' - ); - expect(actualSelection).to.eql( - expectedSelection, - `Job type selection should be '${expectedSelection}' (got '${actualSelection}')` - ); + await retry.tryForTime(5000, async () => { + const actualSelection = await testSubjects.getAttribute( + 'mlAnalyticsCreateJobWizardJobTypeSelect', + 'value' + ); + expect(actualSelection).to.eql( + expectedSelection, + `Job type selection should be '${expectedSelection}' (got '${actualSelection}')` + ); + }); }, async selectJobType(jobType: string) { From cccf15a3f42943aa81393fe532bfb4ce48ea6994 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Mon, 10 Aug 2020 07:35:12 -0500 Subject: [PATCH 16/19] Index pattern field class refactor (#73180) - Better distinction and relationship between IndexPatternField and its spec - IndexPatternField class is no longer defined via object mutation - Reduction of dependencies - UI code moved into Index Pattern class (will be removed in next ticket) - IndexPattern field list was previously composed of IndexPatternFields or specs, now only IndexPatternFields - IndexPattern field list was previously redefined when loading fields, now only its contents are replaced. --- ...ins-data-public.fieldlist._constructor_.md | 23 +++ ...lugin-plugins-data-public.fieldlist.add.md | 11 + ...plugins-data-public.fieldlist.getbyname.md | 11 + ...plugins-data-public.fieldlist.getbytype.md | 11 + ...na-plugin-plugins-data-public.fieldlist.md | 31 +++ ...in-plugins-data-public.fieldlist.remove.md | 11 + ...plugins-data-public.fieldlist.removeall.md | 11 + ...lugins-data-public.fieldlist.replaceall.md | 11 + ...in-plugins-data-public.fieldlist.tospec.md | 25 +++ ...in-plugins-data-public.fieldlist.update.md | 11 + ...-public.getindexpatternfieldlistcreator.md | 11 - ...public.iindexpatternfieldlist.getbyname.md | 6 +- ...public.iindexpatternfieldlist.getbytype.md | 6 +- ...gins-data-public.iindexpatternfieldlist.md | 4 +- ...public.iindexpatternfieldlist.removeall.md | 15 ++ ...ublic.iindexpatternfieldlist.replaceall.md | 22 ++ ...data-public.indexpattern.getfieldbyname.md | 4 +- ...ublic.indexpattern.getformatterforfield.md | 22 ++ ...ublic.indexpattern.getnonscriptedfields.md | 4 +- ...a-public.indexpattern.getscriptedfields.md | 4 +- ...s-data-public.indexpattern.gettimefield.md | 4 +- ...plugin-plugins-data-public.indexpattern.md | 3 +- ...public.indexpattern.removescriptedfield.md | 4 +- ...-public.indexpatternfield._constructor_.md | 12 +- ...a-public.indexpatternfield.aggregatable.md | 2 +- ....indexpatternfield.conflictdescriptions.md | 4 +- ...ins-data-public.indexpatternfield.count.md | 4 +- ...ta-public.indexpatternfield.displayname.md | 2 +- ...s-data-public.indexpatternfield.estypes.md | 2 +- ...ata-public.indexpatternfield.filterable.md | 2 +- ...ns-data-public.indexpatternfield.format.md | 2 +- ...a-public.indexpatternfield.indexpattern.md | 2 +- ...gins-data-public.indexpatternfield.lang.md | 4 +- ...n-plugins-data-public.indexpatternfield.md | 28 ++- ...gins-data-public.indexpatternfield.name.md | 2 +- ...lic.indexpatternfield.readfromdocvalues.md | 2 +- ...ns-data-public.indexpatternfield.script.md | 4 +- ...-data-public.indexpatternfield.scripted.md | 2 +- ...ata-public.indexpatternfield.searchable.md | 2 +- ...-data-public.indexpatternfield.sortable.md | 2 +- ...ins-data-public.indexpatternfield.spec.md} | 6 +- ...s-data-public.indexpatternfield.subtype.md | 2 +- ...ns-data-public.indexpatternfield.tojson.md | 41 ++++ ...ns-data-public.indexpatternfield.tospec.md | 36 +++- ...gins-data-public.indexpatternfield.type.md | 2 +- ...a-public.indexpatternfield.visualizable.md | 2 +- .../kibana-plugin-plugins-data-public.md | 2 +- ....snap => index_pattern_field.test.ts.snap} | 0 .../common/index_patterns/fields/field.ts | 176 ---------------- .../index_patterns/fields/field_list.ts | 159 ++++++++------- .../common/index_patterns/fields/index.ts | 2 +- ...ld.test.ts => index_pattern_field.test.ts} | 105 ++-------- .../fields/index_pattern_field.ts | 188 ++++++++++++++++++ .../index_patterns/fields/obj_define.js | 158 --------------- .../index_patterns/fields/obj_define.test.js | 149 -------------- .../index_patterns/index_pattern.test.ts | 22 +- .../index_patterns/index_pattern.ts | 119 ++++++----- .../index_patterns/index_patterns.ts | 28 +-- .../data/common/index_patterns/types.ts | 15 +- .../kbn_field_types/kbn_field_types.test.ts | 4 +- .../common/kbn_field_types/kbn_field_types.ts | 6 +- .../kbn_field_types_factory.ts | 8 +- src/plugins/data/public/index.ts | 5 +- .../data/public/index_patterns/index.ts | 6 +- src/plugins/data/public/public.api.md | 169 +++++++++++----- .../public/search/aggs/agg_configs.test.ts | 2 +- .../public/search/aggs/param_types/field.ts | 2 +- .../doc_table/components/row_headers.test.js | 43 ++-- .../sidebar/discover_field.test.tsx | 30 +-- .../components/sidebar/discover_sidebar.tsx | 11 +- .../lib/get_index_pattern_field_list.ts | 12 +- .../components/sidebar/lib/group_fields.tsx | 4 +- .../create_edit_field/create_edit_field.tsx | 31 ++- .../indexed_fields_table.test.tsx.snap | 20 +- .../indexed_fields_table.test.tsx | 21 +- .../indexed_fields_table.tsx | 2 +- .../__snapshots__/field_editor.test.tsx.snap | 5 + .../editors/string/string.tsx | 2 +- .../field_editor/field_editor.test.tsx | 23 ++- .../components/field_editor/field_editor.tsx | 166 ++++++++-------- .../public/components/test_utils.tsx | 4 +- src/test_utils/public/stub_index_pattern.js | 22 +- .../management/_index_pattern_popularity.js | 2 +- .../apps/management/_scripted_fields.js | 3 +- .../management/_scripted_fields_filter.js | 4 +- 85 files changed, 1086 insertions(+), 1076 deletions(-) create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist._constructor_.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.add.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.getbyname.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.getbytype.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.remove.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.removeall.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.replaceall.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.tospec.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.update.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getindexpatternfieldlistcreator.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.removeall.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.replaceall.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getformatterforfield.md rename docs/development/plugins/data/public/{kibana-plugin-plugins-data-public.indexpatternfield.__spec.md => kibana-plugin-plugins-data-public.indexpatternfield.spec.md} (56%) create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.tojson.md rename src/plugins/data/common/index_patterns/fields/__snapshots__/{field.test.ts.snap => index_pattern_field.test.ts.snap} (100%) delete mode 100644 src/plugins/data/common/index_patterns/fields/field.ts rename src/plugins/data/common/index_patterns/fields/{field.test.ts => index_pattern_field.test.ts} (64%) create mode 100644 src/plugins/data/common/index_patterns/fields/index_pattern_field.ts delete mode 100644 src/plugins/data/common/index_patterns/fields/obj_define.js delete mode 100644 src/plugins/data/common/index_patterns/fields/obj_define.test.js diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist._constructor_.md new file mode 100644 index 0000000000000..3b60ac0f48edd --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist._constructor_.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldList](./kibana-plugin-plugins-data-public.fieldlist.md) > [(constructor)](./kibana-plugin-plugins-data-public.fieldlist._constructor_.md) + +## FieldList.(constructor) + +Constructs a new instance of the `FieldList` class + +Signature: + +```typescript +constructor(indexPattern: IndexPattern, specs?: FieldSpec[], shortDotsEnable?: boolean, onNotification?: () => void); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| indexPattern | IndexPattern | | +| specs | FieldSpec[] | | +| shortDotsEnable | boolean | | +| onNotification | () => void | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.add.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.add.md new file mode 100644 index 0000000000000..ae3d82f0cc3ea --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.add.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldList](./kibana-plugin-plugins-data-public.fieldlist.md) > [add](./kibana-plugin-plugins-data-public.fieldlist.add.md) + +## FieldList.add property + +Signature: + +```typescript +readonly add: (field: FieldSpec) => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.getbyname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.getbyname.md new file mode 100644 index 0000000000000..af368d003423a --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.getbyname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldList](./kibana-plugin-plugins-data-public.fieldlist.md) > [getByName](./kibana-plugin-plugins-data-public.fieldlist.getbyname.md) + +## FieldList.getByName property + +Signature: + +```typescript +readonly getByName: (name: IndexPatternField['name']) => IndexPatternField | undefined; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.getbytype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.getbytype.md new file mode 100644 index 0000000000000..16bae3ee7c555 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.getbytype.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldList](./kibana-plugin-plugins-data-public.fieldlist.md) > [getByType](./kibana-plugin-plugins-data-public.fieldlist.getbytype.md) + +## FieldList.getByType property + +Signature: + +```typescript +readonly getByType: (type: IndexPatternField['type']) => any[]; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.md new file mode 100644 index 0000000000000..ef740575dff4e --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.md @@ -0,0 +1,31 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldList](./kibana-plugin-plugins-data-public.fieldlist.md) + +## FieldList class + +Signature: + +```typescript +export declare class FieldList extends Array implements IIndexPatternFieldList +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(indexPattern, specs, shortDotsEnable, onNotification)](./kibana-plugin-plugins-data-public.fieldlist._constructor_.md) | | Constructs a new instance of the FieldList class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [add](./kibana-plugin-plugins-data-public.fieldlist.add.md) | | (field: FieldSpec) => void | | +| [getByName](./kibana-plugin-plugins-data-public.fieldlist.getbyname.md) | | (name: IndexPatternField['name']) => IndexPatternField | undefined | | +| [getByType](./kibana-plugin-plugins-data-public.fieldlist.getbytype.md) | | (type: IndexPatternField['type']) => any[] | | +| [remove](./kibana-plugin-plugins-data-public.fieldlist.remove.md) | | (field: IFieldType) => void | | +| [removeAll](./kibana-plugin-plugins-data-public.fieldlist.removeall.md) | | () => void | | +| [replaceAll](./kibana-plugin-plugins-data-public.fieldlist.replaceall.md) | | (specs: FieldSpec[]) => void | | +| [toSpec](./kibana-plugin-plugins-data-public.fieldlist.tospec.md) | | () => {
count: number;
script: string | undefined;
lang: string | undefined;
conflictDescriptions: Record<string, string[]> | undefined;
name: string;
type: string;
esTypes: string[] | undefined;
scripted: boolean;
searchable: boolean;
aggregatable: boolean;
readFromDocValues: boolean;
subType: import("../types").IFieldSubType | undefined;
format: any;
}[] | | +| [update](./kibana-plugin-plugins-data-public.fieldlist.update.md) | | (field: FieldSpec) => void | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.remove.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.remove.md new file mode 100644 index 0000000000000..149410adb3550 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.remove.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldList](./kibana-plugin-plugins-data-public.fieldlist.md) > [remove](./kibana-plugin-plugins-data-public.fieldlist.remove.md) + +## FieldList.remove property + +Signature: + +```typescript +readonly remove: (field: IFieldType) => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.removeall.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.removeall.md new file mode 100644 index 0000000000000..92a45349ad005 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.removeall.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldList](./kibana-plugin-plugins-data-public.fieldlist.md) > [removeAll](./kibana-plugin-plugins-data-public.fieldlist.removeall.md) + +## FieldList.removeAll property + +Signature: + +```typescript +readonly removeAll: () => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.replaceall.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.replaceall.md new file mode 100644 index 0000000000000..5330440e6b96a --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.replaceall.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldList](./kibana-plugin-plugins-data-public.fieldlist.md) > [replaceAll](./kibana-plugin-plugins-data-public.fieldlist.replaceall.md) + +## FieldList.replaceAll property + +Signature: + +```typescript +readonly replaceAll: (specs: FieldSpec[]) => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.tospec.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.tospec.md new file mode 100644 index 0000000000000..e646339feb495 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.tospec.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldList](./kibana-plugin-plugins-data-public.fieldlist.md) > [toSpec](./kibana-plugin-plugins-data-public.fieldlist.tospec.md) + +## FieldList.toSpec property + +Signature: + +```typescript +readonly toSpec: () => { + count: number; + script: string | undefined; + lang: string | undefined; + conflictDescriptions: Record | undefined; + name: string; + type: string; + esTypes: string[] | undefined; + scripted: boolean; + searchable: boolean; + aggregatable: boolean; + readFromDocValues: boolean; + subType: import("../types").IFieldSubType | undefined; + format: any; + }[]; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.update.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.update.md new file mode 100644 index 0000000000000..c718e47b31b50 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldlist.update.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldList](./kibana-plugin-plugins-data-public.fieldlist.md) > [update](./kibana-plugin-plugins-data-public.fieldlist.update.md) + +## FieldList.update property + +Signature: + +```typescript +readonly update: (field: FieldSpec) => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getindexpatternfieldlistcreator.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getindexpatternfieldlistcreator.md deleted file mode 100644 index 880acdc8956d4..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getindexpatternfieldlistcreator.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [getIndexPatternFieldListCreator](./kibana-plugin-plugins-data-public.getindexpatternfieldlistcreator.md) - -## getIndexPatternFieldListCreator variable - -Signature: - -```typescript -getIndexPatternFieldListCreator: ({ fieldFormats, onNotification, }: FieldListDependencies) => CreateIndexPatternFieldList -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbyname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbyname.md index 14b5aa7137dc2..e277df87fe908 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbyname.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbyname.md @@ -7,16 +7,16 @@ Signature: ```typescript -getByName(name: Field['name']): Field | undefined; +getByName(name: IndexPatternField['name']): IndexPatternField | undefined; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| name | Field['name'] | | +| name | IndexPatternField['name'] | | Returns: -`Field | undefined` +`IndexPatternField | undefined` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbytype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbytype.md index 3c65b78e5291d..9a7b3ab36b0c1 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbytype.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbytype.md @@ -7,16 +7,16 @@ Signature: ```typescript -getByType(type: Field['type']): Field[]; +getByType(type: IndexPatternField['type']): IndexPatternField[]; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| type | Field['type'] | | +| type | IndexPatternField['type'] | | Returns: -`Field[]` +`IndexPatternField[]` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.md index 47d7c7491aa86..4ab012a2601d2 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.md @@ -7,7 +7,7 @@ Signature: ```typescript -export interface IIndexPatternFieldList extends Array +export interface IIndexPatternFieldList extends Array ``` ## Methods @@ -18,5 +18,7 @@ export interface IIndexPatternFieldList extends Array | [getByName(name)](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbyname.md) | | | [getByType(type)](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.getbytype.md) | | | [remove(field)](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.remove.md) | | +| [removeAll()](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.removeall.md) | | +| [replaceAll(specs)](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.replaceall.md) | | | [update(field)](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.update.md) | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.removeall.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.removeall.md new file mode 100644 index 0000000000000..55e7ca98e2637 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.removeall.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IIndexPatternFieldList](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.md) > [removeAll](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.removeall.md) + +## IIndexPatternFieldList.removeAll() method + +Signature: + +```typescript +removeAll(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.replaceall.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.replaceall.md new file mode 100644 index 0000000000000..c7e8cdd578bfe --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.replaceall.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IIndexPatternFieldList](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.md) > [replaceAll](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.replaceall.md) + +## IIndexPatternFieldList.replaceAll() method + +Signature: + +```typescript +replaceAll(specs: FieldSpec[]): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| specs | FieldSpec[] | | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getfieldbyname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getfieldbyname.md index e6a23c5c70aab..75cdfd0a2e22e 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getfieldbyname.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getfieldbyname.md @@ -7,7 +7,7 @@ Signature: ```typescript -getFieldByName(name: string): Field | void; +getFieldByName(name: string): IndexPatternField | undefined; ``` ## Parameters @@ -18,5 +18,5 @@ getFieldByName(name: string): Field | void; Returns: -`Field | void` +`IndexPatternField | undefined` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getformatterforfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getformatterforfield.md new file mode 100644 index 0000000000000..7984f7aff1d2d --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getformatterforfield.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [getFormatterForField](./kibana-plugin-plugins-data-public.indexpattern.getformatterforfield.md) + +## IndexPattern.getFormatterForField() method + +Signature: + +```typescript +getFormatterForField(field: IndexPatternField | IndexPatternField['spec']): FieldFormat; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| field | IndexPatternField | IndexPatternField['spec'] | | + +Returns: + +`FieldFormat` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getnonscriptedfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getnonscriptedfields.md index 4e49304484815..77ce6f6f23a67 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getnonscriptedfields.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getnonscriptedfields.md @@ -7,9 +7,9 @@ Signature: ```typescript -getNonScriptedFields(): Field[]; +getNonScriptedFields(): IndexPatternField[]; ``` Returns: -`Field[]` +`IndexPatternField[]` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getscriptedfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getscriptedfields.md index 9ab4f9a9aaed5..055f07367c96e 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getscriptedfields.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getscriptedfields.md @@ -7,9 +7,9 @@ Signature: ```typescript -getScriptedFields(): Field[]; +getScriptedFields(): IndexPatternField[]; ``` Returns: -`Field[]` +`IndexPatternField[]` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.gettimefield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.gettimefield.md index 8e68e8c35aff7..24de0be3794bb 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.gettimefield.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.gettimefield.md @@ -7,9 +7,9 @@ Signature: ```typescript -getTimeField(): Field | undefined; +getTimeField(): IndexPatternField | undefined; ``` Returns: -`Field | undefined` +`IndexPatternField | undefined` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md index a37f115358922..d340aaeeef25e 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md @@ -42,6 +42,7 @@ export declare class IndexPattern implements IIndexPattern | [getAggregationRestrictions()](./kibana-plugin-plugins-data-public.indexpattern.getaggregationrestrictions.md) | | | | [getComputedFields()](./kibana-plugin-plugins-data-public.indexpattern.getcomputedfields.md) | | | | [getFieldByName(name)](./kibana-plugin-plugins-data-public.indexpattern.getfieldbyname.md) | | | +| [getFormatterForField(field)](./kibana-plugin-plugins-data-public.indexpattern.getformatterforfield.md) | | | | [getNonScriptedFields()](./kibana-plugin-plugins-data-public.indexpattern.getnonscriptedfields.md) | | | | [getScriptedFields()](./kibana-plugin-plugins-data-public.indexpattern.getscriptedfields.md) | | | | [getSourceFiltering()](./kibana-plugin-plugins-data-public.indexpattern.getsourcefiltering.md) | | | @@ -55,7 +56,7 @@ export declare class IndexPattern implements IIndexPattern | [popularizeField(fieldName, unit)](./kibana-plugin-plugins-data-public.indexpattern.popularizefield.md) | | | | [prepBody()](./kibana-plugin-plugins-data-public.indexpattern.prepbody.md) | | | | [refreshFields()](./kibana-plugin-plugins-data-public.indexpattern.refreshfields.md) | | | -| [removeScriptedField(field)](./kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md) | | | +| [removeScriptedField(fieldName)](./kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md) | | | | [save(saveAttempts)](./kibana-plugin-plugins-data-public.indexpattern.save.md) | | | | [toJSON()](./kibana-plugin-plugins-data-public.indexpattern.tojson.md) | | | | [toSpec()](./kibana-plugin-plugins-data-public.indexpattern.tospec.md) | | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md index 2a6811f501152..42c6dd72b8c4e 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md @@ -7,14 +7,14 @@ Signature: ```typescript -removeScriptedField(field: IFieldType): Promise; +removeScriptedField(fieldName: string): Promise; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| field | IFieldType | | +| fieldName | string | | Returns: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md index 7a195702b6f13..10b65bdccdf87 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md @@ -4,20 +4,20 @@ ## IndexPatternField.(constructor) -Constructs a new instance of the `Field` class +Constructs a new instance of the `IndexPatternField` class Signature: ```typescript -constructor(indexPattern: IIndexPattern, spec: FieldSpecExportFmt | FieldSpec | Field, shortDotsEnable: boolean, { fieldFormats, onNotification }: FieldDependencies); +constructor(indexPattern: IndexPattern, spec: FieldSpec, displayName: string, onNotification: OnNotification); ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| indexPattern | IIndexPattern | | -| spec | FieldSpecExportFmt | FieldSpec | Field | | -| shortDotsEnable | boolean | | -| { fieldFormats, onNotification } | FieldDependencies | | +| indexPattern | IndexPattern | | +| spec | FieldSpec | | +| displayName | string | | +| onNotification | OnNotification | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.aggregatable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.aggregatable.md index 267c8f786b5dd..6ef87d08600a3 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.aggregatable.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.aggregatable.md @@ -7,5 +7,5 @@ Signature: ```typescript -aggregatable?: boolean; +get aggregatable(): boolean; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md index ec19a4854bf0e..6d62053726197 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md @@ -7,5 +7,7 @@ Signature: ```typescript -conflictDescriptions?: FieldSpecConflictDescriptions; +get conflictDescriptions(): Record | undefined; + +set conflictDescriptions(conflictDescriptions: Record | undefined); ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.count.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.count.md index 8e848276f21c4..84c0a75fd206d 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.count.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.count.md @@ -7,5 +7,7 @@ Signature: ```typescript -count?: number; +get count(): number; + +set count(count: number); ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.displayname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.displayname.md index ed9630f92fc97..c0ce2fff419bf 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.displayname.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.displayname.md @@ -7,5 +7,5 @@ Signature: ```typescript -displayName?: string; +readonly displayName: string; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.estypes.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.estypes.md index dec74df099d43..ac088cb69a3d6 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.estypes.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.estypes.md @@ -7,5 +7,5 @@ Signature: ```typescript -esTypes?: string[]; +get esTypes(): string[] | undefined; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.filterable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.filterable.md index 4290c4a2f86b3..1149047c0eccd 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.filterable.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.filterable.md @@ -7,5 +7,5 @@ Signature: ```typescript -filterable?: boolean; +get filterable(): boolean; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.format.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.format.md index d5df8ed628cb0..f28d5b1bca7e5 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.format.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.format.md @@ -7,5 +7,5 @@ Signature: ```typescript -format: any; +get format(): FieldFormat; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md index 4acaaa8c0dc2c..3d145cce9d07d 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md @@ -7,5 +7,5 @@ Signature: ```typescript -indexPattern?: IIndexPattern; +readonly indexPattern: IndexPattern; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.lang.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.lang.md index f731be8f613cf..0a8446d40e5ec 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.lang.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.lang.md @@ -7,5 +7,7 @@ Signature: ```typescript -lang?: string; +get lang(): string | undefined; + +set lang(lang: string | undefined); ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md index d82999e7a96af..713b29ea3a3d3 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md @@ -7,37 +7,43 @@ Signature: ```typescript -export declare class Field implements IFieldType +export declare class IndexPatternField implements IFieldType ``` ## Constructors | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(indexPattern, spec, shortDotsEnable, { fieldFormats, onNotification })](./kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md) | | Constructs a new instance of the Field class | +| [(constructor)(indexPattern, spec, displayName, onNotification)](./kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md) | | Constructs a new instance of the IndexPatternField class | ## Properties | Property | Modifiers | Type | Description | | --- | --- | --- | --- | -| [$$spec](./kibana-plugin-plugins-data-public.indexpatternfield.__spec.md) | | FieldSpec | | | [aggregatable](./kibana-plugin-plugins-data-public.indexpatternfield.aggregatable.md) | | boolean | | -| [conflictDescriptions](./kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md) | | FieldSpecConflictDescriptions | | +| [conflictDescriptions](./kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md) | | Record<string, string[]> | undefined | | | [count](./kibana-plugin-plugins-data-public.indexpatternfield.count.md) | | number | | | [displayName](./kibana-plugin-plugins-data-public.indexpatternfield.displayname.md) | | string | | -| [esTypes](./kibana-plugin-plugins-data-public.indexpatternfield.estypes.md) | | string[] | | +| [esTypes](./kibana-plugin-plugins-data-public.indexpatternfield.estypes.md) | | string[] | undefined | | | [filterable](./kibana-plugin-plugins-data-public.indexpatternfield.filterable.md) | | boolean | | -| [format](./kibana-plugin-plugins-data-public.indexpatternfield.format.md) | | any | | -| [indexPattern](./kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md) | | IIndexPattern | | -| [lang](./kibana-plugin-plugins-data-public.indexpatternfield.lang.md) | | string | | +| [format](./kibana-plugin-plugins-data-public.indexpatternfield.format.md) | | FieldFormat | | +| [indexPattern](./kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md) | | IndexPattern | | +| [lang](./kibana-plugin-plugins-data-public.indexpatternfield.lang.md) | | string | undefined | | | [name](./kibana-plugin-plugins-data-public.indexpatternfield.name.md) | | string | | | [readFromDocValues](./kibana-plugin-plugins-data-public.indexpatternfield.readfromdocvalues.md) | | boolean | | -| [script](./kibana-plugin-plugins-data-public.indexpatternfield.script.md) | | string | | +| [script](./kibana-plugin-plugins-data-public.indexpatternfield.script.md) | | string | undefined | | | [scripted](./kibana-plugin-plugins-data-public.indexpatternfield.scripted.md) | | boolean | | | [searchable](./kibana-plugin-plugins-data-public.indexpatternfield.searchable.md) | | boolean | | | [sortable](./kibana-plugin-plugins-data-public.indexpatternfield.sortable.md) | | boolean | | -| [subType](./kibana-plugin-plugins-data-public.indexpatternfield.subtype.md) | | IFieldSubType | | -| [toSpec](./kibana-plugin-plugins-data-public.indexpatternfield.tospec.md) | | () => FieldSpecExportFmt | | +| [spec](./kibana-plugin-plugins-data-public.indexpatternfield.spec.md) | | FieldSpec | | +| [subType](./kibana-plugin-plugins-data-public.indexpatternfield.subtype.md) | | import("../types").IFieldSubType | undefined | | | [type](./kibana-plugin-plugins-data-public.indexpatternfield.type.md) | | string | | | [visualizable](./kibana-plugin-plugins-data-public.indexpatternfield.visualizable.md) | | boolean | | +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [toJSON()](./kibana-plugin-plugins-data-public.indexpatternfield.tojson.md) | | | +| [toSpec()](./kibana-plugin-plugins-data-public.indexpatternfield.tospec.md) | | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.name.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.name.md index cb24621e73209..c690edeafea6e 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.name.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.name.md @@ -7,5 +7,5 @@ Signature: ```typescript -name: string; +get name(): string; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.readfromdocvalues.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.readfromdocvalues.md index 4b012c26a8620..22f727e3c00e8 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.readfromdocvalues.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.readfromdocvalues.md @@ -7,5 +7,5 @@ Signature: ```typescript -readFromDocValues?: boolean; +get readFromDocValues(): boolean; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.script.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.script.md index 132ba25a47637..27f9c797c92f2 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.script.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.script.md @@ -7,5 +7,7 @@ Signature: ```typescript -script?: string; +get script(): string | undefined; + +set script(script: string | undefined); ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.scripted.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.scripted.md index 1dd6bc865a75d..f3810b9698a11 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.scripted.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.scripted.md @@ -7,5 +7,5 @@ Signature: ```typescript -scripted?: boolean; +get scripted(): boolean; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.searchable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.searchable.md index 42f984d851435..431907b154dc0 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.searchable.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.searchable.md @@ -7,5 +7,5 @@ Signature: ```typescript -searchable?: boolean; +get searchable(): boolean; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.sortable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.sortable.md index 72d225185140b..871320c9586d3 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.sortable.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.sortable.md @@ -7,5 +7,5 @@ Signature: ```typescript -sortable?: boolean; +get sortable(): boolean; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.__spec.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.spec.md similarity index 56% rename from docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.__spec.md rename to docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.spec.md index f52a3324af36f..9884faaa6c7bb 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.__spec.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.spec.md @@ -1,11 +1,11 @@ -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [$$spec](./kibana-plugin-plugins-data-public.indexpatternfield.__spec.md) +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [spec](./kibana-plugin-plugins-data-public.indexpatternfield.spec.md) -## IndexPatternField.$$spec property +## IndexPatternField.spec property Signature: ```typescript -$$spec: FieldSpec; +readonly spec: FieldSpec; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.subtype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.subtype.md index 2d807f8a5739c..5c3c4d54ad099 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.subtype.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.subtype.md @@ -7,5 +7,5 @@ Signature: ```typescript -subType?: IFieldSubType; +get subType(): import("../types").IFieldSubType | undefined; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.tojson.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.tojson.md new file mode 100644 index 0000000000000..a6a3a5a093c8e --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.tojson.md @@ -0,0 +1,41 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [toJSON](./kibana-plugin-plugins-data-public.indexpatternfield.tojson.md) + +## IndexPatternField.toJSON() method + +Signature: + +```typescript +toJSON(): { + count: number; + script: string | undefined; + lang: string | undefined; + conflictDescriptions: Record | undefined; + name: string; + type: string; + esTypes: string[] | undefined; + scripted: boolean; + searchable: boolean; + aggregatable: boolean; + readFromDocValues: boolean; + subType: import("../types").IFieldSubType | undefined; + }; +``` +Returns: + +`{ + count: number; + script: string | undefined; + lang: string | undefined; + conflictDescriptions: Record | undefined; + name: string; + type: string; + esTypes: string[] | undefined; + scripted: boolean; + searchable: boolean; + aggregatable: boolean; + readFromDocValues: boolean; + subType: import("../types").IFieldSubType | undefined; + }` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.tospec.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.tospec.md index 35714faa03bc9..5037cb0049e82 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.tospec.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.tospec.md @@ -2,10 +2,42 @@ [Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [toSpec](./kibana-plugin-plugins-data-public.indexpatternfield.tospec.md) -## IndexPatternField.toSpec property +## IndexPatternField.toSpec() method Signature: ```typescript -toSpec: () => FieldSpecExportFmt; +toSpec(): { + count: number; + script: string | undefined; + lang: string | undefined; + conflictDescriptions: Record | undefined; + name: string; + type: string; + esTypes: string[] | undefined; + scripted: boolean; + searchable: boolean; + aggregatable: boolean; + readFromDocValues: boolean; + subType: import("../types").IFieldSubType | undefined; + format: any; + }; ``` +Returns: + +`{ + count: number; + script: string | undefined; + lang: string | undefined; + conflictDescriptions: Record | undefined; + name: string; + type: string; + esTypes: string[] | undefined; + scripted: boolean; + searchable: boolean; + aggregatable: boolean; + readFromDocValues: boolean; + subType: import("../types").IFieldSubType | undefined; + format: any; + }` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.type.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.type.md index c8483c9b83c9a..45085b9e74bcc 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.type.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.type.md @@ -7,5 +7,5 @@ Signature: ```typescript -type: string; +get type(): string; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.visualizable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.visualizable.md index dd661ae779c11..9ed689752503a 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.visualizable.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.visualizable.md @@ -7,5 +7,5 @@ Signature: ```typescript -visualizable?: boolean; +get visualizable(): boolean; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index db41936f35cca..c8d45804a3729 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -10,6 +10,7 @@ | --- | --- | | [AggParamType](./kibana-plugin-plugins-data-public.aggparamtype.md) | | | [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) | | +| [FieldList](./kibana-plugin-plugins-data-public.fieldlist.md) | | | [FilterManager](./kibana-plugin-plugins-data-public.filtermanager.md) | | | [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) | | | [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) | | @@ -103,7 +104,6 @@ | [extractSearchSourceReferences](./kibana-plugin-plugins-data-public.extractsearchsourcereferences.md) | | | [fieldFormats](./kibana-plugin-plugins-data-public.fieldformats.md) | | | [FilterBar](./kibana-plugin-plugins-data-public.filterbar.md) | | -| [getIndexPatternFieldListCreator](./kibana-plugin-plugins-data-public.getindexpatternfieldlistcreator.md) | | | [getKbnTypeNames](./kibana-plugin-plugins-data-public.getkbntypenames.md) | Get the esTypes known by all kbnFieldTypes {Array} | | [indexPatterns](./kibana-plugin-plugins-data-public.indexpatterns.md) | | | [injectSearchSourceReferences](./kibana-plugin-plugins-data-public.injectsearchsourcereferences.md) | | diff --git a/src/plugins/data/common/index_patterns/fields/__snapshots__/field.test.ts.snap b/src/plugins/data/common/index_patterns/fields/__snapshots__/index_pattern_field.test.ts.snap similarity index 100% rename from src/plugins/data/common/index_patterns/fields/__snapshots__/field.test.ts.snap rename to src/plugins/data/common/index_patterns/fields/__snapshots__/index_pattern_field.test.ts.snap diff --git a/src/plugins/data/common/index_patterns/fields/field.ts b/src/plugins/data/common/index_patterns/fields/field.ts deleted file mode 100644 index 81c7aff8a0faa..0000000000000 --- a/src/plugins/data/common/index_patterns/fields/field.ts +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@kbn/i18n'; -// @ts-ignore -import { ObjDefine } from './obj_define'; -import { IIndexPattern } from '../../types'; -import { - IFieldType, - getKbnFieldType, - IFieldSubType, - FieldFormat, - shortenDottedString, -} from '../../../common'; -import { - OnNotification, - FieldSpec, - FieldSpecConflictDescriptions, - FieldSpecExportFmt, -} from '../types'; -import { FieldFormatsStartCommon } from '../../field_formats'; - -interface FieldDependencies { - fieldFormats: FieldFormatsStartCommon; - onNotification: OnNotification; -} - -export class Field implements IFieldType { - name: string; - type: string; - script?: string; - lang?: string; - count?: number; - // esTypes might be undefined on old index patterns that have not been refreshed since we added - // this prop. It is also undefined on scripted fields. - esTypes?: string[]; - aggregatable?: boolean; - filterable?: boolean; - searchable?: boolean; - sortable?: boolean; - visualizable?: boolean; - scripted?: boolean; - subType?: IFieldSubType; - displayName?: string; - indexPattern?: IIndexPattern; - readFromDocValues?: boolean; - format: any; - $$spec: FieldSpec; - conflictDescriptions?: FieldSpecConflictDescriptions; - - constructor( - indexPattern: IIndexPattern, - spec: FieldSpecExportFmt | FieldSpec | Field, - shortDotsEnable: boolean, - { fieldFormats, onNotification }: FieldDependencies - ) { - // unwrap old instances of Field - if (spec instanceof Field) spec = spec.$$spec; - - // construct this object using ObjDefine class, which - // extends the Field.prototype but gets it's properties - // defined using the logic below - const obj = new ObjDefine(spec, Field.prototype); - - if (spec.name === '_source') { - spec.type = '_source'; - } - - // find the type for this field, fallback to unknown type - let type = getKbnFieldType(spec.type); - if (spec.type && !type) { - const title = i18n.translate('data.indexPatterns.unknownFieldHeader', { - values: { type: spec.type }, - defaultMessage: 'Unknown field type {type}', - }); - const text = i18n.translate('data.indexPatterns.unknownFieldErrorMessage', { - values: { name: spec.name, title: indexPattern.title }, - defaultMessage: 'Field {name} in indexPattern {title} is using an unknown field type.', - }); - onNotification({ title, text, color: 'danger', iconType: 'alert' }); - } - - if (!type) type = getKbnFieldType('unknown'); - - let format: any = spec.format; - - if (!FieldFormat.isInstanceOfFieldFormat(format)) { - format = - (indexPattern.fieldFormatMap && indexPattern.fieldFormatMap[spec.name]) || - fieldFormats.getDefaultInstance(spec.type, spec.esTypes); - } - - const indexed = !!spec.indexed; - const scripted = !!spec.scripted; - const searchable = !!spec.searchable || scripted; - const aggregatable = !!spec.aggregatable || scripted; - const readFromDocValues = !!spec.readFromDocValues && !scripted; - const sortable = spec.name === '_score' || ((indexed || aggregatable) && type && type.sortable); - const filterable = - spec.name === '_id' || scripted || ((indexed || searchable) && type && type.filterable); - const visualizable = aggregatable; - - this.name = ''; - obj.fact('name'); - this.type = ''; - obj.fact('type'); - obj.fact('esTypes'); - obj.writ('count', spec.count || 0); - - // scripted objs - obj.fact('scripted', scripted); - obj.writ('script', scripted ? spec.script : null); - obj.writ('lang', scripted ? spec.lang || 'painless' : null); - - // stats - obj.fact('searchable', searchable); - obj.fact('aggregatable', aggregatable); - obj.fact('readFromDocValues', readFromDocValues); - - // usage flags, read-only and won't be saved - obj.comp('format', format); - obj.comp('sortable', sortable); - obj.comp('filterable', filterable); - obj.comp('visualizable', visualizable); - - // computed values - obj.comp('indexPattern', indexPattern); - obj.comp('displayName', shortDotsEnable ? shortenDottedString(spec.name) : spec.name); - this.$$spec = spec; - obj.comp('$$spec', spec); - - // conflict info - obj.writ('conflictDescriptions'); - - // multi info - obj.fact('subType'); - - const newObj = obj.create(); - newObj.toSpec = function () { - return { - count: this.count, - script: this.script, - lang: this.lang, - conflictDescriptions: this.conflictDescriptions, - name: this.name, - type: this.type, - esTypes: this.esTypes, - scripted: this.scripted, - searchable: this.searchable, - aggregatable: this.aggregatable, - readFromDocValues: this.readFromDocValues, - subType: this.subType, - format: this.indexPattern?.fieldFormatMap[this.name]?.toJSON() || undefined, - }; - }; - return newObj; - } - // only providing type info as constructor returns new object instead of `this` - toSpec = () => (({} as unknown) as FieldSpecExportFmt); -} diff --git a/src/plugins/data/common/index_patterns/fields/field_list.ts b/src/plugins/data/common/index_patterns/fields/field_list.ts index c1ca5341328ce..207002f42bbce 100644 --- a/src/plugins/data/common/index_patterns/fields/field_list.ts +++ b/src/plugins/data/common/index_patterns/fields/field_list.ts @@ -18,95 +18,110 @@ */ import { findIndex } from 'lodash'; -import { IIndexPattern } from '../../types'; -import { IFieldType } from '../../../common'; -import { Field } from './field'; +import { IFieldType, shortenDottedString } from '../../../common'; +import { IndexPatternField } from './index_pattern_field'; import { OnNotification, FieldSpec } from '../types'; -import { FieldFormatsStartCommon } from '../../field_formats'; +import { IndexPattern } from '../index_patterns'; -type FieldMap = Map; +type FieldMap = Map; -interface FieldListDependencies { - fieldFormats: FieldFormatsStartCommon; - onNotification: OnNotification; -} - -export interface IIndexPatternFieldList extends Array { - getByName(name: Field['name']): Field | undefined; - getByType(type: Field['type']): Field[]; +export interface IIndexPatternFieldList extends Array { add(field: FieldSpec): void; + getByName(name: IndexPatternField['name']): IndexPatternField | undefined; + getByType(type: IndexPatternField['type']): IndexPatternField[]; remove(field: IFieldType): void; + removeAll(): void; + replaceAll(specs: FieldSpec[]): void; update(field: FieldSpec): void; } export type CreateIndexPatternFieldList = ( - indexPattern: IIndexPattern, + indexPattern: IndexPattern, specs?: FieldSpec[], - shortDotsEnable?: boolean + shortDotsEnable?: boolean, + onNotification?: OnNotification ) => IIndexPatternFieldList; -export const getIndexPatternFieldListCreator = ({ - fieldFormats, - onNotification, -}: FieldListDependencies): CreateIndexPatternFieldList => (...fieldListParams) => { - class FieldList extends Array implements IIndexPatternFieldList { - private byName: FieldMap = new Map(); - private groups: Map = new Map(); - private indexPattern: IIndexPattern; - private shortDotsEnable: boolean; - private setByName = (field: Field) => this.byName.set(field.name, field); - private setByGroup = (field: Field) => { - if (typeof this.groups.get(field.type) === 'undefined') { - this.groups.set(field.type, new Map()); - } - this.groups.get(field.type)!.set(field.name, field); - }; - private removeByGroup = (field: IFieldType) => this.groups.get(field.type)!.delete(field.name); +export class FieldList extends Array implements IIndexPatternFieldList { + private byName: FieldMap = new Map(); + private groups: Map = new Map(); + private indexPattern: IndexPattern; + private shortDotsEnable: boolean; + private onNotification: OnNotification; + private setByName = (field: IndexPatternField) => this.byName.set(field.name, field); + private setByGroup = (field: IndexPatternField) => { + if (typeof this.groups.get(field.type) === 'undefined') { + this.groups.set(field.type, new Map()); + } + this.groups.get(field.type)!.set(field.name, field); + }; + private removeByGroup = (field: IFieldType) => this.groups.get(field.type)!.delete(field.name); + private calcDisplayName = (name: string) => + this.shortDotsEnable ? shortenDottedString(name) : name; + constructor( + indexPattern: IndexPattern, + specs: FieldSpec[] = [], + shortDotsEnable = false, + onNotification = () => {} + ) { + super(); + this.indexPattern = indexPattern; + this.shortDotsEnable = shortDotsEnable; + this.onNotification = onNotification; - constructor(indexPattern: IIndexPattern, specs: FieldSpec[] = [], shortDotsEnable = false) { - super(); - this.indexPattern = indexPattern; - this.shortDotsEnable = shortDotsEnable; + specs.map((field) => this.add(field)); + } - specs.map((field) => this.add(field)); - } + public readonly getByName = (name: IndexPatternField['name']) => this.byName.get(name); + public readonly getByType = (type: IndexPatternField['type']) => [ + ...(this.groups.get(type) || new Map()).values(), + ]; + public readonly add = (field: FieldSpec) => { + const newField = new IndexPatternField( + this.indexPattern, + field, + this.calcDisplayName(field.name), + this.onNotification + ); + this.push(newField); + this.setByName(newField); + this.setByGroup(newField); + }; - getByName = (name: Field['name']) => this.byName.get(name); - getByType = (type: Field['type']) => [...(this.groups.get(type) || new Map()).values()]; - add = (field: FieldSpec) => { - const newField = new Field(this.indexPattern, field, this.shortDotsEnable, { - fieldFormats, - onNotification, - }); - this.push(newField); - this.setByName(newField); - this.setByGroup(newField); - }; + public readonly remove = (field: IFieldType) => { + this.removeByGroup(field); + this.byName.delete(field.name); - remove = (field: IFieldType) => { - this.removeByGroup(field); - this.byName.delete(field.name); + const fieldIndex = findIndex(this, { name: field.name }); + this.splice(fieldIndex, 1); + }; - const fieldIndex = findIndex(this, { name: field.name }); - this.splice(fieldIndex, 1); - }; + public readonly update = (field: FieldSpec) => { + const newField = new IndexPatternField( + this.indexPattern, + field, + this.calcDisplayName(field.name), + this.onNotification + ); + const index = this.findIndex((f) => f.name === newField.name); + this.splice(index, 1, newField); + this.setByName(newField); + this.removeByGroup(newField); + this.setByGroup(newField); + }; - update = (field: FieldSpec) => { - const newField = new Field(this.indexPattern, field, this.shortDotsEnable, { - fieldFormats, - onNotification, - }); - const index = this.findIndex((f) => f.name === newField.name); - this.splice(index, 1, newField); - this.setByName(newField); - this.removeByGroup(newField); - this.setByGroup(newField); - }; + public readonly removeAll = () => { + this.length = 0; + this.byName.clear(); + this.groups.clear(); + }; - toSpec = () => { - return [...this.map((field) => field.toSpec())]; - }; - } + public readonly replaceAll = (specs: FieldSpec[]) => { + this.removeAll(); + specs.forEach(this.add); + }; - return new FieldList(...fieldListParams); -}; + public readonly toSpec = () => { + return [...this.map((field) => field.toSpec())]; + }; +} diff --git a/src/plugins/data/common/index_patterns/fields/index.ts b/src/plugins/data/common/index_patterns/fields/index.ts index 1b7c87d556f59..0c3b43181c5b4 100644 --- a/src/plugins/data/common/index_patterns/fields/index.ts +++ b/src/plugins/data/common/index_patterns/fields/index.ts @@ -20,4 +20,4 @@ export * from './types'; export { isFilterable, isNestedField } from './utils'; export * from './field_list'; -export * from './field'; +export * from './index_pattern_field'; diff --git a/src/plugins/data/common/index_patterns/fields/field.test.ts b/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts similarity index 64% rename from src/plugins/data/common/index_patterns/fields/field.test.ts rename to src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts index 910f22088f43a..0cd0fe8324809 100644 --- a/src/plugins/data/common/index_patterns/fields/field.test.ts +++ b/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts @@ -17,10 +17,10 @@ * under the License. */ -import { Field } from './field'; +import { IndexPatternField } from './index_pattern_field'; import { IndexPattern } from '../index_patterns'; -import { FieldFormatsStartCommon } from '../..'; -import { KBN_FIELD_TYPES, FieldSpec, FieldSpecExportFmt } from '../../../common'; +import { KBN_FIELD_TYPES } from '../../../common'; +import { FieldSpec } from '../types'; describe('Field', function () { function flatten(obj: Record) { @@ -28,14 +28,11 @@ describe('Field', function () { } function getField(values = {}) { - return new Field( + return new IndexPatternField( fieldValues.indexPattern as IndexPattern, { ...fieldValues, ...values }, - false, - { - fieldFormats: {} as FieldFormatsStartCommon, - onNotification: () => {}, - } + 'displayName', + () => {} ); } @@ -50,6 +47,7 @@ describe('Field', function () { filterable: true, searchable: true, sortable: true, + indexed: true, readFromDocValues: false, visualizable: true, scripted: true, @@ -58,11 +56,9 @@ describe('Field', function () { indexPattern: ({ fieldFormatMap: { name: {}, _source: {}, _score: {}, _id: {} }, } as unknown) as IndexPattern, - format: { name: 'formatName' }, $$spec: ({} as unknown) as FieldSpec, conflictDescriptions: { a: ['b', 'c'], d: ['e'] }, - toSpec: () => (({} as unknown) as FieldSpecExportFmt), - } as Field; + }; it('the correct properties are writable', () => { const field = getField(); @@ -84,72 +80,6 @@ describe('Field', function () { expect(field.conflictDescriptions).toEqual({}); }); - it('the correct properties are not writable', () => { - const field = getField(); - - expect(field.name).toEqual(fieldValues.name); - field.name = 'newName'; - expect(field.name).toEqual(fieldValues.name); - - expect(field.type).toEqual(fieldValues.type); - field.type = 'newType'; - expect(field.type).toEqual(fieldValues.type); - - expect(field.esTypes).toEqual(fieldValues.esTypes); - field.esTypes = ['newType']; - expect(field.esTypes).toEqual(fieldValues.esTypes); - - expect(field.scripted).toEqual(fieldValues.scripted); - field.scripted = false; - expect(field.scripted).toEqual(fieldValues.scripted); - - expect(field.searchable).toEqual(fieldValues.searchable); - field.searchable = false; - expect(field.searchable).toEqual(fieldValues.searchable); - - expect(field.aggregatable).toEqual(fieldValues.aggregatable); - field.aggregatable = false; - expect(field.aggregatable).toEqual(fieldValues.aggregatable); - - expect(field.readFromDocValues).toEqual(fieldValues.readFromDocValues); - field.readFromDocValues = true; - expect(field.readFromDocValues).toEqual(fieldValues.readFromDocValues); - - expect(field.subType).toEqual(fieldValues.subType); - field.subType = {}; - expect(field.subType).toEqual(fieldValues.subType); - - // not writable, not serialized - expect(() => { - field.indexPattern = {} as IndexPattern; - }).toThrow(); - - // computed fields - expect(() => { - field.format = { name: 'newFormatName' }; - }).toThrow(); - - expect(() => { - field.sortable = false; - }).toThrow(); - - expect(() => { - field.filterable = false; - }).toThrow(); - - expect(() => { - field.visualizable = false; - }).toThrow(); - - expect(() => { - field.displayName = 'newDisplayName'; - }).toThrow(); - - expect(() => { - field.$$spec = ({ a: 'b' } as unknown) as FieldSpec; - }).toThrow(); - }); - it('sets type field when _source field', () => { const field = getField({ name: '_source' }); expect(field.type).toEqual('_source'); @@ -214,26 +144,25 @@ describe('Field', function () { }); it('exports the property to JSON', () => { - const field = new Field({ fieldFormatMap: { name: {} } } as IndexPattern, fieldValues, false, { - fieldFormats: {} as FieldFormatsStartCommon, - onNotification: () => {}, - }); + const field = new IndexPatternField( + { fieldFormatMap: { name: {} } } as IndexPattern, + fieldValues, + 'displayName', + () => {} + ); expect(flatten(field)).toMatchSnapshot(); }); it('spec snapshot', () => { - const field = new Field( + const field = new IndexPatternField( { fieldFormatMap: { name: { toJSON: () => ({ id: 'number', params: { pattern: '$0,0.[00]' } }) }, }, } as IndexPattern, fieldValues, - false, - { - fieldFormats: {} as FieldFormatsStartCommon, - onNotification: () => {}, - } + 'displayName', + () => {} ); expect(field.toSpec()).toMatchSnapshot(); }); diff --git a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts new file mode 100644 index 0000000000000..4e22332bef141 --- /dev/null +++ b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts @@ -0,0 +1,188 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { + IFieldType, + KbnFieldType, + getKbnFieldType, + KBN_FIELD_TYPES, + FieldFormat, +} from '../../../common'; +import { OnNotification, FieldSpec } from '../types'; + +import { IndexPattern } from '../index_patterns'; + +export class IndexPatternField implements IFieldType { + readonly spec: FieldSpec; + // not writable or serialized + readonly indexPattern: IndexPattern; + readonly displayName: string; + private readonly kbnFieldType: KbnFieldType; + + constructor( + indexPattern: IndexPattern, + spec: FieldSpec, + displayName: string, + onNotification: OnNotification + ) { + this.indexPattern = indexPattern; + this.spec = { ...spec, type: spec.name === '_source' ? '_source' : spec.type }; + this.displayName = displayName; + + this.kbnFieldType = getKbnFieldType(spec.type); + if (spec.type && this.kbnFieldType?.name === KBN_FIELD_TYPES.UNKNOWN) { + const title = i18n.translate('data.indexPatterns.unknownFieldHeader', { + values: { type: spec.type }, + defaultMessage: 'Unknown field type {type}', + }); + const text = i18n.translate('data.indexPatterns.unknownFieldErrorMessage', { + values: { name: spec.name, title: indexPattern.title }, + defaultMessage: 'Field {name} in indexPattern {title} is using an unknown field type.', + }); + onNotification({ title, text, color: 'danger', iconType: 'alert' }); + } + } + + // writable attrs + public get count() { + return this.spec.count; + } + + public set count(count) { + this.spec.count = count; + } + + public get script() { + return this.spec.script; + } + + public set script(script) { + this.spec.script = script; + } + + public get lang() { + return this.spec.lang; + } + + public set lang(lang) { + this.spec.lang = lang; + } + + public get conflictDescriptions() { + return this.spec.conflictDescriptions; + } + + public set conflictDescriptions(conflictDescriptions) { + this.spec.conflictDescriptions = conflictDescriptions; + } + + // read only attrs + public get name() { + return this.spec.name; + } + + public get type() { + return this.spec.type; + } + + public get esTypes() { + return this.spec.esTypes; + } + + public get scripted() { + return this.spec.scripted; + } + + public get searchable() { + return !!(this.spec.searchable || this.scripted); + } + + public get aggregatable() { + return !!(this.spec.aggregatable || this.scripted); + } + + public get readFromDocValues() { + return !!(this.spec.readFromDocValues && !this.scripted); + } + + public get subType() { + return this.spec.subType; + } + + // not writable, not serialized + public get sortable() { + return ( + this.name === '_score' || + ((this.spec.indexed || this.aggregatable) && this.kbnFieldType.sortable) + ); + } + + public get filterable() { + return ( + this.name === '_id' || + this.scripted || + ((this.spec.indexed || this.searchable) && this.kbnFieldType.filterable) + ); + } + + public get visualizable() { + return this.aggregatable; + } + + public get format(): FieldFormat { + return this.indexPattern.getFormatterForField(this); + } + + public toJSON() { + return { + count: this.count, + script: this.script, + lang: this.lang, + conflictDescriptions: this.conflictDescriptions, + + name: this.name, + type: this.type, + esTypes: this.esTypes, + scripted: this.scripted, + searchable: this.searchable, + aggregatable: this.aggregatable, + readFromDocValues: this.readFromDocValues, + subType: this.subType, + }; + } + + public toSpec() { + return { + count: this.count, + script: this.script, + lang: this.lang, + conflictDescriptions: this.conflictDescriptions, + name: this.name, + type: this.type, + esTypes: this.esTypes, + scripted: this.scripted, + searchable: this.searchable, + aggregatable: this.aggregatable, + readFromDocValues: this.readFromDocValues, + subType: this.subType, + format: this.indexPattern?.fieldFormatMap[this.name]?.toJSON() || undefined, + }; + } +} diff --git a/src/plugins/data/common/index_patterns/fields/obj_define.js b/src/plugins/data/common/index_patterns/fields/obj_define.js deleted file mode 100644 index 9c9e5c8f3d55f..0000000000000 --- a/src/plugins/data/common/index_patterns/fields/obj_define.js +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; - -export function ObjDefine(defaults, prototype) { - this.obj; // created by this.create() - - this.descs = {}; - this.defaults = defaults || {}; - this.prototype = prototype || Object.prototype; -} - -ObjDefine.REDEFINE_SUPPORTED = (function () { - const a = Object.create(Object.prototype, { - prop: { - configurable: true, - value: 1, - }, - }); - - Object.defineProperty(a, 'prop', { - configurable: true, - value: 2, - }); - - return a.prop === 2; -})(); - -/** - * normal value, writable and exported in JSON - * - * @param {any} v - value - * @return {object} - property descriptor - */ -ObjDefine.prototype.writ = function (name, val) { - this._define(name, val, true, true); -}; - -/** - * known value, exported in JSON, not changeable - * - * @param {any} v - value - * @return {object} - property descriptor - */ -ObjDefine.prototype.fact = function (name, val) { - this._define(name, val, true); -}; - -/** - * computed fact, not exported or changeable - * - * @param {any} v - value - * @return {object} - property descriptor - */ -ObjDefine.prototype.comp = function (name, val) { - this._define(name, val); -}; - -/** - * Creates an object, decorated by the property descriptors - * created by other ObjDefine methods and inheriting form the - * prototype - * - * # note: - * If a value is writable, but the value is undefined, the property will - * be created by not exported to JSON unless the property is written to - * - * @return {object} - created object - */ -ObjDefine.prototype.create = function () { - const self = this; - self.obj = Object.create(this.prototype, self.descs); - - if (!ObjDefine.REDEFINE_SUPPORTED && !self.prototype.toJSON) { - // since we can't redefine properties as enumerable we will - // clone the object on serialization and choose which properties - // to include or trim manually. This is currently only in use in PhantomJS - // due to https://github.com/ariya/phantomjs/issues/11856 - // TODO: remove this: https://github.com/elastic/kibana/issues/27136 - self.obj.toJSON = function () { - return _.transform( - self.obj, - function (json, val, key) { - const desc = self.descs[key]; - if (desc && desc.enumerable && val == null) return; - json[key] = val; - }, - {} - ); - }; - } - - return self.obj; -}; - -/** - * Private APIS - */ - -ObjDefine.prototype._define = function (name, val, exported, changeable) { - val = val != null ? val : this.defaults[name]; - this.descs[name] = this._describe(name, val, !!exported, !!changeable); -}; - -ObjDefine.prototype._describe = function (name, val, exported, changeable) { - const self = this; - const exists = val != null; - - if (exported && ObjDefine.REDEFINE_SUPPORTED) { - return { - enumerable: exists, - configurable: true, - get: _.constant(val), - set: function (update) { - if (!changeable) return false; - - // change the descriptor, since the value now exists. - self.descs[name] = self._describe(name, update, exported, changeable); - - // apply the updated descriptor - Object.defineProperty(self.obj, name, self.descs[name]); - }, - }; - } - - if (exported && !ObjDefine.REDEFINE_SUPPORTED) { - return { - enumerable: true, - configurable: true, - writable: changeable, - value: val, - }; - } - - return { - enumerable: false, - writable: changeable, - configurable: true, - value: val, - }; -}; diff --git a/src/plugins/data/common/index_patterns/fields/obj_define.test.js b/src/plugins/data/common/index_patterns/fields/obj_define.test.js deleted file mode 100644 index ec9a022253621..0000000000000 --- a/src/plugins/data/common/index_patterns/fields/obj_define.test.js +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import { ObjDefine } from './obj_define'; - -describe('ObjDefine Utility', function () { - function flatten(obj) { - return JSON.parse(JSON.stringify(obj)); - } - - describe('#writ', function () { - it('creates writeable properties', function () { - const def = new ObjDefine(); - def.writ('name', 'foo'); - - const obj = def.create(); - expect(obj).to.have.property('name', 'foo'); - - obj.name = 'bar'; - expect(obj).to.have.property('name', 'bar'); - }); - - it('exports the property to JSON', function () { - const def = new ObjDefine(); - def.writ('name', 'foo'); - expect(flatten(def.create())).to.have.property('name', 'foo'); - }); - - it("does not export property to JSON it it's undefined or null", function () { - const def = new ObjDefine(); - def.writ('name'); - expect(flatten(def.create())).to.not.have.property('name'); - - def.writ('name', null); - expect(flatten(def.create())).to.not.have.property('name'); - }); - - it('switched to exporting if a value is written', function () { - const def = new ObjDefine(); - def.writ('name'); - - const obj = def.create(); - expect(flatten(obj)).to.not.have.property('name'); - - obj.name = null; - expect(flatten(obj)).to.not.have.property('name'); - - obj.name = 'foo'; - expect(flatten(obj)).to.have.property('name', 'foo'); - }); - - it('setting a writ value to null prevents it from exporting', function () { - const def = new ObjDefine(); - def.writ('name', 'foo'); - - const obj = def.create(); - expect(flatten(obj)).to.have.property('name', 'foo'); - - obj.name = null; - expect(flatten(obj)).to.not.have.property('name'); - }); - }); - - describe('#fact', function () { - it('creates an immutable field', function () { - const def = new ObjDefine(); - const val = 'foo'; - const notval = 'bar'; - def.fact('name', val); - const obj = def.create(); - - obj.name = notval; // UPDATE SHOULD BE IGNORED - expect(obj).to.have.property('name', val); - }); - - it('exports the fact to JSON', function () { - const def = new ObjDefine(); - def.fact('name', 'foo'); - expect(flatten(def.create())).to.have.property('name', 'foo'); - }); - }); - - describe('#comp', function () { - it('creates an immutable field', function () { - const def = new ObjDefine(); - const val = 'foo'; - const notval = 'bar'; - def.comp('name', val); - const obj = def.create(); - - expect(function () { - 'use strict'; // eslint-disable-line strict - - obj.name = notval; - }).to.throwException(); - }); - - it('does not export the computed value to JSON', function () { - const def = new ObjDefine(); - def.comp('name', 'foo'); - expect(flatten(def.create())).to.not.have.property('name'); - }); - }); - - describe('#create', function () { - it('creates object that inherits from the prototype', function () { - function SomeClass() {} - - const def = new ObjDefine(null, SomeClass.prototype); - const obj = def.create(); - - expect(obj).to.be.a(SomeClass); - }); - - it('uses the defaults for property values', function () { - const def = new ObjDefine({ name: 'bar' }); - def.fact('name'); - - const obj = def.create(); - - expect(obj).to.have.property('name', 'bar'); - }); - - it('ignores default values that are not defined properties', function () { - const def = new ObjDefine({ name: 'foo', name2: 'bar' }); - const obj = def.create(); - - expect(obj).to.not.have.property('name'); - expect(obj).to.not.have.property('name2'); - }); - }); -}); diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts index ebf873b14c379..e4f297b29c372 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts @@ -26,7 +26,7 @@ import { DuplicateField } from '../../../../kibana_utils/common'; import mockLogStashFields from '../../../../../fixtures/logstash_fields'; // @ts-ignore import { stubbedSavedObjectIndexPattern } from '../../../../../fixtures/stubbed_saved_object_index_pattern'; -import { Field } from '../fields'; +import { IndexPatternField } from '../fields'; import { fieldFormatsMock } from '../../field_formats/mocks'; @@ -170,8 +170,8 @@ describe('IndexPattern', () => { describe('getScriptedFields', () => { test('should return all scripted fields', () => { const scriptedNames = mockLogStashFields() - .filter((item: Field) => item.scripted === true) - .map((item: Field) => item.name); + .filter((item: IndexPatternField) => item.scripted === true) + .map((item: IndexPatternField) => item.name); const respNames = map(indexPattern.getScriptedFields(), 'name'); expect(respNames).toEqual(scriptedNames); @@ -214,8 +214,8 @@ describe('IndexPattern', () => { describe('getNonScriptedFields', () => { test('should return all non-scripted fields', () => { const notScriptedNames = mockLogStashFields() - .filter((item: Field) => item.scripted === false) - .map((item: Field) => item.name); + .filter((item: IndexPatternField) => item.scripted === false) + .map((item: IndexPatternField) => item.name); const respNames = map(indexPattern.getNonScriptedFields(), 'name'); expect(respNames).toEqual(notScriptedNames); @@ -235,7 +235,7 @@ describe('IndexPattern', () => { const newFields = indexPattern.getNonScriptedFields(); expect(newFields).toHaveLength(2); - expect(newFields.map((f) => f.name)).toEqual(['foo', 'bar']); + expect([...newFields.map((f) => f.name)]).toEqual(['foo', 'bar']); }); test('should preserve the scripted fields', async () => { @@ -249,8 +249,8 @@ describe('IndexPattern', () => { // sinon.assert.calledOnce(indexPattern.getScriptedFields); expect(indexPattern.getScriptedFields().map((f) => f.name)).toEqual( mockLogStashFields() - .filter((f: Field) => f.scripted) - .map((f: Field) => f.name) + .filter((f: IndexPatternField) => f.scripted) + .map((f: IndexPatternField) => f.name) ); }); }); @@ -278,7 +278,7 @@ describe('IndexPattern', () => { const scriptedFields = indexPattern.getScriptedFields(); // expect(saveSpy.callCount).to.equal(1); expect(scriptedFields).toHaveLength(oldCount + 1); - expect((indexPattern.fields.getByName(scriptedField.name) as Field).name).toEqual( + expect((indexPattern.fields.getByName(scriptedField.name) as IndexPatternField).name).toEqual( scriptedField.name ); }); @@ -287,9 +287,9 @@ describe('IndexPattern', () => { // const saveSpy = sinon.spy(indexPattern, 'save'); const scriptedFields = indexPattern.getScriptedFields(); const oldCount = scriptedFields.length; - const scriptedField = last(scriptedFields) as any; + const scriptedField = last(scriptedFields)!; - await indexPattern.removeScriptedField(scriptedField); + await indexPattern.removeScriptedField(scriptedField.name); // expect(saveSpy.callCount).to.equal(1); expect(indexPattern.getScriptedFields().length).toEqual(oldCount - 1); diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index 2acb9d5f767ad..211919e8e6b53 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -22,10 +22,10 @@ import { i18n } from '@kbn/i18n'; import { SavedObjectsClientCommon } from '../..'; import { DuplicateField, SavedObjectNotFound } from '../../../../kibana_utils/common'; -import { ES_FIELD_TYPES, KBN_FIELD_TYPES, IIndexPattern, IFieldType } from '../../../common'; +import { ES_FIELD_TYPES, KBN_FIELD_TYPES, IIndexPattern } from '../../../common'; import { findByTitle } from '../utils'; import { IndexPatternMissingIndices } from '../lib'; -import { Field, IIndexPatternFieldList, getIndexPatternFieldListCreator } from '../fields'; +import { IndexPatternField, IIndexPatternFieldList, FieldList } from '../fields'; import { createFieldsFetcher } from './_fields_fetcher'; import { formatHitProvider } from './format_hit'; import { flattenHitWrapper } from './flatten_hit'; @@ -36,7 +36,7 @@ import { IIndexPatternsApiClient, IndexPatternAttributes, } from '../types'; -import { FieldFormatsStartCommon } from '../../field_formats'; +import { FieldFormatsStartCommon, FieldFormat } from '../../field_formats'; import { PatternCache } from './_pattern_cache'; import { expandShorthand, FieldMappingSpec, MappingObject } from '../../field_mapping'; import { IndexPatternSpec, TypeMeta, FieldSpec, SourceFilter } from '../types'; @@ -138,12 +138,8 @@ export class IndexPattern implements IIndexPattern { this.shortDotsEnable = uiSettingsValues.shortDotsEnable; this.metaFields = uiSettingsValues.metaFields; - this.createFieldList = getIndexPatternFieldListCreator({ - fieldFormats, - onNotification, - }); + this.fields = new FieldList(this, [], this.shortDotsEnable, this.onUnknownType); - this.fields = this.createFieldList(this, [], this.shortDotsEnable); this.apiClient = apiClient; this.fieldsFetcher = createFieldsFetcher(this, apiClient, uiSettingsValues.metaFields); this.flattenHit = flattenHitWrapper(this, uiSettingsValues.metaFields); @@ -161,49 +157,45 @@ export class IndexPattern implements IIndexPattern { } private deserializeFieldFormatMap(mapping: any) { - const FieldFormat = this.fieldFormats.getType(mapping.id); + const FieldFormatter = this.fieldFormats.getType(mapping.id); return ( - FieldFormat && - new FieldFormat( + FieldFormatter && + new FieldFormatter( mapping.params, (key: string) => this.uiSettingsValues[key]?.userValue || this.uiSettingsValues[key]?.value ) ); } - private initFields(input?: any) { - const newValue = input || this.fields; - - this.fields = this.createFieldList(this, newValue, this.shortDotsEnable); - } - - private isFieldRefreshRequired(): boolean { - if (!this.fields) { + private isFieldRefreshRequired(specs?: FieldSpec[]): boolean { + if (!specs) { return true; } - return this.fields.every((field) => { + return specs.every((spec) => { // See https://github.com/elastic/kibana/pull/8421 - const hasFieldCaps = 'aggregatable' in field && 'searchable' in field; + const hasFieldCaps = 'aggregatable' in spec && 'searchable' in spec; // See https://github.com/elastic/kibana/pull/11969 - const hasDocValuesFlag = 'readFromDocValues' in field; + const hasDocValuesFlag = 'readFromDocValues' in spec; return !hasFieldCaps || !hasDocValuesFlag; }); } - private async indexFields(forceFieldRefresh: boolean = false) { + private async indexFields(forceFieldRefresh: boolean = false, specs?: FieldSpec[]) { if (!this.id) { return; } - if (forceFieldRefresh || this.isFieldRefreshRequired()) { + if (forceFieldRefresh || this.isFieldRefreshRequired(specs)) { await this.refreshFields(); + } else { + if (specs) { + this.fields.replaceAll(specs); + } } - - this.initFields(); } public initFromSpec(spec: IndexPatternSpec) { @@ -223,15 +215,13 @@ export class IndexPattern implements IIndexPattern { this.timeFieldName = spec.timeFieldName; this.sourceFilters = spec.sourceFilters; - // ignoring this because the same thing happens elsewhere but via _.assign - // @ts-expect-error - this.fields = spec.fields || []; + this.fields.replaceAll(spec.fields || []); this.typeMeta = spec.typeMeta; + this.fieldFormatMap = _.mapValues(fieldFormatMap, (mapping) => { return this.deserializeFieldFormatMap(mapping); }); - this.initFields(); return this; } @@ -249,14 +239,16 @@ export class IndexPattern implements IIndexPattern { }); // give index pattern all of the values + const fieldList = this.fields; _.assign(this, response); + this.fields = fieldList; if (!this.title && this.id) { this.title = this.id; } this.version = response.version; - return this.indexFields(forceFieldRefresh); + return this.indexFields(forceFieldRefresh, response.fields); } getComputedFields() { @@ -359,32 +351,26 @@ export class IndexPattern implements IIndexPattern { throw new DuplicateField(name); } - this.fields.add( - new Field( - this, - { - name, - script, - fieldType, - scripted: true, - lang, - aggregatable: true, - filterable: true, - searchable: true, - }, - false, - { - fieldFormats: this.fieldFormats, - onNotification: this.onNotification, - } - ) - ); + this.fields.add({ + name, + script, + type: fieldType, + scripted: true, + lang, + aggregatable: true, + searchable: true, + count: 0, + readFromDocValues: false, + }); await this.save(); } - removeScriptedField(field: IFieldType) { - this.fields.remove(field); + removeScriptedField(fieldName: string) { + const field = this.fields.getByName(fieldName); + if (field) { + this.fields.remove(field); + } return this.save(); } @@ -417,11 +403,11 @@ export class IndexPattern implements IIndexPattern { } getNonScriptedFields() { - return _.filter(this.fields, { scripted: false }); + return [...this.fields.filter((field) => !field.scripted)]; } getScriptedFields() { - return _.filter(this.fields, { scripted: true }); + return [...this.fields.filter((field) => field.scripted)]; } isTimeBased(): boolean { @@ -438,12 +424,12 @@ export class IndexPattern implements IIndexPattern { } getTimeField() { - if (!this.timeFieldName || !this.fields || !this.fields.getByName) return; - return this.fields.getByName(this.timeFieldName); + if (!this.timeFieldName || !this.fields || !this.fields.getByName) return undefined; + return this.fields.getByName(this.timeFieldName) || undefined; } - getFieldByName(name: string): Field | void { - if (!this.fields || !this.fields.getByName) return; + getFieldByName(name: string): IndexPatternField | undefined { + if (!this.fields || !this.fields.getByName) return undefined; return this.fields.getByName(name); } @@ -470,6 +456,16 @@ export class IndexPattern implements IIndexPattern { return body; } + getFormatterForField(field: IndexPatternField | IndexPatternField['spec']): FieldFormat { + return ( + this.fieldFormatMap[field.name] || + this.fieldFormats.getDefaultInstance( + field.type as KBN_FIELD_TYPES, + field.esTypes as ES_FIELD_TYPES[] + ) + ); + } + async create(allowOverride: boolean = false) { const _create = async (duplicateId?: string) => { if (duplicateId) { @@ -581,9 +577,8 @@ export class IndexPattern implements IIndexPattern { async _fetchFields() { const fields = await this.fieldsFetcher.fetch(this); - const scripted = this.getScriptedFields(); - const all = fields.concat(scripted); - await this.initFields(all); + const scripted = this.getScriptedFields().map((field) => field.spec); + this.fields.replaceAll([...fields, ...scripted]); } refreshFields() { diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts index a07ffaf92aea5..8874ce5f04b7c 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts @@ -25,14 +25,13 @@ import { createEnsureDefaultIndexPattern, EnsureDefaultIndexPattern, } from './ensure_default_index_pattern'; -import { getIndexPatternFieldListCreator, CreateIndexPatternFieldList, Field } from '../fields'; +import { IndexPatternField } from '../fields'; import { OnNotification, OnError, UiSettingsCommon, IIndexPatternsApiClient, GetFieldsOptions, - FieldSpec, IndexPatternSpec, } from '../types'; import { FieldFormatsStartCommon } from '../../field_formats'; @@ -65,12 +64,6 @@ export class IndexPatternsService { private onNotification: OnNotification; private onError: OnError; ensureDefaultIndexPattern: EnsureDefaultIndexPattern; - createFieldList: CreateIndexPatternFieldList; - createField: ( - indexPattern: IndexPattern, - spec: FieldSpec | Field, - shortDotsEnable: boolean - ) => Field; constructor({ uiSettings, @@ -91,16 +84,15 @@ export class IndexPatternsService { uiSettings, onRedirectNoIndexPattern ); - this.createFieldList = getIndexPatternFieldListCreator({ - fieldFormats, - onNotification, - }); - this.createField = (indexPattern, spec, shortDotsEnable) => { - return new Field(indexPattern, spec, shortDotsEnable, { - fieldFormats, - onNotification, - }); - }; + } + + public createField( + indexPattern: IndexPattern, + spec: IndexPatternField['spec'], + displayName: string, + onNotification: OnNotification + ) { + return new IndexPatternField(indexPattern, spec, displayName, onNotification); } private async refreshSavedObjectsCache() { diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 4241df5718243..3a7cf54843dfc 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -149,8 +149,21 @@ export interface FieldSpecExportFmt { } export interface FieldSpec { - [key: string]: any; + count: number; + script?: string; + lang?: string; + conflictDescriptions?: Record; format?: SerializedFieldFormat; + + name: string; + type: string; + esTypes?: string[]; + scripted: boolean; + searchable: boolean; + aggregatable: boolean; + readFromDocValues?: boolean; + subType?: IFieldSubType; + indexed?: boolean; } export interface IndexPatternSpec { diff --git a/src/plugins/data/common/kbn_field_types/kbn_field_types.test.ts b/src/plugins/data/common/kbn_field_types/kbn_field_types.test.ts index a3fe19fa9b2fc..6a2d6edd04692 100644 --- a/src/plugins/data/common/kbn_field_types/kbn_field_types.test.ts +++ b/src/plugins/data/common/kbn_field_types/kbn_field_types.test.ts @@ -55,10 +55,10 @@ describe('utils/kbn_field_types', () => { expect(kbnFieldType).toHaveProperty('name', ES_FIELD_TYPES.STRING); }); - test('returns undefined for invalid name', () => { + test('returns unknown for invalid name', () => { const kbnFieldType = getKbnFieldType('wrongType'); - expect(kbnFieldType).toBeUndefined(); + expect(kbnFieldType).toHaveProperty('name', KBN_FIELD_TYPES.UNKNOWN); }); }); diff --git a/src/plugins/data/common/kbn_field_types/kbn_field_types.ts b/src/plugins/data/common/kbn_field_types/kbn_field_types.ts index ce05dc796bbab..ffeb9c517daf5 100644 --- a/src/plugins/data/common/kbn_field_types/kbn_field_types.ts +++ b/src/plugins/data/common/kbn_field_types/kbn_field_types.ts @@ -17,7 +17,7 @@ * under the License. */ -import { createKbnFieldTypes } from './kbn_field_types_factory'; +import { createKbnFieldTypes, kbnFieldTypeUnknown } from './kbn_field_types_factory'; import { KbnFieldType } from './kbn_field_type'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from './types'; @@ -30,8 +30,8 @@ const registeredKbnTypes = createKbnFieldTypes(); * @param {string} typeName * @return {KbnFieldType} */ -export const getKbnFieldType = (typeName: string): KbnFieldType | undefined => - registeredKbnTypes.find((t) => t.name === typeName); +export const getKbnFieldType = (typeName: string): KbnFieldType => + registeredKbnTypes.find((t) => t.name === typeName) || kbnFieldTypeUnknown; /** * Get the esTypes known by all kbnFieldTypes diff --git a/src/plugins/data/common/kbn_field_types/kbn_field_types_factory.ts b/src/plugins/data/common/kbn_field_types/kbn_field_types_factory.ts index cb9357eb9865e..b93ebcbbca9c8 100644 --- a/src/plugins/data/common/kbn_field_types/kbn_field_types_factory.ts +++ b/src/plugins/data/common/kbn_field_types/kbn_field_types_factory.ts @@ -20,6 +20,10 @@ import { KbnFieldType } from './kbn_field_type'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from './types'; +export const kbnFieldTypeUnknown = new KbnFieldType({ + name: KBN_FIELD_TYPES.UNKNOWN, +}); + export const createKbnFieldTypes = (): KbnFieldType[] => [ new KbnFieldType({ name: KBN_FIELD_TYPES.STRING, @@ -103,7 +107,5 @@ export const createKbnFieldTypes = (): KbnFieldType[] => [ new KbnFieldType({ name: KBN_FIELD_TYPES.CONFLICT, }), - new KbnFieldType({ - name: KBN_FIELD_TYPES.UNKNOWN, - }), + kbnFieldTypeUnknown, ]; diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index e95150e8f6f73..5a9930d2b6b56 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -249,9 +249,7 @@ export { IndexPatternsContract, IndexPattern, IIndexPatternFieldList, - Field as IndexPatternField, - // TODO: exported only in stub_index_pattern test. Move into data plugin and remove export. - getIndexPatternFieldListCreator, + IndexPatternField, } from './index_patterns'; export { @@ -264,6 +262,7 @@ export { UI_SETTINGS, TypeMeta as IndexPatternTypeMeta, AggregationRestrictions as IndexPatternAggRestrictions, + FieldList, } from '../common'; /* diff --git a/src/plugins/data/public/index_patterns/index.ts b/src/plugins/data/public/index_patterns/index.ts index a6ee71c624f5a..9cd5e5a4736f1 100644 --- a/src/plugins/data/public/index_patterns/index.ts +++ b/src/plugins/data/public/index_patterns/index.ts @@ -28,11 +28,7 @@ export { } from '../../common/index_patterns/lib'; export { flattenHitWrapper, formatHitProvider, onRedirectNoIndexPattern } from './index_patterns'; -export { - getIndexPatternFieldListCreator, - Field, - IIndexPatternFieldList, -} from '../../common/index_patterns'; +export { IndexPatternField, IIndexPatternFieldList } from '../../common/index_patterns'; export { IndexPatternsService, diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 2cfdab80123ed..76f88df4dd6fc 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -576,6 +576,44 @@ export type FieldFormatsContentType = 'html' | 'text'; // @public (undocumented) export type FieldFormatsGetConfigFn = (key: string, defaultOverride?: T) => T; +// Warning: (ae-missing-release-tag) "FieldList" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class FieldList extends Array implements IIndexPatternFieldList { + // Warning: (ae-forgotten-export) The symbol "FieldSpec" needs to be exported by the entry point index.d.ts + constructor(indexPattern: IndexPattern, specs?: FieldSpec[], shortDotsEnable?: boolean, onNotification?: () => void); + // (undocumented) + readonly add: (field: FieldSpec) => void; + // (undocumented) + readonly getByName: (name: IndexPatternField['name']) => IndexPatternField | undefined; + // (undocumented) + readonly getByType: (type: IndexPatternField['type']) => any[]; + // (undocumented) + readonly remove: (field: IFieldType) => void; + // (undocumented) + readonly removeAll: () => void; + // (undocumented) + readonly replaceAll: (specs: FieldSpec[]) => void; + // (undocumented) + readonly toSpec: () => { + count: number; + script: string | undefined; + lang: string | undefined; + conflictDescriptions: Record | undefined; + name: string; + type: string; + esTypes: string[] | undefined; + scripted: boolean; + searchable: boolean; + aggregatable: boolean; + readFromDocValues: boolean; + subType: import("../types").IFieldSubType | undefined; + format: any; + }[]; + // (undocumented) + readonly update: (field: FieldSpec) => void; +} + // @public (undocumented) export interface FieldMappingSpec { // (undocumented) @@ -658,13 +696,6 @@ export function getDefaultQuery(language?: QueryLanguage): { // @public (undocumented) export function getEsPreference(uiSettings: IUiSettingsClient_2, sessionId?: string): any; -// Warning: (ae-forgotten-export) The symbol "FieldListDependencies" needs to be exported by the entry point index.d.ts -// Warning: (ae-forgotten-export) The symbol "CreateIndexPatternFieldList" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "getIndexPatternFieldListCreator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export const getIndexPatternFieldListCreator: ({ fieldFormats, onNotification, }: FieldListDependencies) => CreateIndexPatternFieldList; - // Warning: (ae-missing-release-tag) "getKbnTypeNames" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @@ -808,8 +839,6 @@ export interface IFieldType { sortable?: boolean; // (undocumented) subType?: IFieldSubType; - // Warning: (ae-forgotten-export) The symbol "FieldSpec" needs to be exported by the entry point index.d.ts - // // (undocumented) toSpec?: () => FieldSpec; // (undocumented) @@ -856,6 +885,10 @@ export interface IIndexPatternFieldList extends Array { // (undocumented) remove(field: IFieldType): void; // (undocumented) + removeAll(): void; + // (undocumented) + replaceAll(specs: FieldSpec[]): void; + // (undocumented) update(field: FieldSpec): void; } @@ -929,7 +962,9 @@ export class IndexPattern implements IIndexPattern { }[]; }; // (undocumented) - getFieldByName(name: string): IndexPatternField | void; + getFieldByName(name: string): IndexPatternField | undefined; + // (undocumented) + getFormatterForField(field: IndexPatternField | IndexPatternField['spec']): FieldFormat; // (undocumented) getNonScriptedFields(): IndexPatternField[]; // (undocumented) @@ -967,7 +1002,7 @@ export class IndexPattern implements IIndexPattern { // (undocumented) refreshFields(): Promise; // (undocumented) - removeScriptedField(field: IFieldType): Promise; + removeScriptedField(fieldName: string): Promise; // (undocumented) save(saveAttempts?: number): Promise; // (undocumented) @@ -1018,55 +1053,85 @@ export interface IndexPatternAttributes { typeMeta: string; } -// Warning: (ae-missing-release-tag) "Field" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "IndexPatternField" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export class IndexPatternField implements IFieldType { + // Warning: (ae-forgotten-export) The symbol "OnNotification" needs to be exported by the entry point index.d.ts + constructor(indexPattern: IndexPattern, spec: FieldSpec, displayName: string, onNotification: OnNotification); // (undocumented) - $$spec: FieldSpec; - // Warning: (ae-forgotten-export) The symbol "FieldSpecExportFmt" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "FieldDependencies" needs to be exported by the entry point index.d.ts - constructor(indexPattern: IIndexPattern, spec: FieldSpecExportFmt | FieldSpec | IndexPatternField, shortDotsEnable: boolean, { fieldFormats, onNotification }: FieldDependencies); + get aggregatable(): boolean; // (undocumented) - aggregatable?: boolean; - // Warning: (ae-forgotten-export) The symbol "FieldSpecConflictDescriptions" needs to be exported by the entry point index.d.ts - // + get conflictDescriptions(): Record | undefined; + set conflictDescriptions(conflictDescriptions: Record | undefined); // (undocumented) - conflictDescriptions?: FieldSpecConflictDescriptions; + get count(): number; + set count(count: number); // (undocumented) - count?: number; + readonly displayName: string; // (undocumented) - displayName?: string; + get esTypes(): string[] | undefined; // (undocumented) - esTypes?: string[]; + get filterable(): boolean; // (undocumented) - filterable?: boolean; + get format(): FieldFormat; // (undocumented) - format: any; + readonly indexPattern: IndexPattern; // (undocumented) - indexPattern?: IIndexPattern; + get lang(): string | undefined; + set lang(lang: string | undefined); // (undocumented) - lang?: string; + get name(): string; // (undocumented) - name: string; + get readFromDocValues(): boolean; // (undocumented) - readFromDocValues?: boolean; + get script(): string | undefined; + set script(script: string | undefined); // (undocumented) - script?: string; + get scripted(): boolean; // (undocumented) - scripted?: boolean; + get searchable(): boolean; // (undocumented) - searchable?: boolean; + get sortable(): boolean; // (undocumented) - sortable?: boolean; + readonly spec: FieldSpec; // (undocumented) - subType?: IFieldSubType; + get subType(): import("../types").IFieldSubType | undefined; + // (undocumented) + toJSON(): { + count: number; + script: string | undefined; + lang: string | undefined; + conflictDescriptions: Record | undefined; + name: string; + type: string; + esTypes: string[] | undefined; + scripted: boolean; + searchable: boolean; + aggregatable: boolean; + readFromDocValues: boolean; + subType: import("../types").IFieldSubType | undefined; + }; // (undocumented) - toSpec: () => FieldSpecExportFmt; + toSpec(): { + count: number; + script: string | undefined; + lang: string | undefined; + conflictDescriptions: Record | undefined; + name: string; + type: string; + esTypes: string[] | undefined; + scripted: boolean; + searchable: boolean; + aggregatable: boolean; + readFromDocValues: boolean; + subType: import("../types").IFieldSubType | undefined; + format: any; + }; // (undocumented) - type: string; + get type(): string; // (undocumented) - visualizable?: boolean; + get visualizable(): boolean; } // Warning: (ae-missing-release-tag) "indexPatterns" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1899,21 +1964,21 @@ export const UI_SETTINGS: { // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "getFromSavedObject" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:371:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:371:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:371:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:371:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:373:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:374:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:383:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:384:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:385:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:386:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:390:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:394:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:395:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:398:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:370:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:370:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:370:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:370:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:372:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:373:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:382:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:383:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:384:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:385:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:390:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:393:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:394:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:397:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:45:5 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts // src/plugins/data/public/types.ts:54:5 - (ae-forgotten-export) The symbol "createFiltersFromValueClickAction" needs to be exported by the entry point index.d.ts // src/plugins/data/public/types.ts:55:5 - (ae-forgotten-export) The symbol "createFiltersFromRangeSelectAction" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/public/search/aggs/agg_configs.test.ts b/src/plugins/data/public/search/aggs/agg_configs.test.ts index df4a5420ae0db..ff0cc3341929e 100644 --- a/src/plugins/data/public/search/aggs/agg_configs.test.ts +++ b/src/plugins/data/public/search/aggs/agg_configs.test.ts @@ -22,7 +22,7 @@ import { AggConfig } from './agg_config'; import { AggConfigs } from './agg_configs'; import { AggTypesRegistryStart } from './agg_types_registry'; import { mockAggTypesRegistry } from './test_helpers'; -import { Field as IndexPatternField, IndexPattern } from '../../index_patterns'; +import { IndexPatternField, IndexPattern } from '../../index_patterns'; import { stubIndexPattern, stubIndexPatternWithFields } from '../../../public/stubs'; describe('AggConfigs', () => { diff --git a/src/plugins/data/public/search/aggs/param_types/field.ts b/src/plugins/data/public/search/aggs/param_types/field.ts index cb3617b02e882..7c00bc668a39f 100644 --- a/src/plugins/data/public/search/aggs/param_types/field.ts +++ b/src/plugins/data/public/search/aggs/param_types/field.ts @@ -23,7 +23,7 @@ import { SavedObjectNotFound } from '../../../../../../plugins/kibana_utils/comm import { BaseParamType } from './base'; import { propFilter } from '../utils'; import { isNestedField, KBN_FIELD_TYPES } from '../../../../common'; -import { Field as IndexPatternField } from '../../../index_patterns'; +import { IndexPatternField } from '../../../index_patterns'; const filterByType = propFilter('type'); diff --git a/src/plugins/discover/public/application/angular/doc_table/components/row_headers.test.js b/src/plugins/discover/public/application/angular/doc_table/components/row_headers.test.js index b30b13b1f0b6e..d85ca6a072890 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/row_headers.test.js +++ b/src/plugins/discover/public/application/angular/doc_table/components/row_headers.test.js @@ -23,7 +23,7 @@ import 'angular-sanitize'; import 'angular-route'; import _ from 'lodash'; import sinon from 'sinon'; -import { getFakeRow, getFakeRowVals } from 'fixtures/fake_row'; +import { getFakeRow } from 'fixtures/fake_row'; import $ from 'jquery'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; import { setScopedHistory, setServices, setDocViewsRegistry } from '../../../../kibana_services'; @@ -33,6 +33,13 @@ import { navigationPluginMock } from '../../../../../../navigation/public/mocks' import { getInnerAngularModule } from '../../../../get_inner_angular'; import { createBrowserHistory } from 'history'; +const fakeRowVals = { + time: 'time_formatted', + bytes: 'bytes_formatted', + '@timestamp': '@timestamp_formatted', + request_body: 'request_body_formatted', +}; + describe('Doc Table', () => { const core = coreMock.createStart(); const dataMock = dataPluginMock.createStartContract(); @@ -45,8 +52,6 @@ describe('Doc Table', () => { // Stub out a minimal mapping of 4 fields let mapping; - let fakeRowVals; - let stubFieldFormatConverter; beforeAll(() => setScopedHistory(createBrowserHistory())); beforeEach(() => { angular.element.prototype.slice = jest.fn(function (index) { @@ -97,21 +102,15 @@ describe('Doc Table', () => { mapping = $parentScope.indexPattern.fields; // Stub `getConverterFor` for a field in the indexPattern to return mock data. - // Returns `val` if provided, otherwise generates fake data for the field. - fakeRowVals = getFakeRowVals('formatted', 0, mapping); - stubFieldFormatConverter = function ($root, field, val) { - const convertFn = (value, type, options) => { - if (val) { - return val; - } - const fieldName = _.get(options, 'field.name', null); - - return fakeRowVals[fieldName] || ''; - }; - - $root.indexPattern.fields.getByName(field).format.convert = convertFn; - $root.indexPattern.fields.getByName(field).format.getConverterFor = () => convertFn; + + const convertFn = (value, type, options) => { + const fieldName = _.get(options, 'field.name', null); + return fakeRowVals[fieldName] || ''; }; + $parentScope.indexPattern.getFormatterForField = () => ({ + convert: convertFn, + getConverterFor: () => convertFn, + }); }) ); @@ -148,9 +147,6 @@ describe('Doc Table', () => { test('should be able to add and remove columns', () => { let childElems; - stubFieldFormatConverter($parentScope, 'bytes'); - stubFieldFormatConverter($parentScope, 'request_body'); - // Should include a column for toggling and the time column by default $parentScope.columns = ['bytes']; $elementScope.$digest(); @@ -302,9 +298,6 @@ describe('Doc Table', () => { $root.mapping = mapping; $root.indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - // Stub field format converters for every field in the indexPattern - $root.indexPattern.fields.forEach((f) => stubFieldFormatConverter($root, f.name)); - $row = $('').attr({ 'kbn-table-row': 'row', columns: 'columns', @@ -417,7 +410,8 @@ describe('Doc Table', () => { }); test('handles two columns with the same content', () => { - stubFieldFormatConverter($root, 'request_body', fakeRowVals.bytes); + const tempVal = fakeRowVals.request_body; + fakeRowVals.request_body = 'bytes_formatted'; $root.columns.length = 0; $root.columns.push('bytes'); @@ -428,6 +422,7 @@ describe('Doc Table', () => { expect($after).toHaveLength(4); expect($after.eq(2).text().trim()).toMatch(/^bytes_formatted/); expect($after.eq(3).text().trim()).toMatch(/^bytes_formatted/); + fakeRowVals.request_body = tempVal; }); test('handles two columns swapping position', () => { diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx index 099ec2e5b1ffc..3f12a8c0fa769 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx @@ -28,7 +28,6 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { DiscoverField } from './discover_field'; import { coreMock } from '../../../../../../core/public/mocks'; import { IndexPatternField } from '../../../../../data/public'; -import { FieldSpecExportFmt } from '../../../../../data/common'; jest.mock('../../../kibana_services', () => ({ getServices: () => ({ @@ -63,20 +62,21 @@ function getComponent(selected = false, showDetails = false, useShortDots = fals coreMock.createStart() ); - const field = { - name: 'bytes', - type: 'number', - esTypes: ['long'], - count: 10, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - format: null, - routes: {}, - $$spec: {}, - toSpec: () => (({} as unknown) as FieldSpecExportFmt), - } as IndexPatternField; + const field = new IndexPatternField( + indexPattern, + { + name: 'bytes', + type: 'number', + esTypes: ['long'], + count: 10, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + 'bytes', + () => {} + ); const props = { indexPattern, diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx index e8ed8b80da3bb..58b468762c501 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx @@ -29,12 +29,7 @@ import { IndexPatternAttributes } from '../../../../../data/common'; import { SavedObject } from '../../../../../../core/types'; import { FIELDS_LIMIT_SETTING } from '../../../../common'; import { groupFields } from './lib/group_fields'; -import { - IIndexPatternFieldList, - IndexPatternField, - IndexPattern, - UI_SETTINGS, -} from '../../../../../data/public'; +import { IndexPatternField, IndexPattern, UI_SETTINGS } from '../../../../../data/public'; import { AppState } from '../../angular/discover_state'; import { getDetails } from './lib/get_details'; import { getDefaultFieldFilter, setFieldFilterProp } from './lib/field_filter'; @@ -99,12 +94,12 @@ export function DiscoverSidebar({ }: DiscoverSidebarProps) { const [openFieldMap, setOpenFieldMap] = useState(new Map()); const [showFields, setShowFields] = useState(false); - const [fields, setFields] = useState(null); + const [fields, setFields] = useState(null); const [fieldFilterState, setFieldFilterState] = useState(getDefaultFieldFilter()); const services = useMemo(() => getServices(), []); useEffect(() => { - const newFields = getIndexPatternFieldList(selectedIndexPattern, fieldCounts, services); + const newFields = getIndexPatternFieldList(selectedIndexPattern, fieldCounts); setFields(newFields); }, [selectedIndexPattern, fieldCounts, hits, services]); diff --git a/src/plugins/discover/public/application/components/sidebar/lib/get_index_pattern_field_list.ts b/src/plugins/discover/public/application/components/sidebar/lib/get_index_pattern_field_list.ts index 0fcbe925e0798..751a59d982153 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/get_index_pattern_field_list.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/get_index_pattern_field_list.ts @@ -18,25 +18,23 @@ */ import { difference, map } from 'lodash'; import { IndexPattern, IndexPatternField } from 'src/plugins/data/public'; -import { DiscoverServices } from '../../../../build_services'; export function getIndexPatternFieldList( indexPattern: IndexPattern, - fieldCounts: Record, - { data }: DiscoverServices + fieldCounts: Record ) { - if (!indexPattern || !fieldCounts) return data.indexPatterns.createFieldList(indexPattern); + if (!indexPattern || !fieldCounts) return []; - const fieldSpecs = indexPattern.fields.slice(0); const fieldNamesInDocs = Object.keys(fieldCounts); const fieldNamesInIndexPattern = map(indexPattern.fields, 'name'); + const unknownTypes: IndexPatternField[] = []; difference(fieldNamesInDocs, fieldNamesInIndexPattern).forEach((unknownFieldName) => { - fieldSpecs.push({ + unknownTypes.push({ name: String(unknownFieldName), type: 'unknown', } as IndexPatternField); }); - return data.indexPatterns.createFieldList(indexPattern, fieldSpecs); + return [...indexPattern.fields, ...unknownTypes]; } diff --git a/src/plugins/discover/public/application/components/sidebar/lib/group_fields.tsx b/src/plugins/discover/public/application/components/sidebar/lib/group_fields.tsx index fab4637d87ca7..c6a06618900fd 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/group_fields.tsx +++ b/src/plugins/discover/public/application/components/sidebar/lib/group_fields.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { IIndexPatternFieldList, IndexPatternField } from 'src/plugins/data/public'; +import { IndexPatternField } from 'src/plugins/data/public'; import { FieldFilterState, isFieldFiltered } from './field_filter'; interface GroupedFields { @@ -29,7 +29,7 @@ interface GroupedFields { * group the fields into selected, popular and unpopular, filter by fieldFilterState */ export function groupFields( - fields: IIndexPatternFieldList | null, + fields: IndexPatternField[] | null, columns: string[], popularLimit: number, fieldCounts: Record, diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/create_edit_field/create_edit_field.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/create_edit_field/create_edit_field.tsx index f7b982ef1659e..22bc78ee0538e 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/create_edit_field/create_edit_field.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/create_edit_field/create_edit_field.tsx @@ -21,7 +21,7 @@ import { withRouter, RouteComponentProps } from 'react-router-dom'; import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { IndexPattern } from '../../../../../../plugins/data/public'; +import { IndexPattern, IndexPatternField } from '../../../../../../plugins/data/public'; import { useKibana } from '../../../../../../plugins/kibana_react/public'; import { IndexPatternManagmentContext } from '../../../types'; import { IndexHeader } from '../index_header'; @@ -44,24 +44,21 @@ const newFieldPlaceholder = i18n.translate( export const CreateEditField = withRouter( ({ indexPattern, mode, fieldName, history }: CreateEditFieldProps) => { - const { data, uiSettings, chrome, notifications } = useKibana< + const { uiSettings, chrome, notifications } = useKibana< IndexPatternManagmentContext >().services; - const field = + const spec = mode === 'edit' && fieldName - ? indexPattern.fields.getByName(fieldName) - : data.indexPatterns.createField( - indexPattern, - { - scripted: true, - type: 'number', - }, - false - ); + ? indexPattern.fields.getByName(fieldName)?.spec + : (({ + scripted: true, + type: 'number', + name: undefined, + } as unknown) as IndexPatternField); const url = `/patterns/${indexPattern.id}`; - if (mode === 'edit' && !field) { + if (mode === 'edit' && !spec) { const message = i18n.translate( 'indexPatternManagement.editIndexPattern.scripted.noFieldLabel', { @@ -74,17 +71,17 @@ export const CreateEditField = withRouter( history.push(url); } - const docFieldName = field?.name || newFieldPlaceholder; + const docFieldName = spec?.name || newFieldPlaceholder; chrome.docTitle.change([docFieldName, indexPattern.title]); const redirectAway = () => { history.push( - `${url}#/?_a=(tab:${field?.scripted ? TAB_SCRIPTED_FIELDS : TAB_INDEXED_FIELDS})` + `${url}#/?_a=(tab:${spec?.scripted ? TAB_SCRIPTED_FIELDS : TAB_INDEXED_FIELDS})` ); }; - if (field) { + if (spec) { return ( @@ -97,7 +94,7 @@ export const CreateEditField = withRouter( ({ @@ -41,6 +41,19 @@ const helpers = { getFieldInfo: () => [], }; +const indexPattern = ({ + getNonScriptedFields: () => fields, +} as unknown) as IIndexPattern; + +const mockFieldToIndexPatternField = (spec: Record) => { + return new IndexPatternField( + indexPattern as IndexPattern, + (spec as unknown) as IndexPatternField['spec'], + spec.displayName as string, + () => {} + ); +}; + const fields = [ { name: 'Elastic', @@ -50,11 +63,7 @@ const fields = [ }, { name: 'timestamp', displayName: 'timestamp', type: 'date' }, { name: 'conflictingField', displayName: 'conflictingField', type: 'conflict' }, -] as IndexPatternField[]; - -const indexPattern = ({ - getNonScriptedFields: () => fields, -} as unknown) as IIndexPattern; +].map(mockFieldToIndexPatternField); describe('IndexedFieldsTable', () => { test('should render normally', async () => { diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx index 3344c46c35ac6..90f81a88b3da0 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx @@ -75,7 +75,7 @@ export class IndexedFieldsTable extends Component< (fields && fields.map((field) => { return { - ...field, + ...field.spec, displayName: field.displayName, indexPattern: field.indexPattern, format: getFieldFormat(indexPattern, field.name), diff --git a/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap b/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap index 7a7545580d82a..c22160bc4036d 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap @@ -30,6 +30,7 @@ exports[`FieldEditor should render create new scripted field correctly 1`] = ` "name": "foobar", }, ], + "getFormatterForField": [Function], } } isVisible={false} @@ -273,6 +274,7 @@ exports[`FieldEditor should render edit scripted field correctly 1`] = ` "type": "number", }, ], + "getFormatterForField": [Function], } } isVisible={false} @@ -523,6 +525,7 @@ exports[`FieldEditor should show conflict field warning 1`] = ` "type": "number", }, ], + "getFormatterForField": [Function], } } isVisible={false} @@ -802,6 +805,7 @@ exports[`FieldEditor should show deprecated lang warning 1`] = ` "type": "number", }, ], + "getFormatterForField": [Function], } } isVisible={false} @@ -1133,6 +1137,7 @@ exports[`FieldEditor should show multiple type field warning with a table contai "type": "number", }, ], + "getFormatterForField": [Function], } } isVisible={false} diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/string/string.tsx b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/string/string.tsx index 7a3bb6f5cd398..cdc29e129c457 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/string/string.tsx +++ b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/string/string.tsx @@ -68,7 +68,7 @@ export class StringFormatEditor extends DefaultFormatEditor { + options={(format.type.transformOptions || []).map((option: TransformOptions) => { return { value: option.kind, text: option.text, diff --git a/src/plugins/index_pattern_management/public/components/field_editor/field_editor.test.tsx b/src/plugins/index_pattern_management/public/components/field_editor/field_editor.test.tsx index e0e053d8b606b..ba1f2ff4b665d 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/field_editor.test.tsx +++ b/src/plugins/index_pattern_management/public/components/field_editor/field_editor.test.tsx @@ -26,7 +26,7 @@ import { jest.mock('brace/mode/groovy', () => ({})); -import { FieldEditor } from './field_editor'; +import { FieldEditor, FieldEdiorProps } from './field_editor'; import { mockManagementPlugin } from '../../mocks'; import { createComponentWithContext } from '../test_utils'; @@ -113,15 +113,16 @@ describe('FieldEditor', () => { beforeEach(() => { indexPattern = ({ fields: fields as IIndexPatternFieldList, + getFormatterForField: () => ({ params: () => ({}) }), } as unknown) as IndexPattern; }); it('should render create new scripted field correctly', async () => { - const component = createComponentWithContext( + const component = createComponentWithContext( FieldEditor, { indexPattern, - field: (field as unknown) as IndexPatternField, + spec: (field as unknown) as IndexPatternField, services: { redirectAway: () => {} }, }, mockContext @@ -146,11 +147,11 @@ describe('FieldEditor', () => { return flds[name] as IndexPatternField; }; - const component = createComponentWithContext( + const component = createComponentWithContext( FieldEditor, { indexPattern, - field: (testField as unknown) as IndexPatternField, + spec: (testField as unknown) as IndexPatternField, services: { redirectAway: () => {} }, }, mockContext @@ -176,11 +177,11 @@ describe('FieldEditor', () => { return flds[name] as IndexPatternField; }; - const component = createComponentWithContext( + const component = createComponentWithContext( FieldEditor, { indexPattern, - field: (testField as unknown) as IndexPatternField, + spec: (testField as unknown) as IndexPatternField, services: { redirectAway: () => {} }, }, mockContext @@ -193,11 +194,11 @@ describe('FieldEditor', () => { it('should show conflict field warning', async () => { const testField = { ...field }; - const component = createComponentWithContext( + const component = createComponentWithContext( FieldEditor, { indexPattern, - field: (testField as unknown) as IndexPatternField, + spec: (testField as unknown) as IndexPatternField, services: { redirectAway: () => {} }, }, mockContext @@ -218,11 +219,11 @@ describe('FieldEditor', () => { text: ['index_name_3'], }, }; - const component = createComponentWithContext( + const component = createComponentWithContext( FieldEditor, { indexPattern, - field: (testField as unknown) as IndexPatternField, + spec: (testField as unknown) as IndexPatternField, services: { redirectAway: () => {} }, }, mockContext diff --git a/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx b/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx index 99ef83604239a..d78e1e1014581 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx +++ b/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx @@ -76,7 +76,7 @@ import { executeScript, isScriptValid } from './lib'; import 'brace/mode/groovy'; const getFieldTypeFormatsList = ( - field: IFieldType, + field: IndexPatternField['spec'], defaultFieldFormat: FieldFormatInstanceType, fieldFormats: DataPublicPluginStart['fieldFormats'] ) => { @@ -108,10 +108,6 @@ interface InitialFieldTypeFormat extends FieldTypeFormat { defaultFieldFormat: FieldFormatInstanceType; } -interface FieldClone extends IndexPatternField { - format: any; -} - export interface FieldEditorState { isReady: boolean; isCreating: boolean; @@ -120,7 +116,6 @@ export interface FieldEditorState { fieldTypes: string[]; fieldTypeFormats: FieldTypeFormat[]; existingFieldNames: string[]; - field: FieldClone; fieldFormatId?: string; fieldFormatParams: { [key: string]: unknown }; showScriptingHelp: boolean; @@ -129,11 +124,13 @@ export interface FieldEditorState { hasScriptError: boolean; isSaving: boolean; errors?: string[]; + format: any; + spec: IndexPatternField['spec']; } export interface FieldEdiorProps { indexPattern: IndexPattern; - field: IndexPatternField; + spec: IndexPatternField['spec']; services: { redirectAway: () => void; }; @@ -149,7 +146,7 @@ export class FieldEditor extends PureComponent f.name), - field: { ...field, format: field.format }, fieldFormatId: undefined, fieldFormatParams: {}, showScriptingHelp: false, @@ -167,6 +163,8 @@ export class FieldEditor extends PureComponent f.name === field.name), - isDeprecatedLang: this.deprecatedLangs.includes(field.lang || ''), + isCreating: !indexPattern.fields.find((f) => f.name === spec.name), + isDeprecatedLang: this.deprecatedLangs.includes(spec.lang || ''), errors: [], scriptingLangs, fieldTypes, fieldTypeFormats: getFieldTypeFormatsList( - field, + spec, DefaultFieldFormat as FieldFormatInstanceType, data.fieldFormats ), - fieldFormatId: get(indexPattern, ['fieldFormatMap', field.name, 'type', 'id']), - fieldFormatParams: field.format.params(), + fieldFormatId: get(indexPattern, ['fieldFormatMap', spec.name, 'type', 'id']), + fieldFormatParams: format.params(), }); } onFieldChange = (fieldName: string, value: string | number) => { - const { field } = this.state; - (field as any)[fieldName] = value; + const { spec } = this.state; + (spec as any)[fieldName] = value; this.forceUpdate(); }; onTypeChange = (type: KBN_FIELD_TYPES) => { const { uiSettings, data } = this.context.services; - const { field } = this.state; + const { spec, format } = this.state; const DefaultFieldFormat = data.fieldFormats.getDefaultType(type) as FieldFormatInstanceType; - field.type = type; + spec.type = type; - field.format = new DefaultFieldFormat(null, (key) => uiSettings.get(key)); + spec.format = new DefaultFieldFormat(null, (key) => uiSettings.get(key)); this.setState({ - fieldTypeFormats: getFieldTypeFormatsList(field, DefaultFieldFormat, data.fieldFormats), + fieldTypeFormats: getFieldTypeFormatsList(spec, DefaultFieldFormat, data.fieldFormats), fieldFormatId: DefaultFieldFormat.id, - fieldFormatParams: field.format.params(), + fieldFormatParams: format.params(), }); }; onLangChange = (lang: string) => { - const { field } = this.state; + const { spec } = this.state; const fieldTypes = get(FIELD_TYPES_BY_LANG, lang, DEFAULT_FIELD_TYPES); - field.lang = lang; - field.type = fieldTypes.includes(field.type) ? field.type : fieldTypes[0]; + spec.lang = lang; + spec.type = fieldTypes.includes(spec.type) ? spec.type : fieldTypes[0]; this.setState({ fieldTypes, @@ -244,18 +246,20 @@ export class FieldEditor extends PureComponent { - const { field, fieldTypeFormats } = this.state; + const { spec, fieldTypeFormats } = this.state; const { uiSettings, data } = this.context.services; const FieldFormat = data.fieldFormats.getType( formatId || (fieldTypeFormats[0] as InitialFieldTypeFormat).defaultFieldFormat.id ) as FieldFormatInstanceType; - field.format = new FieldFormat(params, (key) => uiSettings.get(key)); + const newFormat = new FieldFormat(params, (key) => uiSettings.get(key)); + spec.format = newFormat; this.setState({ fieldFormatId: FieldFormat.id, - fieldFormatParams: field.format.params(), + fieldFormatParams: newFormat.params(), + format: newFormat, }); }; @@ -271,13 +275,13 @@ export class FieldEditor extends PureComponent ), - fieldName: {field.name}, + fieldName: {spec.name}, }} /> @@ -316,7 +320,7 @@ export class FieldEditor extends PureComponent {field.lang}, + language: {spec.lang}, painlessLink: ( { return { value: lang, text: lang }; })} @@ -388,15 +392,15 @@ export class FieldEditor extends PureComponent { return { value: type, text: type }; })} @@ -414,8 +418,8 @@ export class FieldEditor extends PureComponent ({ + const items = Object.entries(spec.conflictDescriptions).map(([type, indices]) => ({ type, indices: Array.isArray(indices) ? indices.join(', ') : 'Index names unavailable', })); @@ -466,7 +470,7 @@ export class FieldEditor extends PureComponent { - return { value: format.id || '', text: format.title }; + options={fieldTypeFormats.map((fmt) => { + return { value: fmt.id || '', text: fmt.title }; })} data-test-subj="editorSelectedFormatId" onChange={(e) => { @@ -507,8 +511,8 @@ export class FieldEditor extends PureComponent {fieldFormatId ? ( { this.onFieldChange('count', e.target.value ? Number(e.target.value) : ''); @@ -550,8 +554,8 @@ export class FieldEditor extends PureComponent ); - return field.scripted ? ( + return spec.scripted ? ( { - const { field } = this.state; + const { spec } = this.state; return this.state.showDeleteModal ? ( { @@ -674,7 +678,7 @@ export class FieldEditor extends PureComponent - {!isCreating && field.scripted ? ( + {!isCreating && spec.scripted ? ( @@ -729,9 +733,9 @@ export class FieldEditor extends PureComponent { - const { scriptingLangs, field, showScriptingHelp } = this.state; + const { scriptingLangs, spec, showScriptingHelp } = this.state; - if (!field.scripted) { + if (!spec.scripted) { return; } @@ -743,9 +747,9 @@ export class FieldEditor extends PureComponent @@ -755,14 +759,14 @@ export class FieldEditor extends PureComponent { const { redirectAway } = this.props.services; const { indexPattern } = this.props; - const { field } = this.state; - const remove = indexPattern.removeScriptedField(field); + const { spec } = this.state; + const remove = indexPattern.removeScriptedField(spec.name); if (remove) { remove.then(() => { const message = i18n.translate('indexPatternManagement.deleteField.deletedHeader', { defaultMessage: "Deleted '{fieldName}'", - values: { fieldName: field.name }, + values: { fieldName: spec.name }, }); this.context.services.notifications.toasts.addSuccess(message); redirectAway(); @@ -773,7 +777,7 @@ export class FieldEditor extends PureComponent { - const field = this.state.field; + const field = this.state.spec; const { indexPattern } = this.props; const { fieldFormatId } = this.state; @@ -802,10 +806,10 @@ export class FieldEditor extends PureComponent f.name === field.name); - let oldField: IFieldType | undefined; + let oldField: IndexPatternField['spec']; if (index > -1) { - oldField = indexPattern.fields.getByName(field.name); + oldField = indexPattern.fields.getByName(field.name)!.spec; indexPattern.fields.update(field); } else { indexPattern.fields.add(field); @@ -837,14 +841,14 @@ export class FieldEditor extends PureComponent @@ -868,7 +872,7 @@ export class FieldEditor extends PureComponent )} diff --git a/src/plugins/index_pattern_management/public/components/test_utils.tsx b/src/plugins/index_pattern_management/public/components/test_utils.tsx index 938547cca04ab..6aa71785d779c 100644 --- a/src/plugins/index_pattern_management/public/components/test_utils.tsx +++ b/src/plugins/index_pattern_management/public/components/test_utils.tsx @@ -23,9 +23,9 @@ import { shallow } from 'enzyme'; // since the 'shallow' from 'enzyme' doesn't support context API for React 16 and above (https://github.com/facebook/react/pull/14329) // we use this workaround where define legacy contextTypes for react class component -export function createComponentWithContext( +export function createComponentWithContext>( MyComponent: React.ComponentClass, - props: Record, + props: Props, mockedContext: Record ) { MyComponent.contextTypes = { diff --git a/src/test_utils/public/stub_index_pattern.js b/src/test_utils/public/stub_index_pattern.js index 5a81139157cef..f7b65930b683d 100644 --- a/src/test_utils/public/stub_index_pattern.js +++ b/src/test_utils/public/stub_index_pattern.js @@ -22,12 +22,7 @@ import sinon from 'sinon'; // because it is one of the few places that we need to access the IndexPattern class itself, rather // than just the type. Doing this as a temporary measure; it will be left behind when migrating to NP. -import { - IndexPattern, - indexPatterns, - KBN_FIELD_TYPES, - getIndexPatternFieldListCreator, -} from '../../plugins/data/public'; +import { IndexPattern, indexPatterns, KBN_FIELD_TYPES, FieldList } from '../../plugins/data/public'; import { setFieldFormats } from '../../plugins/data/public/services'; @@ -42,16 +37,6 @@ import { getFieldFormatsRegistry } from './stub_field_formats'; export default function StubIndexPattern(pattern, getConfig, timeField, fields, core) { const registeredFieldFormats = getFieldFormatsRegistry(core); - const createFieldList = getIndexPatternFieldListCreator({ - fieldFormats: { - getDefaultInstance: () => ({ - convert: (val) => String(val), - }), - }, - toastNotifications: { - addDanger: () => {}, - }, - }); this.id = pattern; this.title = pattern; @@ -74,9 +59,12 @@ export default function StubIndexPattern(pattern, getConfig, timeField, fields, ); this.fieldsFetcher = { apiClient: { baseUrl: '' } }; this.formatField = this.formatHit.formatField; + this.getFormatterForField = () => ({ + convert: () => '', + }); this._reindexFields = function () { - this.fields = createFieldList(this, this.fields || fields, false); + this.fields = new FieldList(this, this.fields || fields, false); }; this.stubSetFieldFormat = function (fieldName, id, params) { diff --git a/test/functional/apps/management/_index_pattern_popularity.js b/test/functional/apps/management/_index_pattern_popularity.js index 530b8e1111a0c..e2fcf50ef2c12 100644 --- a/test/functional/apps/management/_index_pattern_popularity.js +++ b/test/functional/apps/management/_index_pattern_popularity.js @@ -60,7 +60,7 @@ export default function ({ getService, getPageObjects }) { // check that it is 0 (previous increase was cancelled const popularity = await PageObjects.settings.getPopularity(); log.debug('popularity = ' + popularity); - expect(popularity).to.be('0'); + expect(popularity).to.be(''); }); it('can be saved', async function () { diff --git a/test/functional/apps/management/_scripted_fields.js b/test/functional/apps/management/_scripted_fields.js index 2727313ab2336..116d1eac90cea 100644 --- a/test/functional/apps/management/_scripted_fields.js +++ b/test/functional/apps/management/_scripted_fields.js @@ -36,6 +36,7 @@ import expect from '@kbn/expect'; export default function ({ getService, getPageObjects }) { + const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const log = getService('log'); const browser = getService('browser'); @@ -57,9 +58,9 @@ export default function ({ getService, getPageObjects }) { before(async function () { await browser.setWindowSize(1200, 800); + await esArchiver.load('discover'); // delete .kibana index and then wait for Kibana to re-create it await kibanaServer.uiSettings.replace({}); - await PageObjects.settings.createIndexPattern(); await kibanaServer.uiSettings.update({}); }); diff --git a/test/functional/apps/management/_scripted_fields_filter.js b/test/functional/apps/management/_scripted_fields_filter.js index 2eb53508c2846..2d59d2ba57d5b 100644 --- a/test/functional/apps/management/_scripted_fields_filter.js +++ b/test/functional/apps/management/_scripted_fields_filter.js @@ -27,7 +27,9 @@ export default function ({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); const PageObjects = getPageObjects(['settings']); - describe('filter scripted fields', function describeIndexTests() { + // this functionality is no longer functional as of 7.0 but still needs cleanup + // https://github.com/elastic/kibana/issues/74118 + describe.skip('filter scripted fields', function describeIndexTests() { before(async function () { // delete .kibana index and then wait for Kibana to re-create it await browser.setWindowSize(1200, 800); From 244ed04b249a3c0559cf0274ac07481596dc9bb5 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 10 Aug 2020 15:02:35 +0200 Subject: [PATCH 17/19] [Uptime] Fix full reloads while navigating to alert/ml (#73796) Co-authored-by: Elastic Machine --- .../plugins/uptime/public/apps/uptime_app.tsx | 15 ++- .../__tests__/link_events.test.ts | 102 ++++++++++++++++++ .../__tests__/link_for_eui.test.tsx | 77 +++++++++++++ .../common/react_router_helpers/index.ts | 12 +++ .../react_router_helpers/link_events.ts | 31 ++++++ .../react_router_helpers/link_for_eui.tsx | 74 +++++++++++++ .../ml_integerations.test.tsx.snap | 5 +- .../__snapshots__/ml_manage_job.test.tsx.snap | 5 +- .../components/monitor/ml/manage_ml_job.tsx | 8 +- .../components/monitor/ml/translations.tsx | 8 ++ .../uptime/public/lib/__mocks__/index.ts | 7 ++ .../__mocks__/react_router_history.mock.ts | 25 +++++ .../uptime/public/pages/certificates.tsx | 8 +- .../uptime/public/pages/page_header.tsx | 9 +- .../plugins/uptime/public/pages/settings.tsx | 10 +- .../__test__/get_histogram_interval.test.ts | 4 +- 16 files changed, 371 insertions(+), 29 deletions(-) create mode 100644 x-pack/plugins/uptime/public/components/common/react_router_helpers/__tests__/link_events.test.ts create mode 100644 x-pack/plugins/uptime/public/components/common/react_router_helpers/__tests__/link_for_eui.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/common/react_router_helpers/index.ts create mode 100644 x-pack/plugins/uptime/public/components/common/react_router_helpers/link_events.ts create mode 100644 x-pack/plugins/uptime/public/components/common/react_router_helpers/link_for_eui.tsx create mode 100644 x-pack/plugins/uptime/public/lib/__mocks__/index.ts create mode 100644 x-pack/plugins/uptime/public/lib/__mocks__/react_router_history.mock.ts diff --git a/x-pack/plugins/uptime/public/apps/uptime_app.tsx b/x-pack/plugins/uptime/public/apps/uptime_app.tsx index 41370f9fff492..1dc34b44b7c64 100644 --- a/x-pack/plugins/uptime/public/apps/uptime_app.tsx +++ b/x-pack/plugins/uptime/public/apps/uptime_app.tsx @@ -10,7 +10,10 @@ import React, { useEffect } from 'react'; import { Provider as ReduxProvider } from 'react-redux'; import { BrowserRouter as Router } from 'react-router-dom'; import { I18nStart, ChromeBreadcrumb, CoreStart } from 'kibana/public'; -import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; +import { + KibanaContextProvider, + RedirectAppLinks, +} from '../../../../../src/plugins/kibana_react/public'; import { ClientPluginsSetup, ClientPluginsStart } from './plugin'; import { UMUpdateBadge } from '../lib/lib'; import { @@ -103,10 +106,12 @@ const Application = (props: UptimeAppProps) => { -
- - -
+ +
+ + +
+
diff --git a/x-pack/plugins/uptime/public/components/common/react_router_helpers/__tests__/link_events.test.ts b/x-pack/plugins/uptime/public/components/common/react_router_helpers/__tests__/link_events.test.ts new file mode 100644 index 0000000000000..3e857c7c20904 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/common/react_router_helpers/__tests__/link_events.test.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 { letBrowserHandleEvent } from '../index'; + +describe('letBrowserHandleEvent', () => { + const event = { + defaultPrevented: false, + metaKey: false, + altKey: false, + ctrlKey: false, + shiftKey: false, + button: 0, + target: { + getAttribute: () => '_self', + }, + } as any; + + describe('the browser should handle the link when', () => { + it('default is prevented', () => { + expect(letBrowserHandleEvent({ ...event, defaultPrevented: true })).toBe(true); + }); + + it('is modified with metaKey', () => { + expect(letBrowserHandleEvent({ ...event, metaKey: true })).toBe(true); + }); + + it('is modified with altKey', () => { + expect(letBrowserHandleEvent({ ...event, altKey: true })).toBe(true); + }); + + it('is modified with ctrlKey', () => { + expect(letBrowserHandleEvent({ ...event, ctrlKey: true })).toBe(true); + }); + + it('is modified with shiftKey', () => { + expect(letBrowserHandleEvent({ ...event, shiftKey: true })).toBe(true); + }); + + it('it is not a left click event', () => { + expect(letBrowserHandleEvent({ ...event, button: 2 })).toBe(true); + }); + + it('the target is anything value other than _self', () => { + expect( + letBrowserHandleEvent({ + ...event, + target: targetValue('_blank'), + }) + ).toBe(true); + }); + }); + + describe('the browser should NOT handle the link when', () => { + it('default is not prevented', () => { + expect(letBrowserHandleEvent({ ...event, defaultPrevented: false })).toBe(false); + }); + + it('is not modified', () => { + expect( + letBrowserHandleEvent({ + ...event, + metaKey: false, + altKey: false, + ctrlKey: false, + shiftKey: false, + }) + ).toBe(false); + }); + + it('it is a left click event', () => { + expect(letBrowserHandleEvent({ ...event, button: 0 })).toBe(false); + }); + + it('the target is a value of _self', () => { + expect( + letBrowserHandleEvent({ + ...event, + target: targetValue('_self'), + }) + ).toBe(false); + }); + + it('the target has no value', () => { + expect( + letBrowserHandleEvent({ + ...event, + target: targetValue(null), + }) + ).toBe(false); + }); + }); +}); + +const targetValue = (value: string | null) => { + return { + getAttribute: () => value, + }; +}; diff --git a/x-pack/plugins/uptime/public/components/common/react_router_helpers/__tests__/link_for_eui.test.tsx b/x-pack/plugins/uptime/public/components/common/react_router_helpers/__tests__/link_for_eui.test.tsx new file mode 100644 index 0000000000000..4a681f6fa60bf --- /dev/null +++ b/x-pack/plugins/uptime/public/components/common/react_router_helpers/__tests__/link_for_eui.test.tsx @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow, mount } from 'enzyme'; +import { EuiLink, EuiButton } from '@elastic/eui'; + +import '../../../../lib/__mocks__/react_router_history.mock'; + +import { ReactRouterEuiLink, ReactRouterEuiButton } from '../link_for_eui'; +import { mockHistory } from '../../../../lib/__mocks__'; + +describe('EUI & React Router Component Helpers', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(EuiLink)).toHaveLength(1); + }); + + it('renders an EuiButton', () => { + const wrapper = shallow(); + + expect(wrapper.find(EuiButton)).toHaveLength(1); + }); + + it('passes down all ...rest props', () => { + const wrapper = shallow(); + const link = wrapper.find(EuiLink); + + expect(link.prop('external')).toEqual(true); + expect(link.prop('data-test-subj')).toEqual('foo'); + }); + + it('renders with the correct href and onClick props', () => { + const wrapper = mount(); + const link = wrapper.find(EuiLink); + + expect(link.prop('onClick')).toBeInstanceOf(Function); + expect(link.prop('href')).toEqual('/enterprise_search/foo/bar'); + expect(mockHistory.createHref).toHaveBeenCalled(); + }); + + describe('onClick', () => { + it('prevents default navigation and uses React Router history', () => { + const wrapper = mount(); + + const simulatedEvent = { + button: 0, + target: { getAttribute: () => '_self' }, + preventDefault: jest.fn(), + }; + wrapper.find(EuiLink).simulate('click', simulatedEvent); + + expect(simulatedEvent.preventDefault).toHaveBeenCalled(); + expect(mockHistory.push).toHaveBeenCalled(); + }); + + it('does not prevent default browser behavior on new tab/window clicks', () => { + const wrapper = mount(); + + const simulatedEvent = { + shiftKey: true, + target: { getAttribute: () => '_blank' }, + }; + wrapper.find(EuiLink).simulate('click', simulatedEvent); + + expect(mockHistory.push).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/common/react_router_helpers/index.ts b/x-pack/plugins/uptime/public/components/common/react_router_helpers/index.ts new file mode 100644 index 0000000000000..a1885eaee4cbe --- /dev/null +++ b/x-pack/plugins/uptime/public/components/common/react_router_helpers/index.ts @@ -0,0 +1,12 @@ +/* + * 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 { letBrowserHandleEvent } from './link_events'; +export { + ReactRouterEuiLink, + ReactRouterEuiButton, + ReactRouterEuiButtonEmpty, +} from './link_for_eui'; diff --git a/x-pack/plugins/uptime/public/components/common/react_router_helpers/link_events.ts b/x-pack/plugins/uptime/public/components/common/react_router_helpers/link_events.ts new file mode 100644 index 0000000000000..93da2ab71d952 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/common/react_router_helpers/link_events.ts @@ -0,0 +1,31 @@ +/* + * 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 { MouseEvent } from 'react'; + +/** + * Helper functions for determining which events we should + * let browsers handle natively, e.g. new tabs/windows + */ + +type THandleEvent = (event: MouseEvent) => boolean; + +export const letBrowserHandleEvent: THandleEvent = (event) => + event.defaultPrevented || + isModifiedEvent(event) || + !isLeftClickEvent(event) || + isTargetBlank(event); + +const isModifiedEvent: THandleEvent = (event) => + !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey); + +const isLeftClickEvent: THandleEvent = (event) => event.button === 0; + +const isTargetBlank: THandleEvent = (event) => { + const element = event.target as HTMLElement; + const target = element.getAttribute('target'); + return !!target && target !== '_self'; +}; diff --git a/x-pack/plugins/uptime/public/components/common/react_router_helpers/link_for_eui.tsx b/x-pack/plugins/uptime/public/components/common/react_router_helpers/link_for_eui.tsx new file mode 100644 index 0000000000000..7adc8be4533bc --- /dev/null +++ b/x-pack/plugins/uptime/public/components/common/react_router_helpers/link_for_eui.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 from 'react'; +import { useHistory } from 'react-router-dom'; +import { + EuiLink, + EuiButton, + EuiButtonProps, + EuiButtonEmptyProps, + EuiLinkAnchorProps, + EuiButtonEmpty, +} from '@elastic/eui'; + +import { letBrowserHandleEvent } from './link_events'; + +/** + * Generates either an EuiLink or EuiButton with a React-Router-ified link + * + * Based off of EUI's recommendations for handling React Router: + * https://github.com/elastic/eui/blob/master/wiki/react-router.md#react-router-51 + */ + +interface IEuiReactRouterProps { + to: string; +} + +export const ReactRouterHelperForEui: React.FC = ({ to, children }) => { + const history = useHistory(); + + const onClick = (event: React.MouseEvent) => { + if (letBrowserHandleEvent(event)) return; + + // Prevent regular link behavior, which causes a browser refresh. + event.preventDefault(); + + // Push the route to the history. + history.push(to); + }; + + // Generate the correct link href (with basename etc. accounted for) + const href = history.createHref({ pathname: to }); + + const reactRouterProps = { href, onClick }; + return React.cloneElement(children as React.ReactElement, reactRouterProps); +}; + +type TEuiReactRouterLinkProps = EuiLinkAnchorProps & IEuiReactRouterProps; +type TEuiReactRouterButtonProps = EuiButtonProps & IEuiReactRouterProps; +type TEuiReactRouterButtonEmptyProps = EuiButtonEmptyProps & IEuiReactRouterProps; + +export const ReactRouterEuiLink: React.FC = ({ to, ...rest }) => ( + + + +); + +export const ReactRouterEuiButton: React.FC = ({ to, ...rest }) => ( + + + +); + +export const ReactRouterEuiButtonEmpty: React.FC = ({ + to, + ...rest +}) => ( + + + +); diff --git a/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_integerations.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_integerations.test.tsx.snap index 15f5c03512bf1..e7ad86f72dab6 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_integerations.test.tsx.snap +++ b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_integerations.test.tsx.snap @@ -8,6 +8,7 @@ exports[`ML Integrations renders without errors 1`] = ` class="euiPopover__anchor" >
diff --git a/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_manage_job.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_manage_job.test.tsx.snap index fabe94763e07d..cc3417e09987e 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_manage_job.test.tsx.snap +++ b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_manage_job.test.tsx.snap @@ -8,6 +8,7 @@ exports[`Manage ML Job renders without errors 1`] = ` class="euiPopover__anchor" > diff --git a/x-pack/plugins/uptime/public/components/monitor/ml/manage_ml_job.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/manage_ml_job.tsx index 7a2899558891d..f4382b37b3d30 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ml/manage_ml_job.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ml/manage_ml_job.tsx @@ -54,6 +54,10 @@ export const ManageMLJobComponent = ({ hasMLJob, onEnableJob, onJobDelete }: Pro const deleteAnomalyAlert = () => dispatch(deleteAlertAction.get({ alertId: anomalyAlert?.id as string })); + const showLoading = isMLJobCreating || isMLJobLoading; + + const btnText = hasMLJob ? labels.ANOMALY_DETECTION : labels.ENABLE_ANOMALY_DETECTION; + const button = ( - {hasMLJob ? labels.ANOMALY_DETECTION : labels.ENABLE_ANOMALY_DETECTION} + {showLoading ? '' : btnText} ); @@ -79,7 +84,6 @@ export const ManageMLJobComponent = ({ hasMLJob, onEnableJob, onJobDelete }: Pro monitorId, dateRange: { from: dateRangeStart, to: dateRangeEnd }, }), - target: '_blank', }, { name: anomalyAlert ? labels.DISABLE_ANOMALY_ALERT : labels.ENABLE_ANOMALY_ALERT, diff --git a/x-pack/plugins/uptime/public/components/monitor/ml/translations.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/translations.tsx index 90ebdf10a73f5..dfc912e6be9ee 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ml/translations.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ml/translations.tsx @@ -162,3 +162,11 @@ export const START_TRAIL_DESC = i18n.translate( 'In order to access duration anomaly detection, you have to be subscribed to an Elastic Platinum license.', } ); + +export const ENABLE_MANAGE_JOB = i18n.translate( + 'xpack.uptime.ml.enableAnomalyDetectionPanel.enable_or_manage_job', + { + defaultMessage: + 'You can enable anomaly detection job or if job is already there you can manage the job or alert.', + } +); diff --git a/x-pack/plugins/uptime/public/lib/__mocks__/index.ts b/x-pack/plugins/uptime/public/lib/__mocks__/index.ts new file mode 100644 index 0000000000000..45ef5787927e1 --- /dev/null +++ b/x-pack/plugins/uptime/public/lib/__mocks__/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 { mockHistory } from './react_router_history.mock'; diff --git a/x-pack/plugins/uptime/public/lib/__mocks__/react_router_history.mock.ts b/x-pack/plugins/uptime/public/lib/__mocks__/react_router_history.mock.ts new file mode 100644 index 0000000000000..fd422465d87f1 --- /dev/null +++ b/x-pack/plugins/uptime/public/lib/__mocks__/react_router_history.mock.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. + */ + +/** + * NOTE: This variable name MUST start with 'mock*' in order for + * Jest to accept its use within a jest.mock() + */ +export const mockHistory = { + createHref: jest.fn(({ pathname }) => `/enterprise_search${pathname}`), + push: jest.fn(), + location: { + pathname: '/current-path', + }, +}; + +jest.mock('react-router-dom', () => ({ + useHistory: jest.fn(() => mockHistory), +})); + +/** + * For example usage, @see public/applications/shared/react_router_helpers/eui_link.test.tsx + */ diff --git a/x-pack/plugins/uptime/public/pages/certificates.tsx b/x-pack/plugins/uptime/public/pages/certificates.tsx index e46d228c6d21f..a524ce6ba9b71 100644 --- a/x-pack/plugins/uptime/public/pages/certificates.tsx +++ b/x-pack/plugins/uptime/public/pages/certificates.tsx @@ -29,6 +29,7 @@ import { certificatesSelector, getCertificatesAction } from '../state/certificat import { CertificateList, CertificateSearch, CertSort } from '../components/certificates'; import { ToggleAlertFlyoutButton } from '../components/overview/alerts/alerts_containers'; import { CLIENT_ALERT_TYPES } from '../../common/constants/alerts'; +import { ReactRouterEuiButtonEmpty } from '../components/common/react_router_helpers'; const DEFAULT_PAGE_SIZE = 10; const LOCAL_STORAGE_KEY = 'xpack.uptime.certList.pageSize'; @@ -79,15 +80,16 @@ export const CertificatesPage: React.FC = () => { <> - {labels.RETURN_TO_OVERVIEW} - + diff --git a/x-pack/plugins/uptime/public/pages/page_header.tsx b/x-pack/plugins/uptime/public/pages/page_header.tsx index 16279a63b5f40..325d82696d47c 100644 --- a/x-pack/plugins/uptime/public/pages/page_header.tsx +++ b/x-pack/plugins/uptime/public/pages/page_header.tsx @@ -7,12 +7,12 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiSpacer, EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useHistory } from 'react-router-dom'; import styled from 'styled-components'; import { UptimeDatePicker } from '../components/common/uptime_date_picker'; import { SETTINGS_ROUTE } from '../../common/constants'; import { ToggleAlertFlyoutButton } from '../components/overview/alerts/alerts_containers'; import { useKibana } from '../../../../../src/plugins/kibana_react/public'; +import { ReactRouterEuiButtonEmpty } from '../components/common/react_router_helpers'; interface PageHeaderProps { headingText: string | JSX.Element; @@ -58,7 +58,6 @@ export const PageHeader = React.memo( ) : null; const kibana = useKibana(); - const history = useHistory(); const extraLinkComponents = !extraLinks ? null : ( @@ -66,13 +65,13 @@ export const PageHeader = React.memo( - {SETTINGS_LINK_TEXT} - + { ); - const history = useHistory(); - return ( <> - {Translations.settings.returnToOverviewLinkLabel} - + diff --git a/x-pack/plugins/uptime/server/lib/helper/__test__/get_histogram_interval.test.ts b/x-pack/plugins/uptime/server/lib/helper/__test__/get_histogram_interval.test.ts index bddca1b863ce4..09b857f37e1df 100644 --- a/x-pack/plugins/uptime/server/lib/helper/__test__/get_histogram_interval.test.ts +++ b/x-pack/plugins/uptime/server/lib/helper/__test__/get_histogram_interval.test.ts @@ -10,11 +10,11 @@ import { assertCloseTo } from '../assert_close_to'; describe('getHistogramInterval', () => { it('specifies the interval necessary to divide a given timespan into equal buckets, rounded to the nearest integer, expressed in ms', () => { const interval = getHistogramInterval('now-15m', 'now', 10); - assertCloseTo(interval, 90000, 10); + assertCloseTo(interval, 90000, 20); }); it('will supply a default constant value for bucketCount when none is provided', () => { const interval = getHistogramInterval('now-15m', 'now'); - assertCloseTo(interval, 36000, 10); + assertCloseTo(interval, 36000, 20); }); }); From c1b55d57399ded6dbca4415db9077dda7cfccbc9 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 10 Aug 2020 15:06:58 +0200 Subject: [PATCH 18/19] [Lens] Clear out all attribute properties before updating (#74483) --- .../persistence/saved_object_store.test.ts | 47 +++++++++++++------ .../public/persistence/saved_object_store.ts | 44 +++++++++-------- 2 files changed, 58 insertions(+), 33 deletions(-) diff --git a/x-pack/plugins/lens/public/persistence/saved_object_store.test.ts b/x-pack/plugins/lens/public/persistence/saved_object_store.test.ts index f7caac6549389..f8f8d889233a7 100644 --- a/x-pack/plugins/lens/public/persistence/saved_object_store.test.ts +++ b/x-pack/plugins/lens/public/persistence/saved_object_store.test.ts @@ -4,19 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ +import { SavedObjectsClientContract, SavedObjectsBulkUpdateObject } from 'kibana/public'; import { SavedObjectIndexStore } from './saved_object_store'; describe('LensStore', () => { function testStore(testId?: string) { const client = { create: jest.fn(() => Promise.resolve({ id: testId || 'testid' })), - update: jest.fn((_type: string, id: string) => Promise.resolve({ id })), + bulkUpdate: jest.fn(([{ id }]: SavedObjectsBulkUpdateObject[]) => + Promise.resolve({ savedObjects: [{ id }, { id }] }) + ), get: jest.fn(), }; return { client, - store: new SavedObjectIndexStore(client), + store: new SavedObjectIndexStore((client as unknown) as SavedObjectsClientContract), }; } @@ -108,19 +111,35 @@ describe('LensStore', () => { }, }); - expect(client.update).toHaveBeenCalledTimes(1); - expect(client.update).toHaveBeenCalledWith('lens', 'Gandalf', { - title: 'Even the very wise cannot see all ends.', - visualizationType: 'line', - expression: '', - state: { - datasourceMetaData: { filterableIndexPatterns: [] }, - datasourceStates: { indexpattern: { type: 'index_pattern', indexPattern: 'lotr' } }, - visualization: { gear: ['staff', 'pointy hat'] }, - query: { query: '', language: 'lucene' }, - filters: [], + expect(client.bulkUpdate).toHaveBeenCalledTimes(1); + expect(client.bulkUpdate).toHaveBeenCalledWith([ + { + type: 'lens', + id: 'Gandalf', + attributes: { + title: null, + visualizationType: null, + expression: null, + state: null, + }, }, - }); + { + type: 'lens', + id: 'Gandalf', + attributes: { + title: 'Even the very wise cannot see all ends.', + visualizationType: 'line', + expression: '', + state: { + datasourceMetaData: { filterableIndexPatterns: [] }, + datasourceStates: { indexpattern: { type: 'index_pattern', indexPattern: 'lotr' } }, + visualization: { gear: ['staff', 'pointy hat'] }, + query: { query: '', language: 'lucene' }, + filters: [], + }, + }, + }, + ]); }); }); diff --git a/x-pack/plugins/lens/public/persistence/saved_object_store.ts b/x-pack/plugins/lens/public/persistence/saved_object_store.ts index af90634874fb1..59ead53956a8d 100644 --- a/x-pack/plugins/lens/public/persistence/saved_object_store.ts +++ b/x-pack/plugins/lens/public/persistence/saved_object_store.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectAttributes } from 'kibana/server'; +import { SavedObjectAttributes, SavedObjectsClientContract } from 'kibana/public'; import { Query, Filter } from '../../../../../src/plugins/data/public'; export interface Document { @@ -27,20 +27,6 @@ export interface Document { export const DOC_TYPE = 'lens'; -interface SavedObjectClient { - create: (type: string, object: SavedObjectAttributes) => Promise<{ id: string }>; - update: (type: string, id: string, object: SavedObjectAttributes) => Promise<{ id: string }>; - get: ( - type: string, - id: string - ) => Promise<{ - id: string; - type: string; - attributes: SavedObjectAttributes; - error?: { statusCode: number; message: string }; - }>; -} - export interface DocumentSaver { save: (vis: Document) => Promise<{ id: string }>; } @@ -52,9 +38,9 @@ export interface DocumentLoader { export type SavedObjectStore = DocumentLoader & DocumentSaver; export class SavedObjectIndexStore implements SavedObjectStore { - private client: SavedObjectClient; + private client: SavedObjectsClientContract; - constructor(client: SavedObjectClient) { + constructor(client: SavedObjectsClientContract) { this.client = client; } @@ -63,13 +49,33 @@ export class SavedObjectIndexStore implements SavedObjectStore { // TODO: SavedObjectAttributes should support this kind of object, // remove this workaround when SavedObjectAttributes is updated. const attributes = (rest as unknown) as SavedObjectAttributes; + const result = await (id - ? this.client.update(DOC_TYPE, id, attributes) + ? this.safeUpdate(id, attributes) : this.client.create(DOC_TYPE, attributes)); return { ...vis, id: result.id }; } + // As Lens is using an object to store its attributes, using the update API + // will merge the new attribute object with the old one, not overwriting deleted + // keys. As Lens is using objects as maps in various places, this is a problem because + // deleted subtrees make it back into the object after a load. + // This function fixes this by doing two updates - one to empty out the document setting + // every key to null, and a second one to load the new content. + private async safeUpdate(id: string, attributes: SavedObjectAttributes) { + const resetAttributes: SavedObjectAttributes = {}; + Object.keys(attributes).forEach((key) => { + resetAttributes[key] = null; + }); + return ( + await this.client.bulkUpdate([ + { type: DOC_TYPE, id, attributes: resetAttributes }, + { type: DOC_TYPE, id, attributes }, + ]) + ).savedObjects[1]; + } + async load(id: string): Promise { const { type, attributes, error } = await this.client.get(DOC_TYPE, id); @@ -78,7 +84,7 @@ export class SavedObjectIndexStore implements SavedObjectStore { } return { - ...attributes, + ...(attributes as SavedObjectAttributes), id, type, } as Document; From 23adb256bb4799f61e8ae9914e2ee8231e7b7598 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Mon, 10 Aug 2020 16:29:29 +0300 Subject: [PATCH 19/19] [i18n] revert reverted changes (#74633) Co-authored-by: Elastic Machine --- src/dev/i18n/integrate_locale_files.test.ts | 3 ++- src/dev/i18n/integrate_locale_files.ts | 21 +++++++++++++++++++- src/dev/i18n/tasks/check_compatibility.ts | 4 +++- src/dev/i18n/utils.js | 22 +++++++++++++++++++++ src/dev/run_i18n_check.ts | 5 ++++- src/dev/run_i18n_integrate.ts | 7 ++++--- 6 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/dev/i18n/integrate_locale_files.test.ts b/src/dev/i18n/integrate_locale_files.test.ts index 7ff1d87f1bc55..3bd3dc61c044f 100644 --- a/src/dev/i18n/integrate_locale_files.test.ts +++ b/src/dev/i18n/integrate_locale_files.test.ts @@ -21,7 +21,7 @@ import { mockMakeDirAsync, mockWriteFileAsync } from './integrate_locale_files.t import path from 'path'; import { integrateLocaleFiles, verifyMessages } from './integrate_locale_files'; -// @ts-ignore +// @ts-expect-error import { normalizePath } from './utils'; const localePath = path.resolve(__dirname, '__fixtures__', 'integrate_locale_files', 'fr.json'); @@ -36,6 +36,7 @@ const defaultIntegrateOptions = { sourceFileName: localePath, dryRun: false, ignoreIncompatible: false, + ignoreMalformed: false, ignoreMissing: false, ignoreUnused: false, config: { diff --git a/src/dev/i18n/integrate_locale_files.ts b/src/dev/i18n/integrate_locale_files.ts index d8ccccca15559..f9cd6dd1971c7 100644 --- a/src/dev/i18n/integrate_locale_files.ts +++ b/src/dev/i18n/integrate_locale_files.ts @@ -31,7 +31,8 @@ import { normalizePath, readFileAsync, writeFileAsync, - // @ts-ignore + verifyICUMessage, + // @ts-expect-error } from './utils'; import { I18nConfig } from './config'; @@ -41,6 +42,7 @@ export interface IntegrateOptions { sourceFileName: string; targetFileName?: string; dryRun: boolean; + ignoreMalformed: boolean; ignoreIncompatible: boolean; ignoreUnused: boolean; ignoreMissing: boolean; @@ -105,6 +107,23 @@ export function verifyMessages( } } + for (const messageId of localizedMessagesIds) { + const defaultMessage = defaultMessagesMap.get(messageId); + if (defaultMessage) { + try { + const message = localizedMessagesMap.get(messageId)!; + verifyICUMessage(message); + } catch (err) { + if (options.ignoreMalformed) { + localizedMessagesMap.delete(messageId); + options.log.warning(`Malformed translation ignored (${messageId}): ${err}`); + } else { + errorMessage += `\nMalformed translation (${messageId}): ${err}\n`; + } + } + } + } + if (errorMessage) { throw createFailError(errorMessage); } diff --git a/src/dev/i18n/tasks/check_compatibility.ts b/src/dev/i18n/tasks/check_compatibility.ts index 5900bf5aff252..afaf3cd875a8a 100644 --- a/src/dev/i18n/tasks/check_compatibility.ts +++ b/src/dev/i18n/tasks/check_compatibility.ts @@ -22,13 +22,14 @@ import { integrateLocaleFiles, I18nConfig } from '..'; export interface I18nFlags { fix: boolean; + ignoreMalformed: boolean; ignoreIncompatible: boolean; ignoreUnused: boolean; ignoreMissing: boolean; } export function checkCompatibility(config: I18nConfig, flags: I18nFlags, log: ToolingLog) { - const { fix, ignoreIncompatible, ignoreUnused, ignoreMissing } = flags; + const { fix, ignoreIncompatible, ignoreUnused, ignoreMalformed, ignoreMissing } = flags; return config.translations.map((translationsPath) => ({ task: async ({ messages }: { messages: Map }) => { // If `fix` is set we should try apply all possible fixes and override translations file. @@ -37,6 +38,7 @@ export function checkCompatibility(config: I18nConfig, flags: I18nFlags, log: To ignoreIncompatible: fix || ignoreIncompatible, ignoreUnused: fix || ignoreUnused, ignoreMissing: fix || ignoreMissing, + ignoreMalformed: fix || ignoreMalformed, sourceFileName: translationsPath, targetFileName: fix ? translationsPath : undefined, config, diff --git a/src/dev/i18n/utils.js b/src/dev/i18n/utils.js index 1d1c3118e0852..11a002fdbf4a8 100644 --- a/src/dev/i18n/utils.js +++ b/src/dev/i18n/utils.js @@ -208,6 +208,28 @@ export function checkValuesProperty(prefixedValuesKeys, defaultMessage, messageI } } +/** + * Verifies valid ICU message. + * @param message ICU message. + * @param messageId ICU message id + * @returns {undefined} + */ +export function verifyICUMessage(message) { + try { + parser.parse(message); + } catch (error) { + if (error.name === 'SyntaxError') { + const errorWithContext = createParserErrorMessage(message, { + loc: { + line: error.location.start.line, + column: error.location.start.column - 1, + }, + message: error.message, + }); + throw errorWithContext; + } + } +} /** * Extracts value references from the ICU message. * @param message ICU message. diff --git a/src/dev/run_i18n_check.ts b/src/dev/run_i18n_check.ts index 97ea988b1de3a..70eeedac2b8b6 100644 --- a/src/dev/run_i18n_check.ts +++ b/src/dev/run_i18n_check.ts @@ -36,6 +36,7 @@ run( async ({ flags: { 'ignore-incompatible': ignoreIncompatible, + 'ignore-malformed': ignoreMalformed, 'ignore-missing': ignoreMissing, 'ignore-unused': ignoreUnused, 'include-config': includeConfig, @@ -48,12 +49,13 @@ run( fix && (ignoreIncompatible !== undefined || ignoreUnused !== undefined || + ignoreMalformed !== undefined || ignoreMissing !== undefined) ) { throw createFailError( `${chalk.white.bgRed( ' I18N ERROR ' - )} none of the --ignore-incompatible, --ignore-unused or --ignore-missing is allowed when --fix is set.` + )} none of the --ignore-incompatible, --ignore-malformed, --ignore-unused or --ignore-missing is allowed when --fix is set.` ); } @@ -99,6 +101,7 @@ run( checkCompatibility( config, { + ignoreMalformed: !!ignoreMalformed, ignoreIncompatible: !!ignoreIncompatible, ignoreUnused: !!ignoreUnused, ignoreMissing: !!ignoreMissing, diff --git a/src/dev/run_i18n_integrate.ts b/src/dev/run_i18n_integrate.ts index ac1e957adfc99..25c3ea32783aa 100644 --- a/src/dev/run_i18n_integrate.ts +++ b/src/dev/run_i18n_integrate.ts @@ -31,6 +31,7 @@ run( 'ignore-incompatible': ignoreIncompatible = false, 'ignore-missing': ignoreMissing = false, 'ignore-unused': ignoreUnused = false, + 'ignore-malformed': ignoreMalformed = false, 'include-config': includeConfig, path, source, @@ -66,12 +67,13 @@ run( typeof ignoreIncompatible !== 'boolean' || typeof ignoreUnused !== 'boolean' || typeof ignoreMissing !== 'boolean' || + typeof ignoreMalformed !== 'boolean' || typeof dryRun !== 'boolean' ) { throw createFailError( `${chalk.white.bgRed( ' I18N ERROR ' - )} --ignore-incompatible, --ignore-unused, --ignore-missing, and --dry-run can't have values` + )} --ignore-incompatible, --ignore-unused, --ignore-malformed, --ignore-missing, and --dry-run can't have values` ); } @@ -97,6 +99,7 @@ run( ignoreIncompatible, ignoreUnused, ignoreMissing, + ignoreMalformed, config, log, }); @@ -108,7 +111,6 @@ run( const reporter = new ErrorReporter(); const messages: Map = new Map(); await list.run({ messages, reporter }); - process.exitCode = 0; } catch (error) { process.exitCode = 1; if (error instanceof ErrorReporter) { @@ -118,7 +120,6 @@ run( log.error(error); } } - process.exit(); }, { flags: {