diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index fc9c55e7868f4..d1cf0300b9e17 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -17,6 +17,7 @@
/src/plugins/input_control_vis/ @elastic/kibana-app
/src/plugins/management/ @elastic/kibana-app
/src/plugins/kibana_legacy/ @elastic/kibana-app
+/src/plugins/timelion/ @elastic/kibana-app
/src/plugins/vis_default_editor/ @elastic/kibana-app
/src/plugins/vis_type_markdown/ @elastic/kibana-app
/src/plugins/vis_type_metric/ @elastic/kibana-app
@@ -30,32 +31,23 @@
/src/plugins/visualize/ @elastic/kibana-app
/src/plugins/visualizations/ @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/public/local_application_service/ @elastic/kibana-app
-#CC# /src/plugins/vis_type @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/ @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/common/utils @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/migrations @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/public @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/public/dashboard/ @elastic/kibana-app
-#CC# /src/legacy/core_plugins/kibana/public/dev_tools/ @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/public/discover/ @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/public/local_application_service/ @elastic/kibana-app
-#CC# /src/legacy/core_plugins/console_legacy @elastic/kibana-app
#CC# /src/legacy/core_plugins/input_control_vis @elastic/kibana-app
#CC# /src/legacy/core_plugins/timelion @elastic/kibana-app
#CC# /src/legacy/core_plugins/vis_type_tagcloud @elastic/kibana-app
#CC# /src/legacy/core_plugins/vis_type_vega @elastic/kibana-app
#CC# /src/legacy/core_plugins/vis_type_vislib/ @elastic/kibana-app
-#CC# /src/legacy/server/sample_data/ @elastic/kibana-app
#CC# /src/legacy/server/url_shortening/ @elastic/kibana-app
#CC# /src/legacy/ui/public/state_management @elastic/kibana-app
-#CC# /src/plugins/charts/public/static/color_maps @elastic/kibana-app
#CC# /src/plugins/index_pattern_management/public @elastic/kibana-app
-#CC# /src/plugins/input_control_vis/ @elastic/kibana-app
-#CC# /src/plugins/kibana_legacy/ @elastic/kibana-app
-#CC# /src/plugins/timelion @elastic/kibana-app
#CC# /x-pack/legacy/plugins/dashboard_mode/ @elastic/kibana-app
#CC# /x-pack/plugins/dashboard_mode @elastic/kibana-app
-#CC# /x-pack/plugins/lens/ @elastic/kibana-app
# App Architecture
/examples/bfetch_explorer/ @elastic/kibana-app-arch
@@ -147,6 +139,7 @@
/src/plugins/home/server/services/ @elastic/kibana-core-ui
/x-pack/plugins/global_search_bar/ @elastic/kibana-core-ui
#CC# /src/legacy/core_plugins/newsfeed @elastic/kibana-core-ui
+#CC# /src/legacy/server/sample_data/ @elastic/kibana-core-ui
#CC# /src/plugins/newsfeed @elastic/kibana-core-ui
#CC# /src/plugins/home/public @elastic/kibana-core-ui
#CC# /src/plugins/home/server/services/ @elastic/kibana-core-ui
@@ -351,6 +344,8 @@ x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @elastic/kib
/x-pack/plugins/ingest_pipelines/ @elastic/es-ui
/packages/kbn-ace/ @elastic/es-ui
/packages/kbn-monaco/ @elastic/es-ui
+#CC# /src/legacy/core_plugins/kibana/public/dev_tools/ @elastic/es-ui
+#CC# /src/legacy/core_plugins/console_legacy @elastic/es-ui
#CC# /x-pack/legacy/plugins/rollup/ @elastic/es-ui
#CC# /x-pack/legacy/server/lib/create_router/ @elastic/es-ui
#CC# /x-pack/legacy/server/lib/check_license/ @elastic/es-ui
diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc
index ed58e77427d47..bf11f87b96ce9 100644
--- a/docs/developer/plugin-list.asciidoc
+++ b/docs/developer/plugin-list.asciidoc
@@ -168,7 +168,7 @@ It also provides a stateful version of it on the start contract.
|{kib-repo}blob/{branch}/src/plugins/telemetry_management_section/README.md[telemetryManagementSection]
-|This plugin adds the Advanced Settings section for the Usage Data collection (aka Telemetry).
+|This plugin adds the Advanced Settings section for the Usage and Security Data collection (aka Telemetry).
|{kib-repo}blob/{branch}/src/plugins/tile_map[tileMap]
diff --git a/docs/user/security/authorization/index.asciidoc b/docs/user/security/authorization/index.asciidoc
index 44ca96e4aece5..3af49753db664 100644
--- a/docs/user/security/authorization/index.asciidoc
+++ b/docs/user/security/authorization/index.asciidoc
@@ -2,11 +2,11 @@
[[xpack-security-authorization]]
=== Granting access to {kib}
-The Elastic Stack comes with the `kibana_admin` {ref}/built-in-roles.html[built-in role], which you can use to grant access to all Kibana features in all spaces. To grant users access to a subset of spaces or features, you can create a custom role that grants the desired Kibana privileges.
+The Elastic Stack comes with the `kibana_admin` {ref}/built-in-roles.html[built-in role], which you can use to grant access to all {kib} features in all spaces. To grant users access to a subset of spaces or features, you can create a custom role that grants the desired {kib} privileges.
-When you assign a user multiple roles, the user receives a union of the roles’ privileges. Therefore, assigning the `kibana_admin` role in addition to a custom role that grants Kibana privileges is ineffective because `kibana_admin` has access to all the features in all spaces.
+When you assign a user multiple roles, the user receives a union of the roles’ privileges. Therefore, assigning the `kibana_admin` role in addition to a custom role that grants {kib} privileges is ineffective because `kibana_admin` has access to all the features in all spaces.
-NOTE: When running multiple tenants of Kibana by changing the `kibana.index` in your `kibana.yml`, you cannot use `kibana_admin` to grant access. You must create custom roles that authorize the user for that specific tenant. Although multi-tenant installations are supported, the recommended approach to securing access to Kibana segments is to grant users access to specific spaces.
+NOTE: When running multiple tenants of {kib} by changing the `kibana.index` in your `kibana.yml`, you cannot use `kibana_admin` to grant access. You must create custom roles that authorize the user for that specific tenant. Although multi-tenant installations are supported, the recommended approach to securing access to {kib} segments is to grant users access to specific spaces.
[role="xpack"]
[[xpack-kibana-role-management]]
@@ -17,26 +17,26 @@ To create a role that grants {kib} privileges, open the menu, go to *Stack Manag
[[adding_kibana_privileges]]
==== Adding {kib} privileges
-To assign {kib} privileges to the role, click **Add space privilege** in the Kibana section.
+To assign {kib} privileges to the role, click **Add {kib} privilege** in the {kib} section.
[role="screenshot"]
-image::user/security/images/add-space-privileges.png[Add space privileges]
+image::user/security/images/add-space-privileges.png[Add {kib} privileges]
Open the **Spaces** selection control to specify whether to grant the role access to all spaces *** Global (all spaces)** or one or more individual spaces. If you select *** Global (all spaces)**, you can’t select individual spaces until you clear your selection.
Use the **Privilege** menu to grant access to features. The default is **Custom**, which you can use to grant access to individual features. Otherwise, you can grant read and write access to all current and future features by selecting **All**, or grant read access to all current and future features by selecting **Read**.
-When using the **Customize by feature** option, you can choose either **All**, **Read** or **None** for access to each feature. As new features are added to Kibana, roles that use the custom option do not automatically get access to the new features. You must manually update the roles.
+When using the **Customize by feature** option, you can choose either **All**, **Read** or **None** for access to each feature. As new features are added to {kib}, roles that use the custom option do not automatically get access to the new features. You must manually update the roles.
NOTE: *{stack-monitor-app}* relies on built-in roles to grant access. When a
user is assigned the appropriate roles, the *{stack-monitor-app}* application is
available; otherwise, it is not visible.
-To apply your changes, click **Create space privilege**. The space privilege shows up under the Kibana privileges section of the role.
+To apply your changes, click **Add {kib} privilege**. The privilege shows up under the {kib} privileges section of the role.
[role="screenshot"]
-image::user/security/images/create-space-privilege.png[Create space privilege]
+image::user/security/images/create-space-privilege.png[Add {kib} privilege]
==== Feature availability
@@ -64,9 +64,9 @@ Features are available to users when their roles grant access to the features, *
==== Assigning different privileges to different spaces
-Using the same role, it’s possible to assign different privileges to different spaces. After you’ve added space privileges, click **Add space privilege**. If you’ve already added privileges for either *** Global (all spaces)** or an individual space, you will not be able to select these in the **Spaces** selection control.
+Using the same role, it’s possible to assign different privileges to different spaces. After you’ve added privileges, click **Add {kib} privilege**. If you’ve already added privileges for either *** Global (all spaces)** or an individual space, you will not be able to select these in the **Spaces** selection control.
-Additionally, if you’ve already assigned privileges at *** Global (all spaces)**, you are only able to assign additional privileges to individual spaces. Similar to the behavior of multiple roles granting the union of all privileges, space privileges are also a union. If you’ve already granted the user the **All** privilege at *** Global (all spaces)**, you’re not able to restrict the role to only the **Read** privilege at an individual space.
+Additionally, if you’ve already assigned privileges at *** Global (all spaces)**, you are only able to assign additional privileges to individual spaces. Similar to the behavior of multiple roles granting the union of all privileges, {kib} privileges are also a union. If you’ve already granted the user the **All** privilege at *** Global (all spaces)**, you’re not able to restrict the role to only the **Read** privilege at an individual space.
==== Privilege summary
@@ -78,39 +78,37 @@ image::user/security/images/view-privilege-summary.png[View privilege summary]
==== Example 1: Grant all access to Dashboard at an individual space
-. Click **Add space privilege**.
+. Click **Add {kib} privilege**.
. For **Spaces**, select an individual space.
. For **Privilege**, leave the default selection of **Custom**.
. For the Dashboard feature, select **All**
-. Click **Create space privilege**.
+. Click **Add {kib} privilege**.
[role="screenshot"]
image::user/security/images/privilege-example-1.png[Privilege example 1]
==== Example 2: Grant all access to one space and read access to another
-. Click **Add space privilege**.
+. Click **Add {kib} privilege**.
. For **Spaces**, select the first space.
. For **Privilege**, select **All**.
-. Click **Create space privilege**.
-. Click **Add space privilege**.
+. Click **Add {kib} privilege**.
. For **Spaces**, select the second space.
. For **Privilege**, select **Read**.
-. Click **Create space privilege**.
+. Click **Add {kib} privilege**.
[role="screenshot"]
image::user/security/images/privilege-example-2.png[Privilege example 2]
==== Example 3: Grant read access to all spaces and write access to an individual space
-. Click **Add space privilege**.
+. Click **Add {kib} privilege**.
. For **Spaces**, select *** Global (all spaces)**.
. For **Privilege**, select **Read**.
-. Click **Create space privilege**.
-. Click **Add space privilege**.
+. Click **Add {kib} privilege**.
. For **Spaces**, select the individual space.
. For **Privilege**, select **All**.
-. Click **Create space privilege**.
+. Click **Add {kib} privilege**.
[role="screenshot"]
image::user/security/images/privilege-example-3.png[Privilege example 3]
diff --git a/src/plugins/discover/public/application/angular/context/api/_stubs.js b/src/plugins/discover/public/application/angular/context/api/_stubs.js
index 35ddf396c2dba..d82189db60935 100644
--- a/src/plugins/discover/public/application/angular/context/api/_stubs.js
+++ b/src/plugins/discover/public/application/angular/context/api/_stubs.js
@@ -74,7 +74,7 @@ export function createContextSearchSourceStub(hits, timeField = '@timestamp') {
searchSourceStub.fetch = sinon.spy(() => {
const timeField = searchSourceStub._stubTimeField;
const lastQuery = searchSourceStub.setField.withArgs('query').lastCall.args[1];
- const timeRange = lastQuery.query.constant_score.filter.range[timeField];
+ const timeRange = lastQuery.query.bool.must.constant_score.filter.range[timeField];
const lastSort = searchSourceStub.setField.withArgs('sort').lastCall.args[1];
const sortDirection = lastSort[0][timeField];
const sortFunction =
diff --git a/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js
index 4987c77f4bf25..4c0515906a494 100644
--- a/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js
+++ b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js
@@ -124,7 +124,9 @@ describe('context app', function () {
).then((hits) => {
const intervals = mockSearchSource.setField.args
.filter(([property]) => property === 'query')
- .map(([, { query }]) => get(query, ['constant_score', 'filter', 'range', '@timestamp']));
+ .map(([, { query }]) =>
+ get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp'])
+ );
expect(
intervals.every(({ gte, lte }) => (gte && lte ? moment(gte).isBefore(lte) : true))
@@ -160,7 +162,9 @@ describe('context app', function () {
).then((hits) => {
const intervals = mockSearchSource.setField.args
.filter(([property]) => property === 'query')
- .map(([, { query }]) => get(query, ['constant_score', 'filter', 'range', '@timestamp']));
+ .map(([, { query }]) =>
+ get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp'])
+ );
// should have started at the given time
expect(intervals[0].gte).toEqual(moment(MS_PER_DAY * 1000).toISOString());
diff --git a/src/plugins/discover/public/application/angular/context/api/context.successors.test.js b/src/plugins/discover/public/application/angular/context/api/context.successors.test.js
index ebf6e78585962..285d39cd4d8a4 100644
--- a/src/plugins/discover/public/application/angular/context/api/context.successors.test.js
+++ b/src/plugins/discover/public/application/angular/context/api/context.successors.test.js
@@ -125,7 +125,9 @@ describe('context app', function () {
).then((hits) => {
const intervals = mockSearchSource.setField.args
.filter(([property]) => property === 'query')
- .map(([, { query }]) => get(query, ['constant_score', 'filter', 'range', '@timestamp']));
+ .map(([, { query }]) =>
+ get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp'])
+ );
expect(
intervals.every(({ gte, lte }) => (gte && lte ? moment(gte).isBefore(lte) : true))
@@ -163,7 +165,9 @@ describe('context app', function () {
).then((hits) => {
const intervals = mockSearchSource.setField.args
.filter(([property]) => property === 'query')
- .map(([, { query }]) => get(query, ['constant_score', 'filter', 'range', '@timestamp']));
+ .map(([, { query }]) =>
+ get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp'])
+ );
// should have started at the given time
expect(intervals[0].lte).toEqual(moment(MS_PER_DAY * 3000).toISOString());
diff --git a/src/plugins/discover/public/application/angular/context/api/context.ts b/src/plugins/discover/public/application/angular/context/api/context.ts
index e244176914a9b..ba8cffd1d7558 100644
--- a/src/plugins/discover/public/application/angular/context/api/context.ts
+++ b/src/plugins/discover/public/application/angular/context/api/context.ts
@@ -31,6 +31,7 @@ export interface EsHitRecord {
fields: Record;
sort: number[];
_source: Record;
+ _id: string;
}
export type EsHitRecordList = EsHitRecord[];
@@ -100,7 +101,8 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract) {
interval,
searchAfter,
remainingSize,
- nanos
+ nanos,
+ anchor._id
);
documents =
diff --git a/src/plugins/discover/public/application/angular/context/api/utils/fetch_hits_in_interval.ts b/src/plugins/discover/public/application/angular/context/api/utils/fetch_hits_in_interval.ts
index 9a199ea4a62fc..5ac4164191633 100644
--- a/src/plugins/discover/public/application/angular/context/api/utils/fetch_hits_in_interval.ts
+++ b/src/plugins/discover/public/application/angular/context/api/utils/fetch_hits_in_interval.ts
@@ -43,7 +43,8 @@ export async function fetchHitsInInterval(
interval: IntervalValue[],
searchAfter: EsQuerySearchAfter,
maxCount: number,
- nanosValue: string
+ nanosValue: string,
+ anchorId: string
): Promise {
const range: RangeQuery = {
format: 'strict_date_optional_time',
@@ -61,10 +62,19 @@ export async function fetchHitsInInterval(
.setField('size', maxCount)
.setField('query', {
query: {
- constant_score: {
- filter: {
- range: {
- [timeField]: range,
+ bool: {
+ must: {
+ constant_score: {
+ filter: {
+ range: {
+ [timeField]: range,
+ },
+ },
+ },
+ },
+ must_not: {
+ ids: {
+ values: [anchorId],
},
},
},
diff --git a/src/plugins/telemetry_management_section/README.md b/src/plugins/telemetry_management_section/README.md
index 0f795786720c9..c23a8591f6794 100644
--- a/src/plugins/telemetry_management_section/README.md
+++ b/src/plugins/telemetry_management_section/README.md
@@ -1,5 +1,5 @@
# Telemetry Management Section
-This plugin adds the Advanced Settings section for the Usage Data collection (aka Telemetry).
+This plugin adds the Advanced Settings section for the Usage and Security Data collection (aka Telemetry).
The reason for having it separated from the `telemetry` plugin is to avoid circular dependencies. The plugin `advancedSettings` depends on the `home` app that depends on the `telemetry` plugin because of the telemetry banner in the welcome screen.
diff --git a/src/plugins/telemetry_management_section/public/components/__snapshots__/opt_in_security_example_flyout.test.tsx.snap b/src/plugins/telemetry_management_section/public/components/__snapshots__/opt_in_security_example_flyout.test.tsx.snap
new file mode 100644
index 0000000000000..0b9d426008ca4
--- /dev/null
+++ b/src/plugins/telemetry_management_section/public/components/__snapshots__/opt_in_security_example_flyout.test.tsx.snap
@@ -0,0 +1,134 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`security flyout renders as expected renders as expected 1`] = `
+
+
+
+
+
+ Endpoint security data
+
+
+
+
+ This is a representative sample of the endpoint security alert event that we collect. Endpoint security data is collected only when the Elastic Endpoint is enabled. It includes information about the endpoint configuration and detection events.
+
+
+
+
+
+ {
+ "@timestamp": "2020-09-22T14:34:56.82202300Z",
+ "agent": {
+ "build": {
+ "original": "version: 7.9.1, compiled: Thu Aug 27 14:50:21 2020, branch: 7.9, commit: b594beb958817dee9b9d908191ed766d483df3ea"
+ },
+ "id": "22dd8544-bcac-46cb-b970-5e681bb99e0b",
+ "type": "endpoint",
+ "version": "7.9.1"
+ },
+ "Endpoint": {
+ "policy": {
+ "applied": {
+ "artifacts": {
+ "global": {
+ "identifiers": [
+ {
+ "sha256": "6a546aade5563d3e8dffc1fe2d93d33edda8f9ca3e17ac3cc9ac707620cb9ecd",
+ "name": "endpointpe-v4-blocklist"
+ },
+ {
+ "sha256": "04f9f87accc5d5aea433427bd1bd4ec6908f8528c78ceed26f70df7875a99385",
+ "name": "endpointpe-v4-exceptionlist"
+ },
+ {
+ "sha256": "1471838597fcd79a54ea4a3ec9a9beee1a86feaedab6c98e61102559ced822a8",
+ "name": "endpointpe-v4-model"
+ },
+ {
+ "sha256": "824859b0c6749cc31951d92a73bbdddfcfe9f38abfe432087934d4dab9766ce8",
+ "name": "global-exceptionlist-windows"
+ }
+ ],
+ "version": "1.0.0"
+ },
+ "user": {
+ "identifiers": [
+ {
+ "sha256": "d801aa1fb7ddcc330a5e3173372ea6af4a3d08ec58074478e85aa5603e926658",
+ "name": "endpoint-exceptionlist-windows-v1"
+ }
+ ],
+ "version": "1.0.0"
+ }
+ }
+ }
+ }
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "elastic": {
+ "agent": {
+ "id": "b2e88aea-2671-402a-828a-957526bac315"
+ }
+ },
+ "file": {
+ "path": "C:\\\\Windows\\\\Temp\\\\mimikatz.exe",
+ "size": 1263880,
+ "created": "2020-05-19T07:50:06.0Z",
+ "accessed": "2020-09-22T14:29:19.93531400Z",
+ "mtime": "2020-09-22T14:29:03.6040000Z",
+ "directory": "C:\\\\Windows\\\\Temp",
+ "hash": {
+ "sha1": "c9fb7f8a4c6b7b12b493a99a8dc6901d17867388",
+ "sha256": "cb1553a3c88817e4cc774a5a93f9158f6785bd3815447d04b6c3f4c2c4b21ed7",
+ "md5": "465d5d850f54d9cde767bda90743df30"
+ },
+ "Ext": {
+ "code_signature": {
+ "trusted": true,
+ "subject_name": "Open Source Developer, Benjamin Delpy",
+ "exists": true,
+ "status": "trusted"
+ },
+ "malware_classification": {
+ "identifier": "endpointpe-v4-model",
+ "score": 0.99956864118576,
+ "threshold": 0.71,
+ "version": "0.0.0"
+ }
+ }
+ },
+ "host": {
+ "os": {
+ "Ext": {
+ "variant": "Windows 10 Enterprise Evaluation"
+ },
+ "kernel": "2004 (10.0.19041.388)",
+ "name": "Windows",
+ "family": "windows",
+ "version": "2004 (10.0.19041.388)",
+ "platform": "windows",
+ "full": "Windows 10 Enterprise Evaluation 2004 (10.0.19041.388)"
+ }
+ },
+ "event": {
+ "kind": "alert"
+ },
+ "cluster_uuid": "kLbKvSMcRiiFAR0t8LebDA",
+ "cluster_name": "elasticsearch"
+}
+
+
+
+
+`;
diff --git a/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap b/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap
index bed1bbeabb044..7357598c8495f 100644
--- a/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap
+++ b/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap
@@ -80,15 +80,32 @@ exports[`TelemetryManagementSectionComponent renders as expected 1`] = `
/>
-
-
-
+
+
+ ,
+ "endpointSecurityData":
+
+ ,
+ }
+ }
+ />
,
"displayName": "Provide usage statistics",
diff --git a/src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.test.tsx b/src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.test.tsx
new file mode 100644
index 0000000000000..c80d0daf5a695
--- /dev/null
+++ b/src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * 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 React from 'react';
+import { shallowWithIntl } from 'test_utils/enzyme_helpers';
+import { OptInSecurityExampleFlyout } from './opt_in_security_example_flyout';
+
+describe('security flyout renders as expected', () => {
+ it('renders as expected', () => {
+ expect(shallowWithIntl( )).toMatchSnapshot();
+ });
+});
diff --git a/src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.tsx b/src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.tsx
new file mode 100644
index 0000000000000..af0de5b268ddc
--- /dev/null
+++ b/src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.tsx
@@ -0,0 +1,235 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import * as React from 'react';
+
+import {
+ EuiCallOut,
+ EuiCodeBlock,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiFlyout,
+ EuiFlyoutHeader,
+ EuiFlyoutBody,
+ EuiLoadingSpinner,
+ EuiPortal, // EuiPortal is a temporary requirement to use EuiFlyout with "ownFocus"
+ EuiText,
+ EuiTextColor,
+ EuiTitle,
+} from '@elastic/eui';
+
+import { FormattedMessage } from '@kbn/i18n/react';
+
+interface Props {
+ onClose: () => void;
+}
+
+interface State {
+ isLoading: boolean;
+ hasPrivilegeToRead: boolean;
+}
+
+/**
+ * React component for displaying the example data associated with the Telemetry opt-in banner.
+ */
+export class OptInSecurityExampleFlyout extends React.PureComponent {
+ public readonly state: State = {
+ isLoading: true,
+ hasPrivilegeToRead: false,
+ };
+
+ async componentDidMount() {
+ try {
+ this.setState({
+ isLoading: false,
+ hasPrivilegeToRead: true,
+ });
+ } catch (err) {
+ this.setState({
+ isLoading: false,
+ hasPrivilegeToRead: err.status !== 403,
+ });
+ }
+ }
+
+ renderBody({ isLoading, hasPrivilegeToRead }: State) {
+ if (isLoading) {
+ return (
+
+
+
+
+
+ );
+ }
+
+ if (!hasPrivilegeToRead) {
+ return (
+
+ }
+ color="danger"
+ iconType="cross"
+ >
+
+
+ );
+ }
+
+ return (
+
+ {JSON.stringify(this.exampleSecurityPayload, null, 2)}
+
+ );
+ }
+
+ render() {
+ return (
+
+
+
+
+ Endpoint security data
+
+
+
+ This is a representative sample of the endpoint security alert event that we
+ collect. Endpoint security data is collected only when the Elastic Endpoint is
+ enabled. It includes information about the endpoint configuration and detection
+ events.
+
+
+
+ {this.renderBody(this.state)}
+
+
+ );
+ }
+
+ exampleSecurityPayload = {
+ '@timestamp': '2020-09-22T14:34:56.82202300Z',
+ agent: {
+ build: {
+ original:
+ 'version: 7.9.1, compiled: Thu Aug 27 14:50:21 2020, branch: 7.9, commit: b594beb958817dee9b9d908191ed766d483df3ea',
+ },
+ id: '22dd8544-bcac-46cb-b970-5e681bb99e0b',
+ type: 'endpoint',
+ version: '7.9.1',
+ },
+ Endpoint: {
+ policy: {
+ applied: {
+ artifacts: {
+ global: {
+ identifiers: [
+ {
+ sha256: '6a546aade5563d3e8dffc1fe2d93d33edda8f9ca3e17ac3cc9ac707620cb9ecd',
+ name: 'endpointpe-v4-blocklist',
+ },
+ {
+ sha256: '04f9f87accc5d5aea433427bd1bd4ec6908f8528c78ceed26f70df7875a99385',
+ name: 'endpointpe-v4-exceptionlist',
+ },
+ {
+ sha256: '1471838597fcd79a54ea4a3ec9a9beee1a86feaedab6c98e61102559ced822a8',
+ name: 'endpointpe-v4-model',
+ },
+ {
+ sha256: '824859b0c6749cc31951d92a73bbdddfcfe9f38abfe432087934d4dab9766ce8',
+ name: 'global-exceptionlist-windows',
+ },
+ ],
+ version: '1.0.0',
+ },
+ user: {
+ identifiers: [
+ {
+ sha256: 'd801aa1fb7ddcc330a5e3173372ea6af4a3d08ec58074478e85aa5603e926658',
+ name: 'endpoint-exceptionlist-windows-v1',
+ },
+ ],
+ version: '1.0.0',
+ },
+ },
+ },
+ },
+ },
+ ecs: {
+ version: '1.5.0',
+ },
+ elastic: {
+ agent: {
+ id: 'b2e88aea-2671-402a-828a-957526bac315',
+ },
+ },
+ file: {
+ path: 'C:\\Windows\\Temp\\mimikatz.exe',
+ size: 1263880,
+ created: '2020-05-19T07:50:06.0Z',
+ accessed: '2020-09-22T14:29:19.93531400Z',
+ mtime: '2020-09-22T14:29:03.6040000Z',
+ directory: 'C:\\Windows\\Temp',
+ hash: {
+ sha1: 'c9fb7f8a4c6b7b12b493a99a8dc6901d17867388',
+ sha256: 'cb1553a3c88817e4cc774a5a93f9158f6785bd3815447d04b6c3f4c2c4b21ed7',
+ md5: '465d5d850f54d9cde767bda90743df30',
+ },
+ Ext: {
+ code_signature: {
+ trusted: true,
+ subject_name: 'Open Source Developer, Benjamin Delpy',
+ exists: true,
+ status: 'trusted',
+ },
+ malware_classification: {
+ identifier: 'endpointpe-v4-model',
+ score: 0.99956864118576,
+ threshold: 0.71,
+ version: '0.0.0',
+ },
+ },
+ },
+ host: {
+ os: {
+ Ext: {
+ variant: 'Windows 10 Enterprise Evaluation',
+ },
+ kernel: '2004 (10.0.19041.388)',
+ name: 'Windows',
+ family: 'windows',
+ version: '2004 (10.0.19041.388)',
+ platform: 'windows',
+ full: 'Windows 10 Enterprise Evaluation 2004 (10.0.19041.388)',
+ },
+ },
+ event: {
+ kind: 'alert',
+ },
+ cluster_uuid: 'kLbKvSMcRiiFAR0t8LebDA',
+ cluster_name: 'elasticsearch',
+ };
+}
diff --git a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx
index 0e2855f055540..993295746ea5b 100644
--- a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx
+++ b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx
@@ -212,7 +212,7 @@ describe('TelemetryManagementSectionComponent', () => {
/>
);
try {
- const toggleExampleComponent = component.find('p > EuiLink[onClick]');
+ const toggleExampleComponent = component.find('FormattedMessage > EuiLink[onClick]').at(0);
const updatedView = toggleExampleComponent.simulate('click');
updatedView.find('OptInExampleFlyout');
updatedView.simulate('close');
@@ -221,6 +221,42 @@ describe('TelemetryManagementSectionComponent', () => {
}
});
+ it('shows the OptInSecurityExampleFlyout', () => {
+ const onQueryMatchChange = jest.fn();
+ const telemetryService = new TelemetryService({
+ config: {
+ enabled: true,
+ url: '',
+ banner: true,
+ allowChangingOptInStatus: true,
+ optIn: false,
+ optInStatusUrl: '',
+ sendUsageFrom: 'browser',
+ },
+ reportOptInStatusChange: false,
+ notifications: coreStart.notifications,
+ http: coreSetup.http,
+ });
+
+ const component = mountWithIntl(
+
+ );
+ try {
+ const toggleExampleComponent = component.find('FormattedMessage > EuiLink[onClick]').at(1);
+ const updatedView = toggleExampleComponent.simulate('click');
+ updatedView.find('OptInSecurityExampleFlyout');
+ updatedView.simulate('close');
+ } finally {
+ component.unmount();
+ }
+ });
+
it('toggles the OptIn button', async () => {
const onQueryMatchChange = jest.fn();
const telemetryService = new TelemetryService({
diff --git a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx
index 9ae0a3d12fbb5..822d8b49661c1 100644
--- a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx
+++ b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx
@@ -34,6 +34,7 @@ import { i18n } from '@kbn/i18n';
import { TelemetryPluginSetup } from 'src/plugins/telemetry/public';
import { PRIVACY_STATEMENT_URL } from '../../../telemetry/common/constants';
import { OptInExampleFlyout } from './opt_in_example_flyout';
+import { OptInSecurityExampleFlyout } from './opt_in_security_example_flyout';
import { LazyField } from '../../../advanced_settings/public';
import { ToastsStart } from '../../../../core/public';
@@ -53,6 +54,7 @@ interface Props {
interface State {
processing: boolean;
showExample: boolean;
+ showSecurityExample: boolean;
queryMatches: boolean | null;
enabled: boolean;
}
@@ -61,6 +63,7 @@ export class TelemetryManagementSection extends Component {
state: State = {
processing: false,
showExample: false,
+ showSecurityExample: false,
queryMatches: null,
enabled: this.props.telemetryService.getIsOptedIn() || false,
};
@@ -87,7 +90,7 @@ export class TelemetryManagementSection extends Component {
render() {
const { telemetryService } = this.props;
- const { showExample, queryMatches, enabled, processing } = this.state;
+ const { showExample, showSecurityExample, queryMatches, enabled, processing } = this.state;
if (!telemetryService.getCanChangeOptInStatus()) {
return null;
@@ -105,6 +108,7 @@ export class TelemetryManagementSection extends Component {
onClose={this.toggleExample}
/>
)}
+ {showSecurityExample && }
@@ -197,12 +201,25 @@ export class TelemetryManagementSection extends Component {
/>
-
-
-
+
+
+
+ ),
+ endpointSecurityData: (
+
+
+
+ ),
+ }}
+ />
);
@@ -245,6 +262,12 @@ export class TelemetryManagementSection extends Component {
showExample: !this.state.showExample,
});
};
+
+ toggleSecurityExample = () => {
+ this.setState({
+ showSecurityExample: !this.state.showSecurityExample,
+ });
+ };
}
// required for lazy loading
diff --git a/test/functional/apps/visualize/_tsvb_time_series.ts b/test/functional/apps/visualize/_tsvb_time_series.ts
index 0b2a52b367a20..d4a079a38c814 100644
--- a/test/functional/apps/visualize/_tsvb_time_series.ts
+++ b/test/functional/apps/visualize/_tsvb_time_series.ts
@@ -84,8 +84,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await visualBuilder.clickSeriesOption();
await visualBuilder.enterSeriesTemplate('$ {{value}}');
- const actualCount = await visualBuilder.getRhythmChartLegendValue();
- expect(actualCount).to.be(expectedLegendValue);
+ await retry.try(async () => {
+ const actualCount = await visualBuilder.getRhythmChartLegendValue();
+ expect(actualCount).to.be(expectedLegendValue);
+ });
});
it('should show the correct count in the legend with percent formatter', async () => {
diff --git a/x-pack/.telemetryrc.json b/x-pack/.telemetryrc.json
index c7430666c538f..db50727c599a9 100644
--- a/x-pack/.telemetryrc.json
+++ b/x-pack/.telemetryrc.json
@@ -2,7 +2,6 @@
"output": "plugins/telemetry_collection_xpack/schema/xpack_plugins.json",
"root": "plugins/",
"exclude": [
- "plugins/actions/server/usage/actions_usage_collector.ts",
"plugins/alerts/server/usage/alerts_usage_collector.ts",
"plugins/apm/server/lib/apm_telemetry/index.ts"
]
diff --git a/x-pack/plugins/actions/server/usage/actions_usage_collector.ts b/x-pack/plugins/actions/server/usage/actions_usage_collector.ts
index aa546e08ea1ba..fac57b6282c44 100644
--- a/x-pack/plugins/actions/server/usage/actions_usage_collector.ts
+++ b/x-pack/plugins/actions/server/usage/actions_usage_collector.ts
@@ -4,11 +4,26 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
+import { MakeSchemaFrom, UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { get } from 'lodash';
import { TaskManagerStartContract } from '../../../task_manager/server';
import { ActionsUsage } from './types';
+const byTypeSchema: MakeSchemaFrom['count_by_type'] = {
+ // TODO: Find out an automated way to populate the keys or reformat these into an array (and change the Remote Telemetry indexer accordingly)
+ DYNAMIC_KEY: { type: 'long' },
+ // Known actions:
+ __email: { type: 'long' },
+ __index: { type: 'long' },
+ __pagerduty: { type: 'long' },
+ '__server-log': { type: 'long' },
+ __slack: { type: 'long' },
+ __webhook: { type: 'long' },
+ __servicenow: { type: 'long' },
+ __jira: { type: 'long' },
+ __resilient: { type: 'long' },
+};
+
export function createActionsUsageCollector(
usageCollection: UsageCollectionSetup,
taskManager: TaskManagerStartContract
@@ -16,6 +31,12 @@ export function createActionsUsageCollector(
return usageCollection.makeUsageCollector({
type: 'actions',
isReady: () => true,
+ schema: {
+ count_total: { type: 'long' },
+ count_active_total: { type: 'long' },
+ count_by_type: byTypeSchema,
+ count_active_by_type: byTypeSchema,
+ },
fetch: async () => {
try {
const doc = await getLatestTaskState(await taskManager);
diff --git a/x-pack/plugins/apm/common/service_health_status.ts b/x-pack/plugins/apm/common/service_health_status.ts
index 1d4bcfb3b0e07..f66e03a9733a3 100644
--- a/x-pack/plugins/apm/common/service_health_status.ts
+++ b/x-pack/plugins/apm/common/service_health_status.ts
@@ -54,6 +54,22 @@ export function getServiceHealthStatusColor(
}
}
+export function getServiceHealthStatusBadgeColor(
+ theme: EuiTheme,
+ status: ServiceHealthStatus
+) {
+ switch (status) {
+ case ServiceHealthStatus.healthy:
+ return theme.eui.euiColorVis0_behindText;
+ case ServiceHealthStatus.warning:
+ return theme.eui.euiColorVis5_behindText;
+ case ServiceHealthStatus.critical:
+ return theme.eui.euiColorVis9_behindText;
+ case ServiceHealthStatus.unknown:
+ return theme.eui.euiColorMediumShade;
+ }
+}
+
export function getServiceHealthStatusLabel(status: ServiceHealthStatus) {
switch (status) {
case ServiceHealthStatus.critical:
diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_opbeans_beats.json b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_opbeans_beats.json
index 153fa57bb05e7..cfd905f145fe2 100644
--- a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_opbeans_beats.json
+++ b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_opbeans_beats.json
@@ -83,7 +83,7 @@
"id": "opbeans-go~>postgresql",
"sourceData": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go"
},
@@ -103,7 +103,7 @@
"id": "opbeans-go~opbeans-java",
"sourceData": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go"
},
@@ -123,13 +123,13 @@
"id": "opbeans-go~opbeans-node",
"sourceData": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go"
},
"targetData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
},
@@ -143,7 +143,7 @@
"id": "opbeans-go~opbeans-ruby",
"sourceData": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go"
},
@@ -189,7 +189,7 @@
},
"targetData": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go"
},
@@ -209,7 +209,7 @@
},
"targetData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
}
@@ -242,7 +242,7 @@
"id": "opbeans-node~>postgresql",
"sourceData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
},
@@ -262,7 +262,7 @@
"id": "opbeans-node~>redis",
"sourceData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
},
@@ -282,13 +282,13 @@
"id": "opbeans-node~opbeans-go",
"sourceData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
},
"targetData": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go"
},
@@ -302,7 +302,7 @@
"id": "opbeans-node~opbeans-python",
"sourceData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
},
@@ -322,7 +322,7 @@
"id": "opbeans-node~opbeans-ruby",
"sourceData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
},
@@ -408,7 +408,7 @@
},
"targetData": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go"
}
@@ -427,7 +427,7 @@
},
"targetData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
},
@@ -487,7 +487,7 @@
},
"targetData": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go"
},
@@ -527,7 +527,7 @@
},
"targetData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
},
@@ -566,7 +566,7 @@
},
"targetData": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go"
}
@@ -602,7 +602,7 @@
},
"targetData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
}
@@ -673,7 +673,7 @@
{
"data": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs",
"anomaly_score": 41.31593099784474,
@@ -733,7 +733,7 @@
{
"data": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go",
"anomaly_score": 0.2633884161762746,
diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/HealthBadge.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/HealthBadge.tsx
index c6be0a352ef66..e8ad3e65b1a47 100644
--- a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/HealthBadge.tsx
+++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/HealthBadge.tsx
@@ -6,7 +6,7 @@
import React from 'react';
import { EuiBadge } from '@elastic/eui';
import {
- getServiceHealthStatusColor,
+ getServiceHealthStatusBadgeColor,
getServiceHealthStatusLabel,
ServiceHealthStatus,
} from '../../../../../common/service_health_status';
@@ -20,7 +20,7 @@ export function HealthBadge({
const theme = useTheme();
return (
-
+
{getServiceHealthStatusLabel(healthStatus)}
);
diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap
index 40a2b6a5fa81b..ee3a4fce0dbaa 100644
--- a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap
+++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap
@@ -153,7 +153,7 @@ NodeList [
>
(val ? val.split(',') : [])
) as Partial>;
- return useDeepObjectIdentity({ kuery, environment, ...localUiFilters });
+ return useDeepObjectIdentity({
+ kuery,
+ environment: environment || ENVIRONMENT_ALL.value,
+ ...localUiFilters,
+ });
}
const defaultRefresh = (_time: TimeRange) => {};
diff --git a/x-pack/plugins/apm/public/hooks/useTransactionDistribution.ts b/x-pack/plugins/apm/public/hooks/useTransactionDistribution.ts
index cd3e02d155602..a5096a314388c 100644
--- a/x-pack/plugins/apm/public/hooks/useTransactionDistribution.ts
+++ b/x-pack/plugins/apm/public/hooks/useTransactionDistribution.ts
@@ -75,7 +75,7 @@ export function useTransactionDistribution(urlParams: IUrlParams) {
const preferredSample = maybe(bucketsSortedByCount[0]?.samples[0]);
- history.push({
+ history.replace({
...history.location,
search: fromQuery({
...omit(toQuery(history.location.search), [
diff --git a/x-pack/plugins/apm/public/utils/testHelpers.tsx b/x-pack/plugins/apm/public/utils/testHelpers.tsx
index 971455fde3946..7826e9672a3bb 100644
--- a/x-pack/plugins/apm/public/utils/testHelpers.tsx
+++ b/x-pack/plugins/apm/public/utils/testHelpers.tsx
@@ -25,6 +25,7 @@ import {
} from '../../typings/elasticsearch';
import { MockApmPluginContextWrapper } from '../context/ApmPluginContext/MockApmPluginContext';
import { UrlParamsProvider } from '../context/UrlParamsContext';
+import { UIFilters } from '../../typings/ui_filters';
const originalConsoleWarn = console.warn; // eslint-disable-line no-console
/**
@@ -118,7 +119,8 @@ interface MockSetup {
apmEventClient: any;
internalClient: any;
config: APMConfig;
- uiFiltersES: ESFilter[];
+ uiFilters: UIFilters;
+ esFilter: ESFilter[];
indices: {
/* eslint-disable @typescript-eslint/naming-convention */
'apm_oss.sourcemapIndices': string;
@@ -179,7 +181,8 @@ export async function inspectSearchParams(
},
}
) as APMConfig,
- uiFiltersES: [{ term: { 'my.custom.ui.filter': 'foo-bar' } }],
+ uiFilters: { environment: 'test' },
+ esFilter: [{ term: { 'service.environment': 'test' } }],
indices: {
/* eslint-disable @typescript-eslint/naming-convention */
'apm_oss.sourcemapIndices': 'myIndex',
diff --git a/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap
index 63b6c9cde4d0d..632232ffb075d 100644
--- a/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap
@@ -32,7 +32,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -119,7 +119,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -194,7 +194,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap
index ea142ca2acc00..b329499c8b045 100644
--- a/x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap
@@ -40,7 +40,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -91,7 +91,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts b/x-pack/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts
index 1a83113de35f2..50da1f9c20d16 100644
--- a/x-pack/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts
+++ b/x-pack/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts
@@ -41,7 +41,10 @@ describe('timeseriesFetcher', () => {
get: () => 'myIndex',
}
) as APMConfig,
- uiFiltersES: [
+ uiFilters: {
+ environment: 'prod',
+ },
+ esFilter: [
{
term: { 'service.environment': 'prod' },
},
diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts
index de6df15354e79..a42710947a792 100644
--- a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts
+++ b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts
@@ -11,11 +11,7 @@ import {
SERVICE_NAME,
} from '../../../../common/elasticsearch_fieldnames';
import { rangeFilter } from '../../../../common/utils/range_filter';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
export async function getBuckets({
serviceName,
@@ -26,13 +22,13 @@ export async function getBuckets({
serviceName: string;
groupId?: string;
bucketSize: number;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
}) {
- const { start, end, uiFiltersES, apmEventClient } = setup;
+ const { start, end, esFilter, apmEventClient } = setup;
const filter: ESFilter[] = [
{ term: { [SERVICE_NAME]: serviceName } },
{ range: rangeFilter(start, end) },
- ...uiFiltersES,
+ ...esFilter,
];
if (groupId) {
diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts b/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts
index 3b48b6c5be594..dea518cad8e40 100644
--- a/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts
+++ b/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts
@@ -5,11 +5,7 @@
*/
import { PromiseReturnType } from '../../../../typings/common';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { getBuckets } from './get_buckets';
import { BUCKET_TARGET_COUNT } from '../../transactions/constants';
@@ -28,7 +24,7 @@ export async function getErrorDistribution({
}: {
serviceName: string;
groupId?: string;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
}) {
const bucketSize = getBucketSize({ start: setup.start, end: setup.end });
const { buckets, noHits } = await getBuckets({
diff --git a/x-pack/plugins/apm/server/lib/errors/get_error_group.ts b/x-pack/plugins/apm/server/lib/errors/get_error_group.ts
index b23c955b57183..0fbc7720f7111 100644
--- a/x-pack/plugins/apm/server/lib/errors/get_error_group.ts
+++ b/x-pack/plugins/apm/server/lib/errors/get_error_group.ts
@@ -12,11 +12,7 @@ import {
} from '../../../common/elasticsearch_fieldnames';
import { PromiseReturnType } from '../../../typings/common';
import { rangeFilter } from '../../../common/utils/range_filter';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getTransaction } from '../transactions/get_transaction';
export type ErrorGroupAPIResponse = PromiseReturnType;
@@ -29,9 +25,9 @@ export async function getErrorGroup({
}: {
serviceName: string;
groupId: string;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
}) {
- const { start, end, uiFiltersES, apmEventClient } = setup;
+ const { start, end, esFilter, apmEventClient } = setup;
const params = {
apm: {
@@ -45,7 +41,7 @@ export async function getErrorGroup({
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [ERROR_GROUP_ID]: groupId } },
{ range: rangeFilter(start, end) },
- ...uiFiltersES,
+ ...esFilter,
],
should: [{ term: { [TRANSACTION_SAMPLED]: true } }],
},
diff --git a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts
index ab1c2149be343..006d2fae3d4fb 100644
--- a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts
+++ b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts
@@ -13,11 +13,7 @@ import {
ERROR_LOG_MESSAGE,
} from '../../../common/elasticsearch_fieldnames';
import { PromiseReturnType } from '../../../typings/common';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getErrorGroupsProjection } from '../../projections/errors';
import { mergeProjection } from '../../projections/util/merge_projection';
import { SortOptions } from '../../../typings/elasticsearch/aggregations';
@@ -35,7 +31,7 @@ export async function getErrorGroups({
serviceName: string;
sortField?: string;
sortDirection?: 'asc' | 'desc';
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
}) {
const { apmEventClient } = setup;
diff --git a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_ui_filters_es.ts b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_es_filter.ts
similarity index 96%
rename from x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_ui_filters_es.ts
rename to x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_es_filter.ts
index c1405b44f2a8a..1b8f32d4de8b9 100644
--- a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_ui_filters_es.ts
+++ b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_es_filter.ts
@@ -13,7 +13,7 @@ import {
} from '../../ui_filters/local_ui_filters/config';
import { esKuery } from '../../../../../../../src/plugins/data/server';
-export function getUiFiltersES(uiFilters: UIFilters) {
+export function getEsFilter(uiFilters: UIFilters) {
const { kuery, environment, ...localFilterValues } = uiFilters;
const mappedFilters = localUIFilterNames
.filter((name) => name in localFilterValues)
diff --git a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_parsed_ui_filters.ts b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_parsed_ui_filters.ts
deleted file mode 100644
index 324da199807c7..0000000000000
--- a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_parsed_ui_filters.ts
+++ /dev/null
@@ -1,23 +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 { Logger } from 'src/core/server';
-import { UIFilters } from '../../../../typings/ui_filters';
-
-export function getParsedUiFilters({
- uiFilters,
- logger,
-}: {
- uiFilters: string;
- logger: Logger;
-}): UIFilters {
- try {
- return JSON.parse(uiFilters);
- } catch (error) {
- logger.error(error);
- }
- return {};
-}
diff --git a/x-pack/plugins/apm/server/lib/helpers/setup_request.ts b/x-pack/plugins/apm/server/lib/helpers/setup_request.ts
index eba75433a5148..26896a050dd88 100644
--- a/x-pack/plugins/apm/server/lib/helpers/setup_request.ts
+++ b/x-pack/plugins/apm/server/lib/helpers/setup_request.ts
@@ -5,6 +5,7 @@
*/
import moment from 'moment';
+import { Logger } from 'kibana/server';
import { isActivePlatinumLicense } from '../../../common/service_map';
import { UI_SETTINGS } from '../../../../../../src/plugins/data/common';
import { KibanaRequest } from '../../../../../../src/core/server';
@@ -14,7 +15,7 @@ import {
ApmIndicesConfig,
} from '../settings/apm_indices/get_apm_indices';
import { ESFilter } from '../../../typings/elasticsearch';
-import { getUiFiltersES } from './convert_ui_filters/get_ui_filters_es';
+import { getEsFilter } from './convert_ui_filters/get_es_filter';
import { APMRequestHandlerContext } from '../../routes/typings';
import { ProcessorEvent } from '../../../common/processor_event';
import {
@@ -25,14 +26,8 @@ import {
APMInternalClient,
createInternalESClient,
} from './create_es_client/create_internal_es_client';
+import { UIFilters } from '../../../typings/ui_filters';
-function decodeUiFilters(uiFiltersEncoded?: string) {
- if (!uiFiltersEncoded) {
- return [];
- }
- const uiFilters = JSON.parse(uiFiltersEncoded);
- return getUiFiltersES(uiFilters);
-}
// Explicitly type Setup to prevent TS initialization errors
// https://github.com/microsoft/TypeScript/issues/34933
@@ -42,6 +37,8 @@ export interface Setup {
ml?: ReturnType;
config: APMConfig;
indices: ApmIndicesConfig;
+ uiFilters: UIFilters;
+ esFilter: ESFilter[];
}
export interface SetupTimeRange {
@@ -49,10 +46,6 @@ export interface SetupTimeRange {
end: number;
}
-export interface SetupUIFilters {
- uiFiltersES: ESFilter[];
-}
-
interface SetupRequestParams {
query?: {
_debug?: boolean;
@@ -65,16 +58,13 @@ interface SetupRequestParams {
type InferSetup = Setup &
(TParams extends { query: { start: string } } ? { start: number } : {}) &
- (TParams extends { query: { end: string } } ? { end: number } : {}) &
- (TParams extends { query: { uiFilters: string } }
- ? { uiFiltersES: ESFilter[] }
- : {});
+ (TParams extends { query: { end: string } } ? { end: number } : {});
export async function setupRequest(
context: APMRequestHandlerContext,
request: KibanaRequest
): Promise> {
- const { config } = context;
+ const { config, logger } = context;
const { query } = context.params;
const [indices, includeFrozen] = await Promise.all([
@@ -85,7 +75,7 @@ export async function setupRequest(
context.core.uiSettings.client.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN),
]);
- const uiFiltersES = decodeUiFilters(query.uiFilters);
+ const uiFilters = decodeUiFilters(logger, query.uiFilters);
const coreSetupRequest = {
indices,
@@ -108,12 +98,13 @@ export async function setupRequest(
)
: undefined,
config,
+ uiFilters,
+ esFilter: getEsFilter(uiFilters),
};
return {
...('start' in query ? { start: moment.utc(query.start).valueOf() } : {}),
...('end' in query ? { end: moment.utc(query.end).valueOf() } : {}),
- ...('uiFilters' in query ? { uiFiltersES } : {}),
...coreSetupRequest,
} as InferSetup;
}
@@ -129,3 +120,15 @@ function getMlSetup(
modules: ml.modulesProvider(request, savedObjectsClient),
};
}
+
+function decodeUiFilters(logger: Logger, uiFiltersEncoded?: string): UIFilters {
+ if (!uiFiltersEncoded) {
+ return {};
+ }
+ try {
+ return JSON.parse(uiFiltersEncoded);
+ } catch (error) {
+ logger.error(error);
+ return {};
+ }
+}
diff --git a/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap
index 2868dcfda97b6..961a1eee61d1d 100644
--- a/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap
@@ -87,7 +87,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -175,7 +175,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -206,7 +206,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -231,7 +231,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -258,7 +258,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -283,7 +283,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -338,7 +338,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -431,7 +431,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -514,7 +514,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -623,7 +623,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -717,7 +717,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -748,7 +748,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -773,7 +773,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -800,7 +800,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -825,7 +825,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -886,7 +886,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -985,7 +985,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -1074,7 +1074,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -1172,7 +1172,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -1255,7 +1255,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -1286,7 +1286,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -1311,7 +1311,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -1338,7 +1338,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -1363,7 +1363,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -1413,7 +1413,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -1501,7 +1501,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -1579,7 +1579,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts
index 6ee507d7b9bb1..fbcbc9f12791f 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts
@@ -4,16 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { getCPUChartData } from './shared/cpu';
import { getMemoryChartData } from './shared/memory';
export async function getDefaultMetricsCharts(
- setup: Setup & SetupTimeRange & SetupUIFilters,
+ setup: Setup & SetupTimeRange,
serviceName: string
) {
const charts = await Promise.all([
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts
index d7e64bdcacd12..2ed11480a7585 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts
@@ -11,11 +11,7 @@
import { sum, round } from 'lodash';
import theme from '@elastic/eui/dist/eui_theme_light.json';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../../helpers/setup_request';
import { getMetricsDateHistogramParams } from '../../../../helpers/metrics';
import { ChartBase } from '../../../types';
import { getMetricsProjection } from '../../../../../projections/metrics';
@@ -36,7 +32,7 @@ export async function fetchAndTransformGcMetrics({
chartBase,
fieldName,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
chartBase: ChartBase;
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts
index 6e562b9a8ee87..7cedeb828e3b7 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts
@@ -7,11 +7,7 @@
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { i18n } from '@kbn/i18n';
import { METRIC_JAVA_GC_COUNT } from '../../../../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../../helpers/setup_request';
import { fetchAndTransformGcMetrics } from './fetch_and_transform_gc_metrics';
import { ChartBase } from '../../../types';
@@ -35,7 +31,7 @@ const chartBase: ChartBase = {
};
const getGcRateChart = (
- setup: Setup & SetupTimeRange & SetupUIFilters,
+ setup: Setup & SetupTimeRange,
serviceName: string,
serviceNodeName?: string
) => {
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts
index 0b9d6240fc1c9..f21d3d8e7c056 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts
@@ -7,11 +7,7 @@
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { i18n } from '@kbn/i18n';
import { METRIC_JAVA_GC_TIME } from '../../../../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../../helpers/setup_request';
import { fetchAndTransformGcMetrics } from './fetch_and_transform_gc_metrics';
import { ChartBase } from '../../../types';
@@ -35,7 +31,7 @@ const chartBase: ChartBase = {
};
const getGcTimeChart = (
- setup: Setup & SetupTimeRange & SetupUIFilters,
+ setup: Setup & SetupTimeRange,
serviceName: string,
serviceNodeName?: string
) => {
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts
index ba3183c0fa7d7..eb79897f9f055 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts
@@ -12,11 +12,7 @@ import {
METRIC_JAVA_HEAP_MEMORY_USED,
AGENT_NAME,
} from '../../../../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../../helpers/setup_request';
import { fetchAndTransformMetrics } from '../../../fetch_and_transform_metrics';
import { ChartBase } from '../../../types';
@@ -55,7 +51,7 @@ const chartBase: ChartBase = {
};
export async function getHeapMemoryChart(
- setup: Setup & SetupTimeRange & SetupUIFilters,
+ setup: Setup & SetupTimeRange,
serviceName: string,
serviceNodeName?: string
) {
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts
index 21caab6590fc4..d4084701f0f49 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts
@@ -5,11 +5,7 @@
*/
import { getHeapMemoryChart } from './heap_memory';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../helpers/setup_request';
import { getNonHeapMemoryChart } from './non_heap_memory';
import { getThreadCountChart } from './thread_count';
import { getCPUChartData } from '../shared/cpu';
@@ -18,7 +14,7 @@ import { getGcRateChart } from './gc/get_gc_rate_chart';
import { getGcTimeChart } from './gc/get_gc_time_chart';
export async function getJavaMetricsCharts(
- setup: Setup & SetupTimeRange & SetupUIFilters,
+ setup: Setup & SetupTimeRange,
serviceName: string,
serviceNodeName?: string
) {
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts
index 1a2d5bd0b0e68..50cc449da3c15 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts
@@ -12,11 +12,7 @@ import {
METRIC_JAVA_NON_HEAP_MEMORY_USED,
AGENT_NAME,
} from '../../../../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../../helpers/setup_request';
import { ChartBase } from '../../../types';
import { fetchAndTransformMetrics } from '../../../fetch_and_transform_metrics';
@@ -52,7 +48,7 @@ const chartBase: ChartBase = {
};
export async function getNonHeapMemoryChart(
- setup: Setup & SetupUIFilters & SetupTimeRange,
+ setup: Setup & SetupTimeRange,
serviceName: string,
serviceNodeName?: string
) {
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts
index 01cc6d8495244..0062f0a423970 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts
@@ -10,11 +10,7 @@ import {
METRIC_JAVA_THREAD_COUNT,
AGENT_NAME,
} from '../../../../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../../helpers/setup_request';
import { ChartBase } from '../../../types';
import { fetchAndTransformMetrics } from '../../../fetch_and_transform_metrics';
@@ -44,7 +40,7 @@ const chartBase: ChartBase = {
};
export async function getThreadCountChart(
- setup: Setup & SetupTimeRange & SetupUIFilters,
+ setup: Setup & SetupTimeRange,
serviceName: string,
serviceNodeName?: string
) {
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts
index 066ef40b4ab6c..ca642aa12fff1 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts
@@ -10,11 +10,7 @@ import {
METRIC_SYSTEM_CPU_PERCENT,
METRIC_PROCESS_CPU_PERCENT,
} from '../../../../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../../helpers/setup_request';
import { ChartBase } from '../../../types';
import { fetchAndTransformMetrics } from '../../../fetch_and_transform_metrics';
@@ -56,7 +52,7 @@ const chartBase: ChartBase = {
};
export async function getCPUChartData(
- setup: Setup & SetupTimeRange & SetupUIFilters,
+ setup: Setup & SetupTimeRange,
serviceName: string,
serviceNodeName?: string
) {
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts
index a60576ca0c175..e6ee47cc815ef 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts
@@ -11,11 +11,7 @@ import {
METRIC_SYSTEM_FREE_MEMORY,
METRIC_SYSTEM_TOTAL_MEMORY,
} from '../../../../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../../helpers/setup_request';
import { fetchAndTransformMetrics } from '../../../fetch_and_transform_metrics';
import { ChartBase } from '../../../types';
@@ -54,7 +50,7 @@ export const percentCgroupMemoryUsedScript = {
lang: 'painless',
source: `
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -73,7 +69,7 @@ export const percentCgroupMemoryUsedScript = {
};
export async function getMemoryChartData(
- setup: Setup & SetupTimeRange & SetupUIFilters,
+ setup: Setup & SetupTimeRange,
serviceName: string,
serviceNodeName?: string
) {
diff --git a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
index a42a10d6518a0..3ccba8c7586dc 100644
--- a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
@@ -5,11 +5,7 @@
*/
import { Unionize, Overwrite } from 'utility-types';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getMetricsDateHistogramParams } from '../helpers/metrics';
import { ChartBase } from './types';
import { transformDataToMetricsChart } from './transform_metrics_chart';
@@ -58,7 +54,7 @@ export async function fetchAndTransformMetrics({
aggs,
additionalFilters = [],
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
chartBase: ChartBase;
diff --git a/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts b/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts
index 059e1ce48c83d..72cd65deebff6 100644
--- a/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts
@@ -3,11 +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 {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getJavaMetricsCharts } from './by_agent/java';
import { getDefaultMetricsCharts } from './by_agent/default';
import { GenericMetricsChart } from './transform_metrics_chart';
@@ -22,7 +18,7 @@ export async function getMetricsChartDataByAgent({
serviceNodeName,
agentName,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
agentName: string;
diff --git a/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap
index dcafe09221164..1fafa08082443 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap
@@ -61,7 +61,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -151,7 +151,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -230,7 +230,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -500,7 +500,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -552,7 +552,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -667,7 +667,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -723,7 +723,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_client_metrics.ts b/x-pack/plugins/apm/server/lib/rum_client/get_client_metrics.ts
index 6566ea4f5e29b..6d596246d6af9 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_client_metrics.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_client_metrics.ts
@@ -7,11 +7,7 @@
import { TRANSACTION_DURATION } from '../../../common/elasticsearch_fieldnames';
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import { mergeProjection } from '../../projections/util/merge_projection';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import {
TRANSACTION_DOM_INTERACTIVE,
TRANSACTION_TIME_TO_FIRST_BYTE,
@@ -22,7 +18,7 @@ export async function getClientMetrics({
urlQuery,
percentile = 50,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
urlQuery?: string;
percentile?: number;
}) {
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_js_errors.ts b/x-pack/plugins/apm/server/lib/rum_client/get_js_errors.ts
index 0540ea4bf09dd..a8a4e2714c86e 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_js_errors.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_js_errors.ts
@@ -5,11 +5,7 @@
*/
import { mergeProjection } from '../../projections/util/merge_projection';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getRumErrorsProjection } from '../../projections/rum_page_load_transactions';
import {
ERROR_EXC_MESSAGE,
@@ -23,7 +19,7 @@ export async function getJSErrors({
pageSize,
pageIndex,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
pageSize: number;
pageIndex: number;
}) {
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts b/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts
index c2c86ae05d57c..dfb31de8f10f7 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts
@@ -6,11 +6,7 @@
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import { mergeProjection } from '../../projections/util/merge_projection';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
const LONG_TASK_SUM_FIELD = 'transaction.experience.longtask.sum';
const LONG_TASK_COUNT_FIELD = 'transaction.experience.longtask.count';
@@ -21,7 +17,7 @@ export async function getLongTaskMetrics({
urlQuery,
percentile = 50,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
urlQuery?: string;
percentile?: number;
}) {
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts b/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts
index 5f666feb8a18f..225afff2818ab 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts
@@ -7,11 +7,7 @@
import { TRANSACTION_DURATION } from '../../../common/elasticsearch_fieldnames';
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import { mergeProjection } from '../../projections/util/merge_projection';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
export const MICRO_TO_SEC = 1000000;
@@ -56,7 +52,7 @@ export async function getPageLoadDistribution({
maxPercentile,
urlQuery,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
minPercentile?: string;
maxPercentile?: string;
urlQuery?: string;
@@ -168,7 +164,7 @@ const getPercentilesDistribution = async ({
minDuration,
maxDuration,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
minDuration: number;
maxDuration: number;
}) => {
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts b/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts
index 40f8a8bc58a54..c1a602c33feae 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts
@@ -5,11 +5,7 @@
*/
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import { mergeProjection } from '../../projections/util/merge_projection';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { BreakdownItem } from '../../../typings/ui_filters';
export async function getPageViewTrends({
@@ -17,7 +13,7 @@ export async function getPageViewTrends({
breakdowns,
urlQuery,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
breakdowns?: string;
urlQuery?: string;
}) {
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts b/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts
index bebf9c0bc99c9..e2ec59d232b21 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts
@@ -7,11 +7,7 @@
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import { ProcessorEvent } from '../../../common/processor_event';
import { mergeProjection } from '../../projections/util/merge_projection';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import {
CLIENT_GEO_COUNTRY_ISO_CODE,
USER_AGENT_DEVICE,
@@ -46,7 +42,7 @@ export const getPageLoadDistBreakdown = async ({
breakdown,
urlQuery,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
minPercentile: number;
maxPercentile: number;
breakdown: string;
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_rum_services.ts b/x-pack/plugins/apm/server/lib/rum_client/get_rum_services.ts
index 3adad0868ed4b..e9bd203e354cb 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_rum_services.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_rum_services.ts
@@ -5,18 +5,14 @@
*/
import { SERVICE_NAME } from '../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import { mergeProjection } from '../../projections/util/merge_projection';
export async function getRumServices({
setup,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
}) {
const projection = getRumPageLoadTransactionsProjection({
setup,
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_url_search.ts b/x-pack/plugins/apm/server/lib/rum_client/get_url_search.ts
index 6aa39c7ef961f..febfd66897e18 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_url_search.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_url_search.ts
@@ -5,11 +5,7 @@
*/
import { mergeProjection } from '../../projections/util/merge_projection';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import {
TRANSACTION_DURATION,
@@ -21,7 +17,7 @@ export async function getUrlSearch({
urlQuery,
percentile,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
urlQuery?: string;
percentile: number;
}) {
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts b/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts
index 52d089e4e29c9..6350bc2c07016 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts
@@ -6,11 +6,7 @@
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import { mergeProjection } from '../../projections/util/merge_projection';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import {
USER_AGENT_NAME,
USER_AGENT_OS,
@@ -20,7 +16,7 @@ export async function getVisitorBreakdown({
setup,
urlQuery,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
urlQuery?: string;
}) {
const projection = getRumPageLoadTransactionsProjection({
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts b/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts
index 676b3506397a7..c5baf0b529eb4 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts
@@ -6,11 +6,7 @@
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import { mergeProjection } from '../../projections/util/merge_projection';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import {
CLS_FIELD,
FCP_FIELD,
@@ -25,7 +21,7 @@ export async function getWebCoreVitals({
urlQuery,
percentile = 50,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
urlQuery?: string;
percentile?: number;
}) {
diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts
index 75acebe7ed56c..330bb936c9e88 100644
--- a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts
@@ -82,7 +82,7 @@ async function getServicesData(options: IEnvOptions) {
const { setup, searchAggregatedTransactions } = options;
const projection = getServicesProjection({
- setup: { ...setup, uiFiltersES: [] },
+ setup: { ...setup, esFilter: [] },
searchAggregatedTransactions,
});
diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts
index 7af1607697ef3..eb2ddbf38b274 100644
--- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts
@@ -19,11 +19,10 @@ describe('getServiceMapServiceNodeInfo', () => {
}),
},
indices: {},
+ uiFilters: { environment: 'test environment' },
} as unknown) as Setup & SetupTimeRange;
- const environment = 'test environment';
const serviceName = 'test service name';
const result = await getServiceMapServiceNodeInfo({
- uiFilters: { environment },
setup,
serviceName,
searchAggregatedTransactions: false,
@@ -67,11 +66,10 @@ describe('getServiceMapServiceNodeInfo', () => {
config: {
'xpack.apm.metricsInterval': 30,
},
+ uiFilters: { environment: 'test environment' },
} as unknown) as Setup & SetupTimeRange;
- const environment = 'test environment';
const serviceName = 'test service name';
const result = await getServiceMapServiceNodeInfo({
- uiFilters: { environment },
setup,
serviceName,
searchAggregatedTransactions: false,
diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
index 7c2137ce65d83..37b34641435fb 100644
--- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
@@ -8,7 +8,6 @@ import {
TRANSACTION_REQUEST,
TRANSACTION_PAGE_LOAD,
} from '../../../common/transaction_types';
-import { UIFilters } from '../../../typings/ui_filters';
import {
SERVICE_NAME,
METRIC_SYSTEM_CPU_PERCENT,
@@ -53,9 +52,8 @@ export async function getServiceMapServiceNodeInfo({
serviceName,
setup,
searchAggregatedTransactions,
- uiFilters,
-}: Options & { serviceName: string; uiFilters: UIFilters }) {
- const { start, end } = setup;
+}: Options & { serviceName: string }) {
+ const { start, end, uiFilters } = setup;
const filter: ESFilter[] = [
{ range: rangeFilter(start, end) },
@@ -105,7 +103,8 @@ async function getErrorStats({
}) {
const setupWithBlankUiFilters = {
...setup,
- uiFiltersES: getEnvironmentUiFilterES(environment),
+ uiFilters: { environment },
+ esFilter: getEnvironmentUiFilterES(environment),
};
const { noHits, average } = await getErrorRate({
setup: setupWithBlankUiFilters,
diff --git a/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_grouped.json b/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_grouped.json
index e7bba585de180..94c508fe90230 100644
--- a/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_grouped.json
+++ b/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_grouped.json
@@ -3,7 +3,7 @@
{
"data": {
"id": "opbeans-rum",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-rum",
"agent.name": "rum-js"
}
@@ -18,7 +18,7 @@
{
"data": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
}
diff --git a/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_pregrouped.json b/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_pregrouped.json
index 22c5c50de7472..58469f607ac13 100644
--- a/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_pregrouped.json
+++ b/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_pregrouped.json
@@ -3,7 +3,7 @@
{
"data": {
"id": "opbeans-rum",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-rum",
"agent.name": "rum-js"
}
@@ -18,7 +18,7 @@
{
"data": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
}
diff --git a/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap
index 87aca0d056909..d83e558775be4 100644
--- a/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap
@@ -51,7 +51,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -119,7 +119,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -188,7 +188,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
diff --git a/x-pack/plugins/apm/server/lib/service_nodes/index.ts b/x-pack/plugins/apm/server/lib/service_nodes/index.ts
index a83aba192dba9..d5e29532e3d7b 100644
--- a/x-pack/plugins/apm/server/lib/service_nodes/index.ts
+++ b/x-pack/plugins/apm/server/lib/service_nodes/index.ts
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getServiceNodesProjection } from '../../projections/service_nodes';
import { mergeProjection } from '../../projections/util/merge_projection';
import { SERVICE_NODE_NAME_MISSING } from '../../../common/service_nodes';
@@ -23,7 +19,7 @@ const getServiceNodes = async ({
setup,
serviceName,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
serviceName: string;
}) => {
const { apmEventClient } = setup;
diff --git a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap
index 431f11066aaff..3a38f80c87b35 100644
--- a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap
@@ -144,7 +144,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -194,7 +194,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -257,7 +257,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -334,7 +334,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -389,7 +389,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts
index fca472b0ce8c2..d6ba9f5447ba5 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import {
HOST_NAME,
CONTAINER_ID,
@@ -24,7 +20,7 @@ export async function getServiceNodeMetadata({
}: {
serviceName: string;
serviceNodeName: string;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
}) {
const { apmEventClient } = setup;
diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts
index c09be7aacc784..092485c46fb08 100644
--- a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts
@@ -5,11 +5,7 @@
*/
import { joinByKey } from '../../../../common/utils/join_by_key';
import { PromiseReturnType } from '../../../../typings/common';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { getServicesProjection } from '../../../projections/services';
import {
getTransactionDurationAverages,
@@ -21,17 +17,15 @@ import {
} from './get_services_items_stats';
export type ServiceListAPIResponse = PromiseReturnType;
-export type ServicesItemsSetup = Setup & SetupTimeRange & SetupUIFilters;
+export type ServicesItemsSetup = Setup & SetupTimeRange;
export type ServicesItemsProjection = ReturnType;
export async function getServicesItems({
setup,
searchAggregatedTransactions,
- mlAnomaliesEnvironment,
}: {
setup: ServicesItemsSetup;
searchAggregatedTransactions: boolean;
- mlAnomaliesEnvironment?: string;
}) {
const params = {
projection: getServicesProjection({
@@ -55,7 +49,7 @@ export async function getServicesItems({
getTransactionRates(params),
getTransactionErrorRates(params),
getEnvironments(params),
- getHealthStatuses(params, mlAnomaliesEnvironment),
+ getHealthStatuses(params, setup.uiFilters.environment),
]);
const allMetrics = [
diff --git a/x-pack/plugins/apm/server/lib/services/get_services/index.ts b/x-pack/plugins/apm/server/lib/services/get_services/index.ts
index 351457b2a815e..04744a9c791bb 100644
--- a/x-pack/plugins/apm/server/lib/services/get_services/index.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_services/index.ts
@@ -6,11 +6,7 @@
import { isEmpty } from 'lodash';
import { PromiseReturnType } from '../../../../typings/common';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { hasHistoricalAgentData } from './has_historical_agent_data';
import { getLegacyDataStatus } from './get_legacy_data_status';
import { getServicesItems } from './get_services_items';
@@ -20,17 +16,14 @@ export type ServiceListAPIResponse = PromiseReturnType;
export async function getServices({
setup,
searchAggregatedTransactions,
- mlAnomaliesEnvironment,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
- mlAnomaliesEnvironment?: string;
}) {
const [items, hasLegacyData] = await Promise.all([
getServicesItems({
setup,
searchAggregatedTransactions,
- mlAnomaliesEnvironment,
}),
getLegacyDataStatus(setup),
]);
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
index bd6cefa793467..c678e7db711b6 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
@@ -61,7 +61,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -128,7 +128,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -195,7 +195,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -270,7 +270,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -325,7 +325,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -380,7 +380,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -441,7 +441,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts
index 5d581149db667..0a4d9748f2597 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts
@@ -15,11 +15,7 @@ import { getTransactionGroupsProjection } from '../../projections/transaction_gr
import { mergeProjection } from '../../projections/util/merge_projection';
import { PromiseReturnType } from '../../../../observability/typings/common';
import { AggregationOptionsByType } from '../../../typings/elasticsearch/aggregations';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import {
getAverages,
getSums,
@@ -57,7 +53,7 @@ export type TransactionGroupRequestBase = ReturnType<
};
};
-export type TransactionGroupSetup = Setup & SetupTimeRange & SetupUIFilters;
+export type TransactionGroupSetup = Setup & SetupTimeRange;
function getItemsWithRelativeImpact(
setup: TransactionGroupSetup,
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts
index 3dc126c45d328..d5289430b2698 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts
@@ -12,11 +12,7 @@ import {
EVENT_OUTCOME,
} from '../../../common/elasticsearch_fieldnames';
import { rangeFilter } from '../../../common/utils/range_filter';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getBucketSize } from '../helpers/get_bucket_size';
import {
getProcessorEventForAggregatedTransactions,
@@ -33,10 +29,10 @@ export async function getErrorRate({
serviceName: string;
transactionType?: string;
transactionName?: string;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
}) {
- const { start, end, uiFiltersES, apmEventClient } = setup;
+ const { start, end, esFilter, apmEventClient } = setup;
const transactionNamefilter = transactionName
? [{ term: { [TRANSACTION_NAME]: transactionName } }]
@@ -53,7 +49,7 @@ export async function getErrorRate({
},
...transactionNamefilter,
...transactionTypefilter,
- ...uiFiltersES,
+ ...esFilter,
];
const params = {
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_sample_for_group.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_sample_for_group.ts
index 6c9b23b3dc079..7e1aad075fb16 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_sample_for_group.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_sample_for_group.ts
@@ -12,11 +12,7 @@ import {
} from '../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../common/processor_event';
import { rangeFilter } from '../../../common/utils/range_filter';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
export async function getTransactionSampleForGroup({
serviceName,
@@ -25,9 +21,9 @@ export async function getTransactionSampleForGroup({
}: {
serviceName: string;
transactionName: string;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
}) {
- const { apmEventClient, start, end, uiFiltersES } = setup;
+ const { apmEventClient, start, end, esFilter } = setup;
const filter = [
{
@@ -43,7 +39,7 @@ export async function getTransactionSampleForGroup({
[TRANSACTION_NAME]: transactionName,
},
},
- ...uiFiltersES,
+ ...esFilter,
];
const getSampledTransaction = async () => {
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/index.ts b/x-pack/plugins/apm/server/lib/transaction_groups/index.ts
index 6e0d619268d44..3796511029243 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/index.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/index.ts
@@ -4,16 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { transactionGroupsFetcher, Options } from './fetcher';
export async function getTransactionGroupList(
options: Options,
- setup: Setup & SetupTimeRange & SetupUIFilters
+ setup: Setup & SetupTimeRange
) {
const bucketSize = setup.config['xpack.apm.ui.transactionGroupBucketSize'];
return await transactionGroupsFetcher(options, setup, bucketSize);
diff --git a/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap
index c63dfcc0c0ec7..3e0a7317afd70 100644
--- a/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap
@@ -161,7 +161,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -295,7 +295,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -401,7 +401,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -502,7 +502,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -608,7 +608,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -673,7 +673,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
diff --git a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts
index 34863c64f9804..8bbcaebe06513 100644
--- a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts
@@ -36,7 +36,8 @@ function getMockSetup(esResponse: any) {
get: () => 'myIndex',
}
) as APMConfig,
- uiFiltersES: [],
+ uiFilters: {},
+ esFilter: [],
indices: mockIndices,
dynamicIndexPattern: null as any,
};
diff --git a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts
index 9730ddbbf38d7..8febdc898ab97 100644
--- a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts
@@ -16,11 +16,7 @@ import {
TRANSACTION_NAME,
TRANSACTION_BREAKDOWN_COUNT,
} from '../../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { rangeFilter } from '../../../../common/utils/range_filter';
import { getMetricsDateHistogramParams } from '../../helpers/metrics';
import { MAX_KPIS } from './constants';
@@ -32,12 +28,12 @@ export async function getTransactionBreakdown({
transactionName,
transactionType,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
serviceName: string;
transactionName?: string;
transactionType: string;
}) {
- const { uiFiltersES, apmEventClient, start, end, config } = setup;
+ const { esFilter, apmEventClient, start, end, config } = setup;
const subAggs = {
sum_all_self_times: {
@@ -84,7 +80,7 @@ export async function getTransactionBreakdown({
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
{ range: rangeFilter(start, end) },
- ...uiFiltersES,
+ ...esFilter,
];
if (transactionName) {
diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.ts
index 3cf9a54e3fe9b..287c7bc2c47f9 100644
--- a/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.ts
@@ -5,6 +5,7 @@
*/
import { Logger } from 'kibana/server';
+import { ESSearchResponse } from '../../../../../typings/elasticsearch';
import { PromiseReturnType } from '../../../../../../observability/typings/common';
import { Setup, SetupTimeRange } from '../../../helpers/setup_request';
@@ -47,7 +48,7 @@ export async function anomalySeriesFetcher({
filter: [
{ term: { job_id: jobId } },
{ exists: { field: 'bucket_span' } },
- { term: { result_type: 'model_plot' } },
+ { terms: { result_type: ['model_plot', 'record'] } },
{ term: { partition_field_value: serviceName } },
{ term: { by_field_value: transactionType } },
{
@@ -67,7 +68,7 @@ export async function anomalySeriesFetcher({
extended_bounds: { min: newStart, max: end },
},
aggs: {
- anomaly_score: { max: { field: 'anomaly_score' } },
+ anomaly_score: { max: { field: 'record_score' } },
lower: { min: { field: 'model_lower' } },
upper: { max: { field: 'model_upper' } },
},
@@ -77,7 +78,11 @@ export async function anomalySeriesFetcher({
};
try {
- const response = await ml.mlSystem.mlAnomalySearch(params);
+ const response: ESSearchResponse<
+ unknown,
+ typeof params
+ > = (await ml.mlSystem.mlAnomalySearch(params)) as any;
+
return response;
} catch (err) {
const isHttpError = 'statusCode' in err;
diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts
index d8865f0049d35..f11623eaa2dae 100644
--- a/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts
@@ -5,17 +5,13 @@
*/
import { Logger } from 'kibana/server';
import { isNumber } from 'lodash';
+import { ENVIRONMENT_ALL } from '../../../../../common/environment_filter_values';
import { getBucketSize } from '../../../helpers/get_bucket_size';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../helpers/setup_request';
import { anomalySeriesFetcher } from './fetcher';
import { getMlBucketSize } from './get_ml_bucket_size';
import { anomalySeriesTransform } from './transform';
import { getMLJobIds } from '../../../service_map/get_service_anomalies';
-import { UIFilters } from '../../../../../typings/ui_filters';
export async function getAnomalySeries({
serviceName,
@@ -24,15 +20,13 @@ export async function getAnomalySeries({
timeSeriesDates,
setup,
logger,
- uiFilters,
}: {
serviceName: string;
transactionType: string | undefined;
transactionName: string | undefined;
timeSeriesDates: number[];
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
logger: Logger;
- uiFilters: UIFilters;
}) {
// don't fetch anomalies for transaction details page
if (transactionName) {
@@ -44,12 +38,22 @@ export async function getAnomalySeries({
return;
}
+ const { uiFilters, start, end } = setup;
+ const { environment } = uiFilters;
+
+ // don't fetch anomalies when no specific environment is selected
+ if (environment === ENVIRONMENT_ALL.value) {
+ return;
+ }
+
// don't fetch anomalies if unknown uiFilters are applied
const knownFilters = ['environment', 'serviceName'];
- const uiFilterNames = Object.keys(uiFilters);
- if (
- uiFilterNames.some((uiFilterName) => !knownFilters.includes(uiFilterName))
- ) {
+ const hasUnknownFiltersApplied = Object.entries(setup.uiFilters)
+ .filter(([key, value]) => !!value)
+ .map(([key]) => key)
+ .some((uiFilterName) => !knownFilters.includes(uiFilterName));
+
+ if (hasUnknownFiltersApplied) {
return;
}
@@ -64,15 +68,8 @@ export async function getAnomalySeries({
return;
}
- const mlJobIds = await getMLJobIds(
- setup.ml.anomalyDetectors,
- uiFilters.environment
- );
+ const mlJobIds = await getMLJobIds(setup.ml.anomalyDetectors, environment);
- // don't fetch anomalies if there are isn't exaclty 1 ML job match for the given environment
- if (mlJobIds.length !== 1) {
- return;
- }
const jobId = mlJobIds[0];
const mlBucketSize = await getMlBucketSize({ setup, jobId, logger });
@@ -80,7 +77,6 @@ export async function getAnomalySeries({
return;
}
- const { start, end } = setup;
const { intervalString, bucketSize } = getBucketSize(start, end);
const esResponse = await anomalySeriesFetcher({
diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts
index fdbd99bf274d6..75dfae3e7375f 100644
--- a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts
@@ -29,7 +29,10 @@ describe('timeseriesFetcher', () => {
get: () => 'myIndex',
}
) as APMConfig,
- uiFiltersES: [
+ uiFilters: {
+ environment: 'test',
+ },
+ esFilter: [
{
term: { 'service.environment': 'test' },
},
diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts
index 5a3948f577430..e2edbbec63d47 100644
--- a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts
@@ -14,11 +14,7 @@ import {
import { PromiseReturnType } from '../../../../../../observability/typings/common';
import { getBucketSize } from '../../../helpers/get_bucket_size';
import { rangeFilter } from '../../../../../common/utils/range_filter';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../helpers/setup_request';
import {
getProcessorEventForAggregatedTransactions,
getTransactionDurationFieldForAggregatedTransactions,
@@ -36,10 +32,10 @@ export function timeseriesFetcher({
serviceName: string;
transactionType: string | undefined;
transactionName: string | undefined;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
}) {
- const { start, end, uiFiltersES, apmEventClient } = setup;
+ const { start, end, apmEventClient } = setup;
const { intervalString } = getBucketSize(start, end);
const filter: ESFilter[] = [
@@ -48,7 +44,7 @@ export function timeseriesFetcher({
...getDocumentTypeFilterForAggregatedTransactions(
searchAggregatedTransactions
),
- ...uiFiltersES,
+ ...setup.esFilter,
];
if (transactionName) {
diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts
index 81dca447f16ca..c0421005dd06e 100644
--- a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts
@@ -5,11 +5,7 @@
*/
import { getBucketSize } from '../../../helpers/get_bucket_size';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../helpers/setup_request';
import { timeseriesFetcher } from './fetcher';
import { timeseriesTransformer } from './transform';
@@ -17,7 +13,7 @@ export async function getApmTimeseriesData(options: {
serviceName: string;
transactionType: string | undefined;
transactionName: string | undefined;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
}) {
const { start, end } = options.setup;
diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/index.ts b/x-pack/plugins/apm/server/lib/transactions/charts/index.ts
index 43abf0b1a1d33..d8593612c0582 100644
--- a/x-pack/plugins/apm/server/lib/transactions/charts/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/charts/index.ts
@@ -6,15 +6,10 @@
import { Logger } from 'kibana/server';
import { PromiseReturnType } from '../../../../../observability/typings/common';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { getAnomalySeries } from './get_anomaly_data';
import { getApmTimeseriesData } from './get_timeseries_data';
import { ApmTimeSeriesResponse } from './get_timeseries_data/transform';
-import { UIFilters } from '../../../../typings/ui_filters';
function getDates(apmTimeseries: ApmTimeSeriesResponse) {
return apmTimeseries.responseTimes.avg.map((p) => p.x);
@@ -27,10 +22,9 @@ export async function getTransactionCharts(options: {
serviceName: string;
transactionType: string | undefined;
transactionName: string | undefined;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
logger: Logger;
- uiFilters: UIFilters;
}) {
const apmTimeseries = await getApmTimeseriesData(options);
const anomalyTimeseries = await getAnomalySeries({
diff --git a/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts
index 6e2fe34a5f5ef..34d01627a2869 100644
--- a/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts
@@ -17,11 +17,7 @@ import {
TRANSACTION_TYPE,
} from '../../../../../common/elasticsearch_fieldnames';
import { rangeFilter } from '../../../../../common/utils/range_filter';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../helpers/setup_request';
import {
getDocumentTypeFilterForAggregatedTransactions,
getProcessorEventForAggregatedTransactions,
@@ -66,17 +62,17 @@ export async function getBuckets({
traceId: string;
distributionMax: number;
bucketSize: number;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
}) {
- const { start, end, uiFiltersES, apmEventClient } = setup;
+ const { start, end, esFilter, apmEventClient } = setup;
const commonFilters = [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
{ term: { [TRANSACTION_NAME]: transactionName } },
{ range: rangeFilter(start, end) },
- ...uiFiltersES,
+ ...esFilter,
];
async function getSamplesForDistributionBuckets() {
diff --git a/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts
index 24ca2a4a07b68..249b1c4fbb20a 100644
--- a/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts
@@ -9,11 +9,7 @@ import {
TRANSACTION_NAME,
TRANSACTION_TYPE,
} from '../../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import {
getProcessorEventForAggregatedTransactions,
getTransactionDurationFieldForAggregatedTransactions,
@@ -29,10 +25,10 @@ export async function getDistributionMax({
serviceName: string;
transactionName: string;
transactionType: string;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
}) {
- const { start, end, uiFiltersES, apmEventClient } = setup;
+ const { start, end, esFilter, apmEventClient } = setup;
const params = {
apm: {
@@ -59,7 +55,7 @@ export async function getDistributionMax({
},
},
},
- ...uiFiltersES,
+ ...esFilter,
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts
index b9ab36fb08d42..deafc37ee42e2 100644
--- a/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts
@@ -5,11 +5,7 @@
*/
import { PromiseReturnType } from '../../../../../observability/typings/common';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { getBuckets } from './get_buckets';
import { getDistributionMax } from './get_distribution_max';
import { roundToNearestFiveOrTen } from '../../helpers/round_to_nearest_five_or_ten';
@@ -39,7 +35,7 @@ export async function getTransactionDistribution({
transactionType: string;
transactionId: string;
traceId: string;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
}) {
const distributionMax = await getDistributionMax({
diff --git a/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts
index 9aa1a8f4de87f..8958be0819613 100644
--- a/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts
@@ -9,11 +9,7 @@ import {
TRANSACTION_ID,
} from '../../../../common/elasticsearch_fieldnames';
import { rangeFilter } from '../../../../common/utils/range_filter';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { ProcessorEvent } from '../../../../common/processor_event';
export async function getTransaction({
@@ -23,7 +19,7 @@ export async function getTransaction({
}: {
transactionId: string;
traceId: string;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
}) {
const { start, end, apmEventClient } = setup;
diff --git a/x-pack/plugins/apm/server/lib/transactions/queries.test.ts b/x-pack/plugins/apm/server/lib/transactions/queries.test.ts
index 87b8bc7c4ae90..eff9451c9e1cd 100644
--- a/x-pack/plugins/apm/server/lib/transactions/queries.test.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/queries.test.ts
@@ -56,7 +56,6 @@ describe('transaction queries', () => {
setup,
searchAggregatedTransactions: false,
logger: loggerMock.create(),
- uiFilters: {},
})
);
expect(mock.params).toMatchSnapshot();
@@ -71,7 +70,6 @@ describe('transaction queries', () => {
setup,
searchAggregatedTransactions: false,
logger: loggerMock.create(),
- uiFilters: {},
})
);
expect(mock.params).toMatchSnapshot();
@@ -86,7 +84,6 @@ describe('transaction queries', () => {
setup,
searchAggregatedTransactions: false,
logger: loggerMock.create(),
- uiFilters: {},
})
);
diff --git a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap
index 5f38432719280..e7ca65eb740b6 100644
--- a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap
@@ -46,7 +46,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
diff --git a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts
index 10f6e93c1cfc1..9fbdba679b667 100644
--- a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts
+++ b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts
@@ -8,7 +8,7 @@ import { omit } from 'lodash';
import { mergeProjection } from '../../../projections/util/merge_projection';
import { Projection } from '../../../projections/typings';
import { UIFilters } from '../../../../typings/ui_filters';
-import { getUiFiltersES } from '../../helpers/convert_ui_filters/get_ui_filters_es';
+import { getEsFilter } from '../../helpers/convert_ui_filters/get_es_filter';
import { localUIFilters } from './config';
import { LocalUIFilterName } from '../../../../common/ui_filter';
@@ -22,7 +22,7 @@ export const getLocalFilterQuery = ({
localUIFilterName: LocalUIFilterName;
}) => {
const field = localUIFilters[localUIFilterName];
- const filter = getUiFiltersES(omit(uiFilters, field.name));
+ const filter = getEsFilter(omit(uiFilters, field.name));
const bucketCountAggregation = projection.body.aggs
? {
diff --git a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts
index 22fa20e255f6e..f4e8aafc1bcf5 100644
--- a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts
+++ b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts
@@ -15,7 +15,7 @@ describe('local ui filter queries', () => {
let mock: SearchParamsMock;
beforeEach(() => {
- jest.mock('../../helpers/convert_ui_filters/get_ui_filters_es', () => {
+ jest.mock('../../helpers/convert_ui_filters/get_es_filter', () => {
return [];
});
});
diff --git a/x-pack/plugins/apm/server/projections/errors.ts b/x-pack/plugins/apm/server/projections/errors.ts
index 49a0e9f479d26..173dc94a0840c 100644
--- a/x-pack/plugins/apm/server/projections/errors.ts
+++ b/x-pack/plugins/apm/server/projections/errors.ts
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../server/lib/helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request';
import {
SERVICE_NAME,
ERROR_GROUP_ID,
@@ -20,10 +16,10 @@ export function getErrorGroupsProjection({
setup,
serviceName,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
serviceName: string;
}) {
- const { start, end, uiFiltersES } = setup;
+ const { start, end, esFilter } = setup;
return {
apm: {
@@ -35,7 +31,7 @@ export function getErrorGroupsProjection({
filter: [
{ term: { [SERVICE_NAME]: serviceName } },
{ range: rangeFilter(start, end) },
- ...uiFiltersES,
+ ...esFilter,
],
},
},
diff --git a/x-pack/plugins/apm/server/projections/metrics.ts b/x-pack/plugins/apm/server/projections/metrics.ts
index eb80a6bc73248..c3b5db5be6af8 100644
--- a/x-pack/plugins/apm/server/projections/metrics.ts
+++ b/x-pack/plugins/apm/server/projections/metrics.ts
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../server/lib/helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request';
import {
SERVICE_NAME,
SERVICE_NODE_NAME,
@@ -34,17 +30,17 @@ export function getMetricsProjection({
serviceName,
serviceNodeName,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
}) {
- const { start, end, uiFiltersES } = setup;
+ const { start, end, esFilter } = setup;
const filter = [
{ term: { [SERVICE_NAME]: serviceName } },
{ range: rangeFilter(start, end) },
...getServiceNodeNameFilters(serviceNodeName),
- ...uiFiltersES,
+ ...esFilter,
];
return {
diff --git a/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts b/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts
index c27314923f6bd..96ee26c6e65f5 100644
--- a/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts
+++ b/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../server/lib/helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request';
import {
AGENT_NAME,
TRANSACTION_TYPE,
@@ -22,10 +18,10 @@ export function getRumPageLoadTransactionsProjection({
setup,
urlQuery,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
urlQuery?: string;
}) {
- const { start, end, uiFiltersES } = setup;
+ const { start, end, esFilter } = setup;
const bool = {
filter: [
@@ -49,7 +45,7 @@ export function getRumPageLoadTransactionsProjection({
},
]
: []),
- ...uiFiltersES,
+ ...esFilter,
],
};
@@ -68,9 +64,9 @@ export function getRumPageLoadTransactionsProjection({
export function getRumErrorsProjection({
setup,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
}) {
- const { start, end, uiFiltersES } = setup;
+ const { start, end, esFilter: esFilter } = setup;
const bool = {
filter: [
@@ -82,7 +78,7 @@ export function getRumErrorsProjection({
[SERVICE_LANGUAGE_NAME]: 'javascript',
},
},
- ...uiFiltersES,
+ ...esFilter,
],
};
diff --git a/x-pack/plugins/apm/server/projections/service_nodes.ts b/x-pack/plugins/apm/server/projections/service_nodes.ts
index 87fe815a12d0d..ed8d4c7409eda 100644
--- a/x-pack/plugins/apm/server/projections/service_nodes.ts
+++ b/x-pack/plugins/apm/server/projections/service_nodes.ts
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../server/lib/helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request';
import { SERVICE_NODE_NAME } from '../../common/elasticsearch_fieldnames';
import { mergeProjection } from './util/merge_projection';
import { getMetricsProjection } from './metrics';
@@ -18,7 +14,7 @@ export function getServiceNodesProjection({
serviceName,
serviceNodeName,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
}) {
diff --git a/x-pack/plugins/apm/server/projections/services.ts b/x-pack/plugins/apm/server/projections/services.ts
index ba61f72519a23..d912a95546515 100644
--- a/x-pack/plugins/apm/server/projections/services.ts
+++ b/x-pack/plugins/apm/server/projections/services.ts
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupUIFilters,
- SetupTimeRange,
-} from '../../server/lib/helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request';
import { SERVICE_NAME } from '../../common/elasticsearch_fieldnames';
import { rangeFilter } from '../../common/utils/range_filter';
import { ProcessorEvent } from '../../common/processor_event';
@@ -18,10 +14,10 @@ export function getServicesProjection({
setup,
searchAggregatedTransactions,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
}) {
- const { start, end, uiFiltersES } = setup;
+ const { start, end, esFilter } = setup;
return {
apm: {
@@ -37,7 +33,7 @@ export function getServicesProjection({
size: 0,
query: {
bool: {
- filter: [{ range: rangeFilter(start, end) }, ...uiFiltersES],
+ filter: [{ range: rangeFilter(start, end) }, ...esFilter],
},
},
aggs: {
diff --git a/x-pack/plugins/apm/server/projections/transaction_groups.ts b/x-pack/plugins/apm/server/projections/transaction_groups.ts
index 0cc3a7a35d214..2ce720eb12167 100644
--- a/x-pack/plugins/apm/server/projections/transaction_groups.ts
+++ b/x-pack/plugins/apm/server/projections/transaction_groups.ts
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { omit } from 'lodash';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../server/lib/helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request';
import {
TRANSACTION_NAME,
PARENT_ID,
@@ -22,7 +18,7 @@ export function getTransactionGroupsProjection({
setup,
options,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
options: Options;
}) {
const transactionsProjection = getTransactionsProjection({
diff --git a/x-pack/plugins/apm/server/projections/transactions.ts b/x-pack/plugins/apm/server/projections/transactions.ts
index 8e9bb3bf321f6..548e77b5d2cd9 100644
--- a/x-pack/plugins/apm/server/projections/transactions.ts
+++ b/x-pack/plugins/apm/server/projections/transactions.ts
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../server/lib/helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request';
import {
SERVICE_NAME,
TRANSACTION_TYPE,
@@ -27,13 +23,13 @@ export function getTransactionsProjection({
transactionType,
searchAggregatedTransactions,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
serviceName?: string;
transactionName?: string;
transactionType?: string;
searchAggregatedTransactions: boolean;
}) {
- const { start, end, uiFiltersES } = setup;
+ const { start, end, esFilter } = setup;
const transactionNameFilter = transactionName
? [{ term: { [TRANSACTION_NAME]: transactionName } }]
@@ -51,7 +47,7 @@ export function getTransactionsProjection({
...transactionNameFilter,
...transactionTypeFilter,
...serviceNameFilter,
- ...uiFiltersES,
+ ...esFilter,
...getDocumentTypeFilterForAggregatedTransactions(
searchAggregatedTransactions
),
diff --git a/x-pack/plugins/apm/server/routes/service_map.ts b/x-pack/plugins/apm/server/routes/service_map.ts
index 1996d4d4a262d..6e86ececd1bfe 100644
--- a/x-pack/plugins/apm/server/routes/service_map.ts
+++ b/x-pack/plugins/apm/server/routes/service_map.ts
@@ -17,7 +17,6 @@ import { createRoute } from './create_route';
import { rangeRt, uiFiltersRt } from './default_api_types';
import { notifyFeatureUsage } from '../feature';
import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions';
-import { getParsedUiFilters } from '../lib/helpers/convert_ui_filters/get_parsed_ui_filters';
export const serviceMapRoute = createRoute(() => ({
path: '/api/apm/service-map',
@@ -77,24 +76,20 @@ export const serviceMapServiceNodeRoute = createRoute(() => ({
if (!isActivePlatinumLicense(context.licensing.license)) {
throw Boom.forbidden(invalidLicenseMessage);
}
- const logger = context.logger;
const setup = await setupRequest(context, request);
const {
- query: { uiFilters: uiFiltersJson },
path: { serviceName },
} = context.params;
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
);
- const uiFilters = getParsedUiFilters({ uiFilters: uiFiltersJson, logger });
return getServiceMapServiceNodeInfo({
setup,
serviceName,
searchAggregatedTransactions,
- uiFilters,
});
},
}));
diff --git a/x-pack/plugins/apm/server/routes/services.ts b/x-pack/plugins/apm/server/routes/services.ts
index 4bb10f31ba6a1..538ba3926c792 100644
--- a/x-pack/plugins/apm/server/routes/services.ts
+++ b/x-pack/plugins/apm/server/routes/services.ts
@@ -17,7 +17,6 @@ import { uiFiltersRt, rangeRt } from './default_api_types';
import { getServiceAnnotations } from '../lib/services/annotations';
import { dateAsStringRt } from '../../common/runtime_types/date_as_string_rt';
import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions';
-import { getParsedUiFilters } from '../lib/helpers/convert_ui_filters/get_parsed_ui_filters';
export const servicesRoute = createRoute(() => ({
path: '/api/apm/services',
@@ -25,22 +24,13 @@ export const servicesRoute = createRoute(() => ({
query: t.intersection([uiFiltersRt, rangeRt]),
},
handler: async ({ context, request }) => {
- const { environment } = getParsedUiFilters({
- uiFilters: context.params.query.uiFilters,
- logger: context.logger,
- });
-
const setup = await setupRequest(context, request);
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
);
- const services = await getServices({
- setup,
- searchAggregatedTransactions,
- mlAnomaliesEnvironment: environment,
- });
+ const services = await getServices({ setup, searchAggregatedTransactions });
return services;
},
diff --git a/x-pack/plugins/apm/server/routes/transaction_groups.ts b/x-pack/plugins/apm/server/routes/transaction_groups.ts
index dd1335fb2c2a1..18fc73b468cd4 100644
--- a/x-pack/plugins/apm/server/routes/transaction_groups.ts
+++ b/x-pack/plugins/apm/server/routes/transaction_groups.ts
@@ -5,6 +5,7 @@
*/
import * as t from 'io-ts';
+import Boom from 'boom';
import { setupRequest } from '../lib/helpers/setup_request';
import { getTransactionCharts } from '../lib/transactions/charts';
import { getTransactionDistribution } from '../lib/transactions/distribution';
@@ -15,7 +16,6 @@ import { uiFiltersRt, rangeRt } from './default_api_types';
import { getTransactionSampleForGroup } from '../lib/transaction_groups/get_transaction_sample_for_group';
import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions';
import { getErrorRate } from '../lib/transaction_groups/get_error_rate';
-import { getParsedUiFilters } from '../lib/helpers/convert_ui_filters/get_parsed_ui_filters';
export const transactionGroupsRoute = createRoute(() => ({
path: '/api/apm/services/{serviceName}/transaction_groups',
@@ -71,27 +71,28 @@ export const transactionGroupsChartsRoute = createRoute(() => ({
const setup = await setupRequest(context, request);
const logger = context.logger;
const { serviceName } = context.params.path;
- const {
- transactionType,
- transactionName,
- uiFilters: uiFiltersJson,
- } = context.params.query;
+ const { transactionType, transactionName } = context.params.query;
- const uiFilters = getParsedUiFilters({ uiFilters: uiFiltersJson, logger });
+ if (!setup.uiFilters.environment) {
+ throw Boom.badRequest(
+ `environment is a required property of the ?uiFilters JSON for transaction_groups/charts.`
+ );
+ }
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
);
- return getTransactionCharts({
+ const options = {
serviceName,
transactionType,
transactionName,
setup,
searchAggregatedTransactions,
logger,
- uiFilters,
- });
+ };
+
+ return getTransactionCharts(options);
},
}));
diff --git a/x-pack/plugins/apm/server/routes/ui_filters.ts b/x-pack/plugins/apm/server/routes/ui_filters.ts
index 936d460102dce..26fe0118c02ed 100644
--- a/x-pack/plugins/apm/server/routes/ui_filters.ts
+++ b/x-pack/plugins/apm/server/routes/ui_filters.ts
@@ -9,13 +9,12 @@ import { omit } from 'lodash';
import {
setupRequest,
Setup,
- SetupUIFilters,
SetupTimeRange,
} from '../lib/helpers/setup_request';
import { getEnvironments } from '../lib/ui_filters/get_environments';
import { Projection } from '../projections/typings';
import { localUIFilterNames } from '../lib/ui_filters/local_ui_filters/config';
-import { getUiFiltersES } from '../lib/helpers/convert_ui_filters/get_ui_filters_es';
+import { getEsFilter } from '../lib/helpers/convert_ui_filters/get_es_filter';
import { getLocalUIFilters } from '../lib/ui_filters/local_ui_filters';
import { getServicesProjection } from '../projections/services';
import { getTransactionGroupsProjection } from '../projections/transaction_groups';
@@ -97,23 +96,23 @@ function createLocalFiltersRoute<
},
handler: async ({ context, request }) => {
const setup = await setupRequest(context, request);
+ const { uiFilters } = setup;
const { query } = context.params;
- const { uiFilters, filterNames } = query;
- const parsedUiFilters = JSON.parse(uiFilters);
+ const { filterNames } = query;
const projection = await getProjection({
query,
context,
setup: {
...setup,
- uiFiltersES: getUiFiltersES(omit(parsedUiFilters, filterNames)),
+ esFilter: getEsFilter(omit(uiFilters, filterNames)),
},
});
return getLocalUIFilters({
projection,
setup,
- uiFilters: parsedUiFilters,
+ uiFilters,
localFilterNames: filterNames,
});
},
@@ -271,6 +270,6 @@ type GetProjection<
context,
}: {
query: t.TypeOf;
- setup: Setup & SetupUIFilters & SetupTimeRange;
+ setup: Setup & SetupTimeRange;
context: APMRequestHandlerContext;
}) => Promise | TProjection;
diff --git a/x-pack/plugins/apm/server/utils/test_helpers.tsx b/x-pack/plugins/apm/server/utils/test_helpers.tsx
index 98c1436b2b9b8..18b990b35b5a5 100644
--- a/x-pack/plugins/apm/server/utils/test_helpers.tsx
+++ b/x-pack/plugins/apm/server/utils/test_helpers.tsx
@@ -9,6 +9,7 @@ import {
ESSearchRequest,
} from '../../typings/elasticsearch';
import { PromiseReturnType } from '../../typings/common';
+import { UIFilters } from '../../typings/ui_filters';
import { APMConfig } from '..';
interface Options {
@@ -23,7 +24,8 @@ interface MockSetup {
apmEventClient: any;
internalClient: any;
config: APMConfig;
- uiFiltersES: ESFilter[];
+ uiFilters: UIFilters;
+ esFilter: ESFilter[];
indices: {
/* eslint-disable @typescript-eslint/naming-convention */
'apm_oss.sourcemapIndices': string;
@@ -78,7 +80,8 @@ export async function inspectSearchParams(
},
}
) as APMConfig,
- uiFiltersES: [{ term: { 'my.custom.ui.filter': 'foo-bar' } }],
+ uiFilters: { environment: 'test' },
+ esFilter: [{ term: { 'service.environment': 'test' } }],
indices: {
/* eslint-disable @typescript-eslint/naming-convention */
'apm_oss.sourcemapIndices': 'myIndex',
diff --git a/x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_dark.svg b/x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_dark.svg
new file mode 100644
index 0000000000000..3a87f06b7bcc8
--- /dev/null
+++ b/x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_light.svg b/x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_light.svg
new file mode 100644
index 0000000000000..ac5298be17cca
--- /dev/null
+++ b/x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/x-pack/plugins/global_search_bar/public/components/search_bar.test.tsx b/x-pack/plugins/global_search_bar/public/components/search_bar.test.tsx
index 11fbc7931e620..6fad3335c5efc 100644
--- a/x-pack/plugins/global_search_bar/public/components/search_bar.test.tsx
+++ b/x-pack/plugins/global_search_bar/public/components/search_bar.test.tsx
@@ -8,6 +8,7 @@ import React from 'react';
import { wait } from '@testing-library/react';
import { of } from 'rxjs';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
+import { httpServiceMock, uiSettingsServiceMock } from '../../../../../src/core/public/mocks';
import {
GlobalSearchBatchedResults,
GlobalSearchPluginStart,
@@ -47,6 +48,10 @@ const getSearchProps: any = (component: any) => component.find('EuiFieldSearch')
describe('SearchBar', () => {
let searchService: GlobalSearchPluginStart;
let findSpy: jest.SpyInstance;
+ const http = httpServiceMock.createSetupContract({ basePath: '/test' });
+ const basePathUrl = http.basePath.prepend('/plugins/globalSearchBar/assets/');
+ const uiSettings = uiSettingsServiceMock.createStartContract();
+ const darkMode = uiSettings.get('theme:darkMode');
beforeEach(() => {
searchService = globalSearchPluginMock.createStartContract();
@@ -66,7 +71,12 @@ describe('SearchBar', () => {
.mockReturnValueOnce(of(createBatch('Discover', { id: 'My Dashboard', type: 'test' })));
const component = mountWithIntl(
-
+
);
expect(findSpy).toHaveBeenCalledTimes(0);
@@ -85,7 +95,14 @@ describe('SearchBar', () => {
});
it('supports keyboard shortcuts', () => {
- mountWithIntl( );
+ mountWithIntl(
+
+ );
const searchEvent = new KeyboardEvent('keydown', {
key: '/',
diff --git a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx
index 0dde28db0436d..4ca0f8cf81b7b 100644
--- a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx
+++ b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx
@@ -12,6 +12,7 @@ import {
EuiSelectableTemplateSitewideOption,
EuiText,
EuiIcon,
+ EuiImage,
EuiHeaderSectionItemButton,
EuiSelectableMessage,
} from '@elastic/eui';
@@ -27,6 +28,8 @@ import { GlobalSearchPluginStart, GlobalSearchResult } from '../../../global_sea
interface Props {
globalSearch: GlobalSearchPluginStart['find'];
navigateToUrl: ApplicationStart['navigateToUrl'];
+ basePathUrl: string;
+ darkMode: boolean;
}
const clearField = (field: HTMLInputElement) => {
@@ -42,7 +45,7 @@ const clearField = (field: HTMLInputElement) => {
const cleanMeta = (str: string) => (str.charAt(0).toUpperCase() + str.slice(1)).replace(/-/g, ' ');
const blurEvent = new FocusEvent('blur');
-export function SearchBar({ globalSearch, navigateToUrl }: Props) {
+export function SearchBar({ globalSearch, navigateToUrl, basePathUrl, darkMode }: Props) {
const isMounted = useMountedState();
const [searchValue, setSearchValue] = useState('');
const [searchRef, setSearchRef] = useState(null);
@@ -134,6 +137,34 @@ export function SearchBar({ globalSearch, navigateToUrl }: Props) {
}
};
+ const emptyMessage = (
+
+
+
+
+
+
+
+
+
+
+
+ );
+
useEvent('keydown', onKeyDown);
return (
@@ -164,22 +195,8 @@ export function SearchBar({ globalSearch, navigateToUrl }: Props) {
popoverProps={{
repositionOnScroll: true,
}}
- emptyMessage={
-
-
-
-
-
-
-
-
- }
+ emptyMessage={emptyMessage}
+ noMatchesMessage={emptyMessage}
popoverFooter={
{
public start(core: CoreStart, { globalSearch }: GlobalSearchBarPluginStartDeps) {
core.chrome.navControls.registerCenter({
order: 1000,
- mount: (target) => this.mount(target, globalSearch, core.application.navigateToUrl),
+ mount: (target) =>
+ this.mount(
+ target,
+ globalSearch,
+ core.application.navigateToUrl,
+ core.http.basePath.prepend('/plugins/globalSearchBar/assets/'),
+ core.uiSettings.get('theme:darkMode')
+ ),
});
return {};
}
@@ -32,11 +39,18 @@ export class GlobalSearchBarPlugin implements Plugin<{}, {}> {
private mount(
targetDomElement: HTMLElement,
globalSearch: GlobalSearchPluginStart,
- navigateToUrl: ApplicationStart['navigateToUrl']
+ navigateToUrl: ApplicationStart['navigateToUrl'],
+ basePathUrl: string,
+ darkMode: boolean
) {
ReactDOM.render(
-
+
,
targetDomElement
);
diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts
index a9f2671de8259..e36f38add641a 100644
--- a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts
+++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts
@@ -33,11 +33,11 @@ export interface ModuleDescriptor {
partitionField?: string
) => Promise;
cleanUpModule: (spaceId: string, sourceId: string) => Promise;
- validateSetupIndices: (
+ validateSetupIndices?: (
indices: string[],
timestampField: string
) => Promise;
- validateSetupDatasets: (
+ validateSetupDatasets?: (
indices: string[],
timestampField: string,
startTime: number,
diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts b/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts
deleted file mode 100644
index 0dfe3b301f240..0000000000000
--- a/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts
+++ /dev/null
@@ -1,289 +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 { isEqual } from 'lodash';
-import { useCallback, useEffect, useMemo, useState } from 'react';
-import { usePrevious } from 'react-use';
-import {
- combineDatasetFilters,
- DatasetFilter,
- filterDatasetFilter,
- isExampleDataIndex,
-} from '../../../common/infra_ml';
-import {
- AvailableIndex,
- ValidationIndicesError,
- ValidationUIError,
-} from '../../components/logging/log_analysis_setup/initial_configuration_step';
-import { useTrackedPromise } from '../../utils/use_tracked_promise';
-import { ModuleDescriptor, ModuleSourceConfiguration } from './infra_ml_module_types';
-
-type SetupHandler = (
- indices: string[],
- startTime: number | undefined,
- endTime: number | undefined,
- datasetFilter: DatasetFilter
-) => void;
-
-interface AnalysisSetupStateArguments {
- cleanUpAndSetUpModule: SetupHandler;
- moduleDescriptor: ModuleDescriptor;
- setUpModule: SetupHandler;
- sourceConfiguration: ModuleSourceConfiguration;
-}
-
-const fourWeeksInMs = 86400000 * 7 * 4;
-
-export const useAnalysisSetupState = ({
- cleanUpAndSetUpModule,
- moduleDescriptor: { validateSetupDatasets, validateSetupIndices },
- setUpModule,
- sourceConfiguration,
-}: AnalysisSetupStateArguments) => {
- const [startTime, setStartTime] = useState(Date.now() - fourWeeksInMs);
- const [endTime, setEndTime] = useState(undefined);
-
- const isTimeRangeValid = useMemo(
- () => (startTime != null && endTime != null ? startTime < endTime : true),
- [endTime, startTime]
- );
-
- const [validatedIndices, setValidatedIndices] = useState(
- sourceConfiguration.indices.map((indexName) => ({
- name: indexName,
- validity: 'unknown' as const,
- }))
- );
-
- const updateIndicesWithValidationErrors = useCallback(
- (validationErrors: ValidationIndicesError[]) =>
- setValidatedIndices((availableIndices) =>
- availableIndices.map((previousAvailableIndex) => {
- const indexValiationErrors = validationErrors.filter(
- ({ index }) => index === previousAvailableIndex.name
- );
-
- if (indexValiationErrors.length > 0) {
- return {
- validity: 'invalid',
- name: previousAvailableIndex.name,
- errors: indexValiationErrors,
- };
- } else if (previousAvailableIndex.validity === 'valid') {
- return {
- ...previousAvailableIndex,
- validity: 'valid',
- errors: [],
- };
- } else {
- return {
- validity: 'valid',
- name: previousAvailableIndex.name,
- isSelected: !isExampleDataIndex(previousAvailableIndex.name),
- availableDatasets: [],
- datasetFilter: {
- type: 'includeAll' as const,
- },
- };
- }
- })
- ),
- []
- );
-
- const updateIndicesWithAvailableDatasets = useCallback(
- (availableDatasets: Array<{ indexName: string; datasets: string[] }>) =>
- setValidatedIndices((availableIndices) =>
- availableIndices.map((previousAvailableIndex) => {
- if (previousAvailableIndex.validity !== 'valid') {
- return previousAvailableIndex;
- }
-
- const availableDatasetsForIndex = availableDatasets.filter(
- ({ indexName }) => indexName === previousAvailableIndex.name
- );
- const newAvailableDatasets = availableDatasetsForIndex.flatMap(
- ({ datasets }) => datasets
- );
-
- // filter out datasets that have disappeared if this index' datasets were updated
- const newDatasetFilter: DatasetFilter =
- availableDatasetsForIndex.length > 0
- ? filterDatasetFilter(previousAvailableIndex.datasetFilter, (dataset) =>
- newAvailableDatasets.includes(dataset)
- )
- : previousAvailableIndex.datasetFilter;
-
- return {
- ...previousAvailableIndex,
- availableDatasets: newAvailableDatasets,
- datasetFilter: newDatasetFilter,
- };
- })
- ),
- []
- );
-
- const validIndexNames = useMemo(
- () => validatedIndices.filter((index) => index.validity === 'valid').map((index) => index.name),
- [validatedIndices]
- );
-
- const selectedIndexNames = useMemo(
- () =>
- validatedIndices
- .filter((index) => index.validity === 'valid' && index.isSelected)
- .map((i) => i.name),
- [validatedIndices]
- );
-
- const datasetFilter = useMemo(
- () =>
- validatedIndices
- .flatMap((validatedIndex) =>
- validatedIndex.validity === 'valid'
- ? validatedIndex.datasetFilter
- : { type: 'includeAll' as const }
- )
- .reduce(combineDatasetFilters, { type: 'includeAll' as const }),
- [validatedIndices]
- );
-
- const [validateIndicesRequest, validateIndices] = useTrackedPromise(
- {
- cancelPreviousOn: 'resolution',
- createPromise: async () => {
- return await validateSetupIndices(
- sourceConfiguration.indices,
- sourceConfiguration.timestampField
- );
- },
- onResolve: ({ data: { errors } }) => {
- updateIndicesWithValidationErrors(errors);
- },
- onReject: () => {
- setValidatedIndices([]);
- },
- },
- [sourceConfiguration.indices, sourceConfiguration.timestampField]
- );
-
- const [validateDatasetsRequest, validateDatasets] = useTrackedPromise(
- {
- cancelPreviousOn: 'resolution',
- createPromise: async () => {
- if (validIndexNames.length === 0) {
- return { data: { datasets: [] } };
- }
-
- return await validateSetupDatasets(
- validIndexNames,
- sourceConfiguration.timestampField,
- startTime ?? 0,
- endTime ?? Date.now()
- );
- },
- onResolve: ({ data: { datasets } }) => {
- updateIndicesWithAvailableDatasets(datasets);
- },
- },
- [validIndexNames, sourceConfiguration.timestampField, startTime, endTime]
- );
-
- const setUp = useCallback(() => {
- return setUpModule(selectedIndexNames, startTime, endTime, datasetFilter);
- }, [setUpModule, selectedIndexNames, startTime, endTime, datasetFilter]);
-
- const cleanUpAndSetUp = useCallback(() => {
- return cleanUpAndSetUpModule(selectedIndexNames, startTime, endTime, datasetFilter);
- }, [cleanUpAndSetUpModule, selectedIndexNames, startTime, endTime, datasetFilter]);
-
- const isValidating = useMemo(
- () => validateIndicesRequest.state === 'pending' || validateDatasetsRequest.state === 'pending',
- [validateDatasetsRequest.state, validateIndicesRequest.state]
- );
-
- const validationErrors = useMemo(() => {
- if (isValidating) {
- return [];
- }
-
- return [
- // validate request status
- ...(validateIndicesRequest.state === 'rejected' ||
- validateDatasetsRequest.state === 'rejected'
- ? [{ error: 'NETWORK_ERROR' as const }]
- : []),
- // validation request results
- ...validatedIndices.reduce((errors, index) => {
- return index.validity === 'invalid' && selectedIndexNames.includes(index.name)
- ? [...errors, ...index.errors]
- : errors;
- }, []),
- // index count
- ...(selectedIndexNames.length === 0 ? [{ error: 'TOO_FEW_SELECTED_INDICES' as const }] : []),
- // time range
- ...(!isTimeRangeValid ? [{ error: 'INVALID_TIME_RANGE' as const }] : []),
- ];
- }, [
- isValidating,
- validateIndicesRequest.state,
- validateDatasetsRequest.state,
- validatedIndices,
- selectedIndexNames,
- isTimeRangeValid,
- ]);
-
- const prevStartTime = usePrevious(startTime);
- const prevEndTime = usePrevious(endTime);
- const prevValidIndexNames = usePrevious(validIndexNames);
-
- useEffect(() => {
- if (!isTimeRangeValid) {
- return;
- }
-
- validateIndices();
- }, [isTimeRangeValid, validateIndices]);
-
- useEffect(() => {
- if (!isTimeRangeValid) {
- return;
- }
-
- if (
- startTime !== prevStartTime ||
- endTime !== prevEndTime ||
- !isEqual(validIndexNames, prevValidIndexNames)
- ) {
- validateDatasets();
- }
- }, [
- endTime,
- isTimeRangeValid,
- prevEndTime,
- prevStartTime,
- prevValidIndexNames,
- startTime,
- validIndexNames,
- validateDatasets,
- ]);
-
- return {
- cleanUpAndSetUp,
- datasetFilter,
- endTime,
- isValidating,
- selectedIndexNames,
- setEndTime,
- setStartTime,
- setUp,
- startTime,
- validatedIndices,
- setValidatedIndices,
- validationErrors,
- };
-};
diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts
index cec87fb1144e3..7ea87c3d21322 100644
--- a/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts
+++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts
@@ -10,17 +10,27 @@ import { cleanUpJobsAndDatafeeds } from '../../infra_ml_cleanup';
import { callJobsSummaryAPI } from '../../api/ml_get_jobs_summary_api';
import { callGetMlModuleAPI } from '../../api/ml_get_module';
import { callSetupMlModuleAPI } from '../../api/ml_setup_module_api';
-import { callValidateIndicesAPI } from '../../../logs/log_analysis/api/validate_indices';
-import { callValidateDatasetsAPI } from '../../../logs/log_analysis/api/validate_datasets';
import {
metricsHostsJobTypes,
getJobId,
MetricsHostsJobType,
DatasetFilter,
bucketSpan,
- partitionField,
} from '../../../../../common/infra_ml';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import MemoryJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/hosts_memory_usage.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import MemoryDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/datafeed_hosts_memory_usage.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import NetworkInJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/hosts_network_in.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import NetworkInDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/datafeed_hosts_network_in.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import NetworkOutJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/hosts_network_out.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import NetworkOutDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/datafeed_hosts_network_out.json';
+type JobType = 'hosts_memory_usage' | 'hosts_network_in' | 'hosts_network_out';
const moduleId = 'metrics_ui_hosts';
const moduleName = i18n.translate('xpack.infra.ml.metricsModuleName', {
defaultMessage: 'Metrics anomanly detection',
@@ -54,23 +64,68 @@ const setUpModule = async (
end: number | undefined,
datasetFilter: DatasetFilter,
{ spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration,
- pField?: string
+ partitionField?: string
) => {
const indexNamePattern = indices.join(',');
- const jobIds = ['hosts_memory_usage', 'hosts_network_in', 'hosts_network_out'];
- const jobOverrides = jobIds.map((id) => ({
- job_id: id,
- data_description: {
- time_field: timestampField,
- },
- custom_settings: {
- metrics_source_config: {
- indexPattern: indexNamePattern,
- timestampField,
- bucketSpan,
+ const jobIds: JobType[] = ['hosts_memory_usage', 'hosts_network_in', 'hosts_network_out'];
+
+ const jobOverrides = jobIds.map((id) => {
+ const { job: defaultJobConfig } = getDefaultJobConfigs(id);
+
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const analysis_config: any = {
+ ...defaultJobConfig.analysis_config,
+ };
+
+ if (partitionField) {
+ analysis_config.detectors[0].partition_field_name = partitionField;
+ if (analysis_config.influencers.indexOf(partitionField) === -1) {
+ analysis_config.influencers.push(partitionField);
+ }
+ }
+
+ return {
+ job_id: id,
+ data_description: {
+ time_field: timestampField,
+ },
+ analysis_config,
+ custom_settings: {
+ metrics_source_config: {
+ indexPattern: indexNamePattern,
+ timestampField,
+ bucketSpan,
+ },
+ },
+ };
+ });
+
+ const datafeedOverrides = jobIds.map((id) => {
+ const { datafeed: defaultDatafeedConfig } = getDefaultJobConfigs(id);
+
+ if (!partitionField || id === 'hosts_memory_usage') {
+ // Since the host memory usage doesn't have custom aggs, we don't need to do anything to add a partition field
+ return defaultDatafeedConfig;
+ }
+
+ // If we have a partition field, we need to change the aggregation to do a terms agg at the top level
+ const aggregations = {
+ [partitionField]: {
+ terms: {
+ field: partitionField,
+ },
+ aggregations: {
+ ...defaultDatafeedConfig.aggregations,
+ },
},
- },
- }));
+ };
+
+ return {
+ ...defaultDatafeedConfig,
+ job_id: id,
+ aggregations,
+ };
+ });
return callSetupMlModuleAPI(
moduleId,
@@ -80,34 +135,32 @@ const setUpModule = async (
sourceId,
indexNamePattern,
jobOverrides,
- []
+ datafeedOverrides
);
};
-const cleanUpModule = async (spaceId: string, sourceId: string) => {
- return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsHostsJobTypes);
-};
-
-const validateSetupIndices = async (indices: string[], timestampField: string) => {
- return await callValidateIndicesAPI(indices, [
- {
- name: timestampField,
- validTypes: ['date'],
- },
- {
- name: partitionField,
- validTypes: ['keyword'],
- },
- ]);
+const getDefaultJobConfigs = (jobId: JobType): { datafeed: any; job: any } => {
+ switch (jobId) {
+ case 'hosts_memory_usage':
+ return {
+ datafeed: MemoryDatafeed,
+ job: MemoryJob,
+ };
+ case 'hosts_network_in':
+ return {
+ datafeed: NetworkInDatafeed,
+ job: NetworkInJob,
+ };
+ case 'hosts_network_out':
+ return {
+ datafeed: NetworkOutDatafeed,
+ job: NetworkOutJob,
+ };
+ }
};
-const validateSetupDatasets = async (
- indices: string[],
- timestampField: string,
- startTime: number,
- endTime: number
-) => {
- return await callValidateDatasetsAPI(indices, timestampField, startTime, endTime);
+const cleanUpModule = async (spaceId: string, sourceId: string) => {
+ return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsHostsJobTypes);
};
export const metricHostsModule: ModuleDescriptor = {
@@ -121,6 +174,4 @@ export const metricHostsModule: ModuleDescriptor = {
getModuleDefinition,
setUpModule,
cleanUpModule,
- validateSetupDatasets,
- validateSetupIndices,
};
diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts
index cbcff1c307af6..eaf7489c84eb4 100644
--- a/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts
+++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts
@@ -10,17 +10,28 @@ import { cleanUpJobsAndDatafeeds } from '../../infra_ml_cleanup';
import { callJobsSummaryAPI } from '../../api/ml_get_jobs_summary_api';
import { callGetMlModuleAPI } from '../../api/ml_get_module';
import { callSetupMlModuleAPI } from '../../api/ml_setup_module_api';
-import { callValidateIndicesAPI } from '../../../logs/log_analysis/api/validate_indices';
-import { callValidateDatasetsAPI } from '../../../logs/log_analysis/api/validate_datasets';
import {
metricsK8SJobTypes,
getJobId,
MetricK8sJobType,
DatasetFilter,
bucketSpan,
- partitionField,
} from '../../../../../common/infra_ml';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import MemoryJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/k8s_memory_usage.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import MemoryDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/datafeed_k8s_memory_usage.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import NetworkInJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/k8s_network_in.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import NetworkInDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/datafeed_k8s_network_in.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import NetworkOutJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/k8s_network_out.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import NetworkOutDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/datafeed_k8s_network_out.json';
+type JobType = 'k8s_memory_usage' | 'k8s_network_in' | 'k8s_network_out';
+export const DEFAULT_K8S_PARTITION_FIELD = 'kubernetes.namespace';
const moduleId = 'metrics_ui_k8s';
const moduleName = i18n.translate('xpack.infra.ml.metricsModuleName', {
defaultMessage: 'Metrics anomanly detection',
@@ -54,26 +65,72 @@ const setUpModule = async (
end: number | undefined,
datasetFilter: DatasetFilter,
{ spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration,
- pField?: string
+ partitionField?: string
) => {
const indexNamePattern = indices.join(',');
- const jobIds = ['k8s_memory_usage', 'k8s_network_in', 'k8s_network_out'];
- const jobOverrides = jobIds.map((id) => ({
- job_id: id,
- analysis_config: {
- bucket_span: `${bucketSpan}ms`,
- },
- data_description: {
- time_field: timestampField,
- },
- custom_settings: {
- metrics_source_config: {
- indexPattern: indexNamePattern,
- timestampField,
- bucketSpan,
+ const jobIds: JobType[] = ['k8s_memory_usage', 'k8s_network_in', 'k8s_network_out'];
+ const jobOverrides = jobIds.map((id) => {
+ const { job: defaultJobConfig } = getDefaultJobConfigs(id);
+
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const analysis_config: any = {
+ ...defaultJobConfig.analysis_config,
+ };
+
+ if (partitionField) {
+ analysis_config.detectors[0].partition_field_name = partitionField;
+ if (analysis_config.influencers.indexOf(partitionField) === -1) {
+ analysis_config.influencers.push(partitionField);
+ }
+ }
+
+ return {
+ job_id: id,
+ data_description: {
+ time_field: timestampField,
+ },
+ analysis_config,
+ custom_settings: {
+ metrics_source_config: {
+ indexPattern: indexNamePattern,
+ timestampField,
+ bucketSpan,
+ },
+ },
+ };
+ });
+
+ const datafeedOverrides = jobIds.map((id) => {
+ const { datafeed: defaultDatafeedConfig } = getDefaultJobConfigs(id);
+
+ if (!partitionField || id === 'k8s_memory_usage') {
+ // Since the host memory usage doesn't have custom aggs, we don't need to do anything to add a partition field
+ return defaultDatafeedConfig;
+ }
+
+ // Because the ML K8s jobs ship with a default partition field of {kubernetes.namespace}, ignore that agg and wrap it in our own agg.
+ const innerAggregation =
+ defaultDatafeedConfig.aggregations[DEFAULT_K8S_PARTITION_FIELD].aggregations;
+
+ // If we have a partition field, we need to change the aggregation to do a terms agg to partition the data at the top level
+ const aggregations = {
+ [partitionField]: {
+ terms: {
+ field: partitionField,
+ size: 25, // 25 is arbitratry and only used to keep the number of buckets to a managable level in the event that the user choose a high cardinality partition field.
+ },
+ aggregations: {
+ ...innerAggregation,
+ },
},
- },
- }));
+ };
+
+ return {
+ ...defaultDatafeedConfig,
+ job_id: id,
+ aggregations,
+ };
+ });
return callSetupMlModuleAPI(
moduleId,
@@ -83,34 +140,32 @@ const setUpModule = async (
sourceId,
indexNamePattern,
jobOverrides,
- []
+ datafeedOverrides
);
};
-const cleanUpModule = async (spaceId: string, sourceId: string) => {
- return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsK8SJobTypes);
-};
-
-const validateSetupIndices = async (indices: string[], timestampField: string) => {
- return await callValidateIndicesAPI(indices, [
- {
- name: timestampField,
- validTypes: ['date'],
- },
- {
- name: partitionField,
- validTypes: ['keyword'],
- },
- ]);
+const getDefaultJobConfigs = (jobId: JobType): { datafeed: any; job: any } => {
+ switch (jobId) {
+ case 'k8s_memory_usage':
+ return {
+ datafeed: MemoryDatafeed,
+ job: MemoryJob,
+ };
+ case 'k8s_network_in':
+ return {
+ datafeed: NetworkInDatafeed,
+ job: NetworkInJob,
+ };
+ case 'k8s_network_out':
+ return {
+ datafeed: NetworkOutDatafeed,
+ job: NetworkOutJob,
+ };
+ }
};
-const validateSetupDatasets = async (
- indices: string[],
- timestampField: string,
- startTime: number,
- endTime: number
-) => {
- return await callValidateDatasetsAPI(indices, timestampField, startTime, endTime);
+const cleanUpModule = async (spaceId: string, sourceId: string) => {
+ return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsK8SJobTypes);
};
export const metricHostsModule: ModuleDescriptor = {
@@ -124,6 +179,4 @@ export const metricHostsModule: ModuleDescriptor = {
getModuleDefinition,
setUpModule,
cleanUpModule,
- validateSetupDatasets,
- validateSetupIndices,
};
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx
index b063713fa2c97..b5d224910e819 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx
@@ -50,10 +50,10 @@ export const AnomalyDetectionFlyout = () => {
return (
<>
-
+
{showFlyout && (
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx
index 801dff9c4a17a..5b520084ebb74 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx
@@ -5,7 +5,7 @@
*/
import React, { useState, useCallback, useEffect } from 'react';
-import { EuiFlyoutHeader, EuiTitle, EuiFlyoutBody, EuiTabs, EuiTab, EuiSpacer } from '@elastic/eui';
+import { EuiFlyoutHeader, EuiTitle, EuiFlyoutBody, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiText, EuiFlexGroup, EuiFlexItem, EuiCard, EuiIcon } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
@@ -30,7 +30,7 @@ interface Props {
}
export const FlyoutHome = (props: Props) => {
- const [tab, setTab] = useState<'jobs' | 'anomalies'>('jobs');
+ const [tab] = useState<'jobs' | 'anomalies'>('jobs');
const { goToSetup } = props;
const {
fetchJobStatus: fetchHostJobStatus,
@@ -56,18 +56,10 @@ export const FlyoutHome = (props: Props) => {
goToSetup('kubernetes');
}, [goToSetup]);
- const goToJobs = useCallback(() => {
- setTab('jobs');
- }, []);
-
const jobIds = [
...(k8sJobSummaries || []).map((k) => k.id),
...(hostJobSummaries || []).map((h) => h.id),
];
- const anomaliesUrl = useLinkProps({
- app: 'ml',
- pathname: `/explorer?_g=${createResultsUrl(jobIds)}`,
- });
useEffect(() => {
if (hasInfraMLReadCapabilities) {
@@ -105,30 +97,24 @@ export const FlyoutHome = (props: Props) => {
-
-
-
-
-
-
-
-
+
+
{hostJobSummaries.length > 0 && (
<>
0}
hasK8sJobs={k8sJobSummaries.length > 0}
+ jobIds={jobIds}
/>
>
@@ -151,6 +137,7 @@ export const FlyoutHome = (props: Props) => {
interface CalloutProps {
hasHostJobs: boolean;
hasK8sJobs: boolean;
+ jobIds: string[];
}
const JobsEnabledCallout = (props: CalloutProps) => {
let target = '';
@@ -175,8 +162,34 @@ const JobsEnabledCallout = (props: CalloutProps) => {
pathname: '/jobs',
});
+ const anomaliesUrl = useLinkProps({
+ app: 'ml',
+ pathname: `/explorer?_g=${createResultsUrl(props.jobIds)}`,
+ });
+
return (
<>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{
}
iconType="check"
/>
-
-
-
-
>
);
};
@@ -211,30 +217,11 @@ interface CreateJobTab {
const CreateJobTab = (props: CreateJobTab) => {
return (
<>
-
-
-
+ {/* */}
}
// title="Hosts"
title={
@@ -245,7 +232,7 @@ const CreateJobTab = (props: CreateJobTab) => {
}
description={
}
@@ -254,7 +241,7 @@ const CreateJobTab = (props: CreateJobTab) => {
{props.hasHostJobs && (
@@ -262,7 +249,7 @@ const CreateJobTab = (props: CreateJobTab) => {
{!props.hasHostJobs && (
@@ -273,7 +260,7 @@ const CreateJobTab = (props: CreateJobTab) => {
}
title={
{
}
description={
}
@@ -292,7 +279,7 @@ const CreateJobTab = (props: CreateJobTab) => {
{props.hasK8sJobs && (
@@ -300,7 +287,7 @@ const CreateJobTab = (props: CreateJobTab) => {
{!props.hasK8sJobs && (
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx
index 428c002da6383..c327d187f6bc2 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx
@@ -20,6 +20,7 @@ import { useSourceViaHttp } from '../../../../../../containers/source/use_source
import { useMetricK8sModuleContext } from '../../../../../../containers/ml/modules/metrics_k8s/module';
import { useMetricHostsModuleContext } from '../../../../../../containers/ml/modules/metrics_hosts/module';
import { FixedDatePicker } from '../../../../../../components/fixed_datepicker';
+import { DEFAULT_K8S_PARTITION_FIELD } from '../../../../../../containers/ml/modules/metrics_k8s/module_descriptor';
interface Props {
jobType: 'hosts' | 'kubernetes';
@@ -107,7 +108,7 @@ export const JobSetupScreen = (props: Props) => {
useEffect(() => {
if (props.jobType === 'kubernetes') {
- setPartitionField(['kubernetes.namespace']);
+ setPartitionField([DEFAULT_K8S_PARTITION_FIELD]);
}
}, [props.jobType]);
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx
index 2a5798ac6a70c..3993b4ffc02b0 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx
@@ -208,27 +208,22 @@ export function InnerWorkspacePanel({
>
- {expression === null ? (
-
- ) : (
-
- )}
+ {expression === null
+ ? i18n.translate('xpack.lens.editorFrame.emptyWorkspace', {
+ defaultMessage: 'Drop some fields here to start',
+ })
+ : i18n.translate('xpack.lens.editorFrame.emptyWorkspaceSimple', {
+ defaultMessage: 'Drop field here',
+ })}
{expression === null && (
<>
-
+ {i18n.translate('xpack.lens.editorFrame.emptyWorkspaceHeading', {
+ defaultMessage: 'Lens is a new tool for creating visualization',
+ })}
@@ -237,10 +232,9 @@ export function InnerWorkspacePanel({
target="_blank"
external
>
-
+ {i18n.translate('xpack.lens.editorFrame.goToForums', {
+ defaultMessage: 'Make requests and give feedback',
+ })}
diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx
index 3bd6cc73d6320..5fc89d831a961 100644
--- a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx
+++ b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx
@@ -896,7 +896,12 @@ describe('xy_expression', () => {
test('it applies histogram mode to the series for single series', () => {
const { data, args } = sampleArgs();
- const firstLayer: LayerArgs = { ...args.layers[0], seriesType: 'bar', isHistogram: true };
+ const firstLayer: LayerArgs = {
+ ...args.layers[0],
+ accessors: ['b'],
+ seriesType: 'bar',
+ isHistogram: true,
+ };
delete firstLayer.splitAccessor;
const component = shallow(
{
/>
);
expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true);
- expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(true);
+ });
+
+ test('it does not apply histogram mode to more than one bar series for unstacked bar chart', () => {
+ const { data, args } = sampleArgs();
+ const firstLayer: LayerArgs = { ...args.layers[0], seriesType: 'bar', isHistogram: true };
+ delete firstLayer.splitAccessor;
+ const component = shallow(
+
+ );
+ expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(false);
+ expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(false);
+ });
+
+ test('it applies histogram mode to more than one the series for unstacked line/area chart', () => {
+ const { data, args } = sampleArgs();
+ const firstLayer: LayerArgs = { ...args.layers[0], seriesType: 'line', isHistogram: true };
+ delete firstLayer.splitAccessor;
+ const secondLayer: LayerArgs = { ...args.layers[0], seriesType: 'line', isHistogram: true };
+ delete secondLayer.splitAccessor;
+ const component = shallow(
+
+ );
+ expect(component.find(LineSeries).at(0).prop('enableHistogramMode')).toEqual(true);
+ expect(component.find(LineSeries).at(1).prop('enableHistogramMode')).toEqual(true);
});
test('it applies histogram mode to the series for stacked series', () => {
diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx
index f36525a9a0b14..a59739ec78f7f 100644
--- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx
+++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx
@@ -299,6 +299,13 @@ export function XYChart({
yRight: true,
};
+ const filteredBarLayers = filteredLayers.filter((layer) => layer.seriesType.includes('bar'));
+
+ const chartHasMoreThanOneBarSeries =
+ filteredBarLayers.length > 1 ||
+ filteredBarLayers.some((layer) => layer.accessors.length > 1) ||
+ filteredBarLayers.some((layer) => layer.splitAccessor);
+
function calculateMinInterval() {
// check all the tables to see if all of the rows have the same timestamp
// that would mean that chart will draw a single bar
@@ -599,7 +606,12 @@ export function XYChart({
groupId: yAxesConfiguration.find((axisConfiguration) =>
axisConfiguration.series.find((currentSeries) => currentSeries.accessor === accessor)
)?.groupId,
- enableHistogramMode: isHistogram && (seriesType.includes('stacked') || !splitAccessor),
+ enableHistogramMode:
+ isHistogram &&
+ (seriesType.includes('stacked') || !splitAccessor) &&
+ (seriesType.includes('stacked') ||
+ !seriesType.includes('bar') ||
+ !chartHasMoreThanOneBarSeries),
stackMode: seriesType.includes('percentage') ? StackMode.Percentage : undefined,
timeZone,
areaSeriesStyle: {
diff --git a/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts
index f3521cca2e456..16b60492c9b78 100644
--- a/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts
+++ b/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts
@@ -38,15 +38,21 @@ export type VectorSourceRequestMeta = MapFilters & {
applyGlobalQuery: boolean;
fieldNames: string[];
geogridPrecision?: number;
- sourceQuery: MapQuery;
+ sourceQuery?: MapQuery;
sourceMeta: VectorSourceSyncMeta;
};
+export type VectorJoinSourceRequestMeta = MapFilters & {
+ applyGlobalQuery: boolean;
+ fieldNames: string[];
+ sourceQuery: MapQuery;
+};
+
export type VectorStyleRequestMeta = MapFilters & {
dynamicStyleFields: string[];
isTimeAware: boolean;
sourceQuery: MapQuery;
- timeFilters: unknown;
+ timeFilters: TimeRange;
};
export type ESSearchSourceResponseMeta = {
@@ -59,9 +65,12 @@ export type ESSearchSourceResponseMeta = {
};
// Partial because objects are justified downstream in constructors
-export type DataMeta = Partial &
- Partial &
- Partial;
+export type DataMeta = Partial<
+ VectorSourceRequestMeta &
+ VectorJoinSourceRequestMeta &
+ VectorStyleRequestMeta &
+ ESSearchSourceResponseMeta
+>;
type NumericalStyleFieldData = {
avg: number;
diff --git a/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts b/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts
index d064dfb1c4a37..b769b125cf0f8 100644
--- a/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts
+++ b/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts
@@ -17,7 +17,7 @@ export type MapExtent = {
};
export type MapQuery = Query & {
- queryLastTriggeredAt: string;
+ queryLastTriggeredAt?: string;
};
export type MapRefreshConfig = {
diff --git a/x-pack/plugins/maps/common/elasticsearch_util/es_agg_utils.ts b/x-pack/plugins/maps/common/elasticsearch_util/es_agg_utils.ts
index 7828c3cc6410b..f157ffe9f1c80 100644
--- a/x-pack/plugins/maps/common/elasticsearch_util/es_agg_utils.ts
+++ b/x-pack/plugins/maps/common/elasticsearch_util/es_agg_utils.ts
@@ -33,8 +33,10 @@ export function addFieldToDSL(dsl: object, field: IFieldType) {
};
}
+export type BucketProperties = Record;
+
export function extractPropertiesFromBucket(bucket: any, ignoreKeys: string[] = []) {
- const properties: Record = {};
+ const properties: BucketProperties = {};
for (const key in bucket) {
if (ignoreKeys.includes(key) || !bucket.hasOwnProperty(key)) {
continue;
diff --git a/x-pack/plugins/maps/public/actions/data_request_actions.ts b/x-pack/plugins/maps/public/actions/data_request_actions.ts
index 14d8196900506..d7d9259e1539e 100644
--- a/x-pack/plugins/maps/public/actions/data_request_actions.ts
+++ b/x-pack/plugins/maps/public/actions/data_request_actions.ts
@@ -47,7 +47,7 @@ const FIT_TO_BOUNDS_SCALE_FACTOR = 0.1;
export type DataRequestContext = {
startLoading(dataId: string, requestToken: symbol, meta: DataMeta): void;
- stopLoading(dataId: string, requestToken: symbol, data: object, meta: DataMeta): void;
+ stopLoading(dataId: string, requestToken: symbol, data: object, meta?: DataMeta): void;
onLoadError(dataId: string, requestToken: symbol, errorMessage: string): void;
updateSourceData(newData: unknown): void;
isRequestStillActive(dataId: string, requestToken: symbol): boolean;
diff --git a/x-pack/plugins/maps/public/classes/joins/inner_join.d.ts b/x-pack/plugins/maps/public/classes/joins/inner_join.d.ts
index befff0965fb70..3e2ceac4971c4 100644
--- a/x-pack/plugins/maps/public/classes/joins/inner_join.d.ts
+++ b/x-pack/plugins/maps/public/classes/joins/inner_join.d.ts
@@ -4,19 +4,40 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { Feature, GeoJsonProperties } from 'geojson';
import { IESTermSource } from '../sources/es_term_source';
-import { IJoin } from './join';
+import { IJoin, PropertiesMap } from './join';
import { JoinDescriptor } from '../../../common/descriptor_types';
import { ISource } from '../sources/source';
+import { ITooltipProperty } from '../tooltips/tooltip_property';
+import { IField } from '../fields/field';
export class InnerJoin implements IJoin {
constructor(joinDescriptor: JoinDescriptor, leftSource: ISource);
+ destroy: () => void;
+
getRightJoinSource(): IESTermSource;
toDescriptor(): JoinDescriptor;
+ getJoinFields: () => IField[];
+
+ getLeftField: () => IField;
+
+ getIndexPatternIds: () => string[];
+
+ getQueryableIndexPatternIds: () => string[];
+
+ getSourceDataRequestId: () => string;
+
getSourceMetaDataRequestId(): string;
getSourceFormattersDataRequestId(): string;
+
+ getTooltipProperties(properties: GeoJsonProperties): Promise;
+
+ hasCompleteConfig: () => boolean;
+
+ joinPropertiesToFeature: (feature: Feature, propertiesMap?: PropertiesMap) => boolean;
}
diff --git a/x-pack/plugins/maps/public/classes/joins/join.ts b/x-pack/plugins/maps/public/classes/joins/join.ts
index 5bcc4bfdec87e..df6f6f684f4d2 100644
--- a/x-pack/plugins/maps/public/classes/joins/join.ts
+++ b/x-pack/plugins/maps/public/classes/joins/join.ts
@@ -4,15 +4,39 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { Feature, GeoJsonProperties } from 'geojson';
import { IESTermSource } from '../sources/es_term_source';
import { JoinDescriptor } from '../../../common/descriptor_types';
+import { ITooltipProperty } from '../tooltips/tooltip_property';
+import { IField } from '../fields/field';
+import { BucketProperties } from '../../../common/elasticsearch_util';
+
+export type PropertiesMap = Map;
export interface IJoin {
- getRightJoinSource(): IESTermSource;
+ destroy: () => void;
+
+ getRightJoinSource: () => IESTermSource;
+
+ toDescriptor: () => JoinDescriptor;
+
+ getJoinFields: () => IField[];
+
+ getLeftField: () => IField;
+
+ getIndexPatternIds: () => string[];
+
+ getQueryableIndexPatternIds: () => string[];
+
+ getSourceDataRequestId: () => string;
+
+ getSourceMetaDataRequestId: () => string;
+
+ getSourceFormattersDataRequestId: () => string;
- toDescriptor(): JoinDescriptor;
+ getTooltipProperties: (properties: GeoJsonProperties) => Promise;
- getSourceMetaDataRequestId(): string;
+ hasCompleteConfig: () => boolean;
- getSourceFormattersDataRequestId(): string;
+ joinPropertiesToFeature: (feature: Feature, propertiesMap?: PropertiesMap) => boolean;
}
diff --git a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts
index 90e8d25a77958..9b6a67ac28ad0 100644
--- a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts
+++ b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts
@@ -37,7 +37,6 @@ import {
LayerDescriptor,
VectorLayerDescriptor,
} from '../../../../common/descriptor_types';
-import { IStyle } from '../../styles/style';
import { IVectorSource } from '../../sources/vector_source';
const ACTIVE_COUNT_DATA_ID = 'ACTIVE_COUNT_DATA_ID';
@@ -257,7 +256,7 @@ export class BlendedVectorLayer extends VectorLayer implements IVectorLayer {
return clonedDescriptor;
}
- getSource() {
+ getSource(): IVectorSource {
return this._isClustered ? this._clusterSource : this._documentSource;
}
@@ -268,11 +267,11 @@ export class BlendedVectorLayer extends VectorLayer implements IVectorLayer {
return this._documentSource;
}
- getCurrentStyle(): IStyle {
+ getCurrentStyle(): IVectorStyle {
return this._isClustered ? this._clusterStyle : this._documentStyle;
}
- getStyleForEditing(): IStyle {
+ getStyleForEditing(): IVectorStyle {
return this._documentStyle;
}
@@ -281,8 +280,8 @@ export class BlendedVectorLayer extends VectorLayer implements IVectorLayer {
const requestToken = Symbol(`layer-active-count:${this.getId()}`);
const searchFilters = this._getSearchFilters(
syncContext.dataFilters,
- this.getSource() as IVectorSource,
- this.getCurrentStyle() as IVectorStyle
+ this.getSource(),
+ this.getCurrentStyle()
);
const canSkipFetch = await canSkipSourceUpdate({
source: this.getSource(),
diff --git a/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.js b/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.js
index adcc86b9d1546..33e82db79f3cf 100644
--- a/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.js
+++ b/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.js
@@ -31,6 +31,18 @@ export class HeatmapLayer extends VectorLayer {
}
}
+ getStyleForEditing() {
+ return this._style;
+ }
+
+ getStyle() {
+ return this._style;
+ }
+
+ getCurrentStyle() {
+ return this._style;
+ }
+
_getPropKeyOfSelectedMetric() {
const metricfields = this.getSource().getMetricFields();
return metricfields[0].getName();
diff --git a/x-pack/plugins/maps/public/classes/layers/layer.test.ts b/x-pack/plugins/maps/public/classes/layers/layer.test.ts
index 7bc91d71f83e2..76df7c2af840a 100644
--- a/x-pack/plugins/maps/public/classes/layers/layer.test.ts
+++ b/x-pack/plugins/maps/public/classes/layers/layer.test.ts
@@ -7,7 +7,6 @@
import { AbstractLayer } from './layer';
import { ISource } from '../sources/source';
-import { IStyle } from '../styles/style';
import { AGG_TYPE, FIELD_ORIGIN, LAYER_STYLE_TYPE, VECTOR_STYLES } from '../../../common/constants';
import { ESTermSourceDescriptor, VectorStyleDescriptor } from '../../../common/descriptor_types';
import { getDefaultDynamicProperties } from '../styles/vector/vector_style_defaults';
@@ -38,8 +37,6 @@ class MockSource {
}
}
-class MockStyle {}
-
describe('cloneDescriptor', () => {
describe('with joins', () => {
const styleDescriptor = {
@@ -84,7 +81,6 @@ describe('cloneDescriptor', () => {
const layer = new MockLayer({
layerDescriptor,
source: (new MockSource() as unknown) as ISource,
- style: (new MockStyle() as unknown) as IStyle,
});
const clonedDescriptor = await layer.cloneDescriptor();
const clonedStyleProps = (clonedDescriptor.style as VectorStyleDescriptor).properties;
@@ -122,7 +118,6 @@ describe('cloneDescriptor', () => {
const layer = new MockLayer({
layerDescriptor,
source: (new MockSource() as unknown) as ISource,
- style: (new MockStyle() as unknown) as IStyle,
});
const clonedDescriptor = await layer.cloneDescriptor();
const clonedStyleProps = (clonedDescriptor.style as VectorStyleDescriptor).properties;
@@ -165,7 +160,6 @@ describe('isFittable', () => {
const layer = new MockLayer({
layerDescriptor,
source: (new MockSource({ fitToBounds: test.fitToBounds }) as unknown) as ISource,
- style: (new MockStyle() as unknown) as IStyle,
});
expect(await layer.isFittable()).toBe(test.canFit);
});
diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx
index cd720063c6703..d6bd5180375ce 100644
--- a/x-pack/plugins/maps/public/classes/layers/layer.tsx
+++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx
@@ -110,13 +110,11 @@ export type CustomIconAndTooltipContent = {
export interface ILayerArguments {
layerDescriptor: LayerDescriptor;
source: ISource;
- style: IStyle;
}
export class AbstractLayer implements ILayer {
protected readonly _descriptor: LayerDescriptor;
protected readonly _source: ISource;
- protected readonly _style: IStyle;
protected readonly _dataRequests: DataRequest[];
static createDescriptor(options: Partial): LayerDescriptor {
@@ -140,10 +138,9 @@ export class AbstractLayer implements ILayer {
}
}
- constructor({ layerDescriptor, source, style }: ILayerArguments) {
+ constructor({ layerDescriptor, source }: ILayerArguments) {
this._descriptor = AbstractLayer.createDescriptor(layerDescriptor);
this._source = source;
- this._style = style;
if (this._descriptor.__dataRequests) {
this._dataRequests = this._descriptor.__dataRequests.map(
(dataRequest) => new DataRequest(dataRequest)
@@ -257,11 +254,15 @@ export class AbstractLayer implements ILayer {
}
getStyleForEditing(): IStyle {
- return this._style;
+ throw new Error('Should implement AbstractLayer#getStyleForEditing');
}
- getStyle() {
- return this._style;
+ getStyle(): IStyle {
+ throw new Error('Should implement AbstractLayer#getStyle');
+ }
+
+ getCurrentStyle(): IStyle {
+ throw new Error('Should implement AbstractLayer#getCurrentStyle');
}
getLabel(): string {
@@ -412,10 +413,6 @@ export class AbstractLayer implements ILayer {
return this._descriptor.query ? this._descriptor.query : null;
}
- getCurrentStyle(): IStyle {
- return this._style;
- }
-
async getImmutableSourceProperties() {
const source = this.getSource();
return await source.getImmutableProperties();
diff --git a/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js b/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js
index 3e2009c24a2e4..fa60017f0eaf7 100644
--- a/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js
+++ b/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js
@@ -21,7 +21,20 @@ export class TileLayer extends AbstractLayer {
}
constructor({ source, layerDescriptor }) {
- super({ source, layerDescriptor, style: new TileStyle() });
+ super({ source, layerDescriptor });
+ this._style = new TileStyle();
+ }
+
+ getStyleForEditing() {
+ return this._style;
+ }
+
+ getStyle() {
+ return this._style;
+ }
+
+ getCurrentStyle() {
+ return this._style;
}
async syncData({ startLoading, stopLoading, onLoadError, dataFilters }) {
diff --git a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx
index 70bf8ea3883b7..68b9f2931f398 100644
--- a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx
+++ b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx
@@ -5,9 +5,14 @@
*/
import React from 'react';
+import {
+ Map as MbMap,
+ GeoJSONSource as MbGeoJSONSource,
+ VectorSource as MbVectorSource,
+} from 'mapbox-gl';
import { EuiIcon } from '@elastic/eui';
import { Feature } from 'geojson';
-import { VectorStyle } from '../../styles/vector/vector_style';
+import { IVectorStyle, VectorStyle } from '../../styles/vector/vector_style';
import { SOURCE_DATA_REQUEST_ID, LAYER_TYPE } from '../../../../common/constants';
import { VectorLayer, VectorLayerArguments } from '../vector_layer/vector_layer';
import { ITiledSingleLayerVectorSource } from '../../sources/vector_source';
@@ -59,7 +64,7 @@ export class TiledVectorLayer extends VectorLayer {
const searchFilters: VectorSourceRequestMeta = this._getSearchFilters(
dataFilters,
this.getSource(),
- this._style
+ this._style as IVectorStyle
);
const prevDataRequest = this.getSourceDataRequest();
@@ -88,13 +93,12 @@ export class TiledVectorLayer extends VectorLayer {
}
async syncData(syncContext: DataRequestContext) {
- await this._syncSourceStyleMeta(syncContext, this._source, this._style);
- await this._syncSourceFormatters(syncContext, this._source, this._style);
+ await this._syncSourceStyleMeta(syncContext, this._source, this._style as IVectorStyle);
+ await this._syncSourceFormatters(syncContext, this._source, this._style as IVectorStyle);
await this._syncMVTUrlTemplate(syncContext);
}
- _syncSourceBindingWithMb(mbMap: unknown) {
- // @ts-expect-error
+ _syncSourceBindingWithMb(mbMap: MbMap) {
const mbSource = mbMap.getSource(this._getMbSourceId());
if (mbSource) {
return;
@@ -113,7 +117,6 @@ export class TiledVectorLayer extends VectorLayer {
}
const mbSourceId = this._getMbSourceId();
- // @ts-expect-error
mbMap.addSource(mbSourceId, {
type: 'vector',
tiles: [sourceMeta.urlTemplate],
@@ -126,7 +129,7 @@ export class TiledVectorLayer extends VectorLayer {
return this._getMbSourceId() === mbSourceId;
}
- _syncStylePropertiesWithMb(mbMap: unknown) {
+ _syncStylePropertiesWithMb(mbMap: MbMap) {
// @ts-ignore
const mbSource = mbMap.getSource(this._getMbSourceId());
if (!mbSource) {
@@ -146,12 +149,16 @@ export class TiledVectorLayer extends VectorLayer {
this._setMbLinePolygonProperties(mbMap, sourceMeta.layerName);
}
- _requiresPrevSourceCleanup(mbMap: unknown): boolean {
- // @ts-expect-error
- const mbTileSource = mbMap.getSource(this._getMbSourceId());
- if (!mbTileSource) {
+ _requiresPrevSourceCleanup(mbMap: MbMap): boolean {
+ const mbSource = mbMap.getSource(this._getMbSourceId()) as MbVectorSource | MbGeoJSONSource;
+ if (!mbSource) {
return false;
}
+ if (!('tiles' in mbSource)) {
+ // Expected source is not compatible, so remove.
+ return true;
+ }
+ const mbTileSource = mbSource as MbVectorSource;
const dataRequest = this.getSourceDataRequest();
if (!dataRequest) {
@@ -163,13 +170,8 @@ export class TiledVectorLayer extends VectorLayer {
return false;
}
- if (!mbTileSource.tiles) {
- // Expected source is not compatible, so remove.
- return true;
- }
-
const isSourceDifferent =
- mbTileSource.tiles[0] !== tiledSourceMeta.urlTemplate ||
+ mbTileSource.tiles?.[0] !== tiledSourceMeta.urlTemplate ||
mbTileSource.minzoom !== tiledSourceMeta.minSourceZoom ||
mbTileSource.maxzoom !== tiledSourceMeta.maxSourceZoom;
@@ -179,9 +181,8 @@ export class TiledVectorLayer extends VectorLayer {
const layerIds = this.getMbLayerIds();
for (let i = 0; i < layerIds.length; i++) {
- // @ts-expect-error
const mbLayer = mbMap.getLayer(layerIds[i]);
- if (mbLayer && mbLayer.sourceLayer !== tiledSourceMeta.layerName) {
+ if (mbLayer && mbLayer['source-layer'] !== tiledSourceMeta.layerName) {
// If the source-pointer of one of the layers is stale, they will all be stale.
// In this case, all the mb-layers need to be removed and re-added.
return true;
@@ -191,7 +192,7 @@ export class TiledVectorLayer extends VectorLayer {
return false;
}
- syncLayerWithMB(mbMap: unknown) {
+ syncLayerWithMB(mbMap: MbMap) {
this._removeStaleMbSourcesAndLayers(mbMap);
this._syncSourceBindingWithMb(mbMap);
this._syncStylePropertiesWithMb(mbMap);
diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.d.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.d.ts
deleted file mode 100644
index fa614ae87b290..0000000000000
--- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.d.ts
+++ /dev/null
@@ -1,85 +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.
- */
-/* eslint-disable @typescript-eslint/consistent-type-definitions */
-
-import { Feature, GeoJsonProperties } from 'geojson';
-import { AbstractLayer } from '../layer';
-import { IVectorSource } from '../../sources/vector_source';
-import {
- MapFilters,
- VectorLayerDescriptor,
- VectorSourceRequestMeta,
-} from '../../../../common/descriptor_types';
-import { ILayer } from '../layer';
-import { IJoin } from '../../joins/join';
-import { IVectorStyle } from '../../styles/vector/vector_style';
-import { IField } from '../../fields/field';
-import { DataRequestContext } from '../../../actions';
-import { ITooltipProperty } from '../../tooltips/tooltip_property';
-
-export type VectorLayerArguments = {
- source: IVectorSource;
- joins?: IJoin[];
- layerDescriptor: VectorLayerDescriptor;
-};
-
-export interface IVectorLayer extends ILayer {
- getFields(): Promise;
- getStyleEditorFields(): Promise;
- getJoins(): IJoin[];
- getValidJoins(): IJoin[];
- getSource(): IVectorSource;
- getFeatureById(id: string | number): Feature | null;
- getPropertiesForTooltip(properties: GeoJsonProperties): Promise;
- hasJoins(): boolean;
-}
-
-export class VectorLayer extends AbstractLayer implements IVectorLayer {
- static type: string;
-
- protected readonly _style: IVectorStyle;
- static createDescriptor(
- options: Partial,
- mapColors?: string[]
- ): VectorLayerDescriptor;
-
- constructor(options: VectorLayerArguments);
- getLayerTypeIconName(): string;
- getFields(): Promise;
- getStyleEditorFields(): Promise;
- getJoins(): IJoin[];
- getValidJoins(): IJoin[];
- _syncSourceStyleMeta(
- syncContext: DataRequestContext,
- source: IVectorSource,
- style: IVectorStyle
- ): Promise;
- _syncSourceFormatters(
- syncContext: DataRequestContext,
- source: IVectorSource,
- style: IVectorStyle
- ): Promise;
- syncLayerWithMB(mbMap: unknown): void;
- _getSearchFilters(
- dataFilters: MapFilters,
- source: IVectorSource,
- style: IVectorStyle
- ): VectorSourceRequestMeta;
- _syncData(
- syncContext: DataRequestContext,
- source: IVectorSource,
- style: IVectorStyle
- ): Promise;
- ownsMbSourceId(sourceId: string): boolean;
- ownsMbLayerId(sourceId: string): boolean;
- _setMbPointsProperties(mbMap: unknown, mvtSourceLayer?: string): void;
- _setMbLinePolygonProperties(mbMap: unknown, mvtSourceLayer?: string): void;
- getSource(): IVectorSource;
- getFeatureById(id: string | number): Feature | null;
- getPropertiesForTooltip(properties: GeoJsonProperties): Promise;
- hasJoins(): boolean;
- isFittable(): Promise;
-}
diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.js b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx
similarity index 78%
rename from x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.js
rename to x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx
index 27c344b713a60..a2532d4e7b10e 100644
--- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.js
+++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx
@@ -5,8 +5,13 @@
*/
import React from 'react';
+import { Map as MbMap, Layer as MbLayer, GeoJSONSource as MbGeoJSONSource } from 'mapbox-gl';
+import { Feature, FeatureCollection, GeoJsonProperties } from 'geojson';
+import _ from 'lodash';
+import { EuiIcon } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
import { AbstractLayer } from '../layer';
-import { VectorStyle } from '../../styles/vector/vector_style';
+import { IVectorStyle, VectorStyle } from '../../styles/vector/vector_style';
import {
FEATURE_ID_PROPERTY_NAME,
SOURCE_DATA_REQUEST_ID,
@@ -20,11 +25,9 @@ import {
FIELD_ORIGIN,
LAYER_STYLE_TYPE,
KBN_TOO_MANY_FEATURES_IMAGE_ID,
+ FieldFormatter,
} from '../../../../common/constants';
-import _ from 'lodash';
import { JoinTooltipProperty } from '../../tooltips/join_tooltip_property';
-import { EuiIcon } from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
import { DataRequestAbortError } from '../../util/data_request';
import {
canSkipSourceUpdate,
@@ -39,15 +42,66 @@ import {
getPointFilterExpression,
} from '../../util/mb_filter_expressions';
+import {
+ DynamicStylePropertyOptions,
+ MapFilters,
+ MapQuery,
+ VectorLayerDescriptor,
+ VectorSourceRequestMeta,
+ VectorStyleRequestMeta,
+} from '../../../../common/descriptor_types';
+import { IVectorSource } from '../../sources/vector_source';
+import { CustomIconAndTooltipContent, ILayer } from '../layer';
+import { IJoin, PropertiesMap } from '../../joins/join';
+import { IField } from '../../fields/field';
+import { DataRequestContext } from '../../../actions';
+import { ITooltipProperty } from '../../tooltips/tooltip_property';
+import { IDynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property';
+import { IESSource } from '../../sources/es_source';
+
+interface SourceResult {
+ refreshed: boolean;
+ featureCollection?: FeatureCollection;
+}
+
+interface JoinState {
+ dataHasChanged: boolean;
+ join: IJoin;
+ propertiesMap?: PropertiesMap;
+}
+
+export interface VectorLayerArguments {
+ source: IVectorSource;
+ joins?: IJoin[];
+ layerDescriptor: VectorLayerDescriptor;
+}
+
+export interface IVectorLayer extends ILayer {
+ getFields(): Promise;
+ getStyleEditorFields(): Promise;
+ getJoins(): IJoin[];
+ getValidJoins(): IJoin[];
+ getSource(): IVectorSource;
+ getFeatureById(id: string | number): Feature | null;
+ getPropertiesForTooltip(properties: GeoJsonProperties): Promise;
+ hasJoins(): boolean;
+}
+
export class VectorLayer extends AbstractLayer {
static type = LAYER_TYPE.VECTOR;
- static createDescriptor(options, mapColors) {
+ protected readonly _style: IVectorStyle;
+ private readonly _joins: IJoin[];
+
+ static createDescriptor(
+ options: Partial,
+ mapColors?: string[]
+ ): VectorLayerDescriptor {
const layerDescriptor = super.createDescriptor(options);
layerDescriptor.type = VectorLayer.type;
if (!options.style) {
- const styleProperties = VectorStyle.createDefaultStyleProperties(mapColors);
+ const styleProperties = VectorStyle.createDefaultStyleProperties(mapColors ? mapColors : []);
layerDescriptor.style = VectorStyle.createDescriptor(styleProperties);
}
@@ -55,16 +109,31 @@ export class VectorLayer extends AbstractLayer {
layerDescriptor.joins = [];
}
- return layerDescriptor;
+ return layerDescriptor as VectorLayerDescriptor;
}
- constructor({ layerDescriptor, source, joins = [] }) {
- super({ layerDescriptor, source });
+ constructor({ layerDescriptor, source, joins = [] }: VectorLayerArguments) {
+ super({
+ layerDescriptor,
+ source,
+ });
this._joins = joins;
- this._style = new VectorStyle(this._descriptor.style, source, this);
+ this._style = new VectorStyle(layerDescriptor.style, source, this);
+ }
+
+ getSource(): IVectorSource {
+ return super.getSource() as IVectorSource;
+ }
+
+ getStyleForEditing(): IVectorStyle {
+ return this._style;
+ }
+
+ getStyle(): IVectorStyle {
+ return this._style;
}
- getStyle() {
+ getCurrentStyle(): IVectorStyle {
return this._style;
}
@@ -108,7 +177,7 @@ export class VectorLayer extends AbstractLayer {
return true;
}
- getCustomIconAndTooltipContent() {
+ getCustomIconAndTooltipContent(): CustomIconAndTooltipContent {
const featureCollection = this._getSourceFeatureCollection();
const noResultsIcon = ;
@@ -124,7 +193,7 @@ export class VectorLayer extends AbstractLayer {
if (
this.getJoins().length &&
!featureCollection.features.some(
- (feature) => feature.properties[FEATURE_VISIBLE_PROPERTY_NAME]
+ (feature) => feature.properties?.[FEATURE_VISIBLE_PROPERTY_NAME]
)
) {
return {
@@ -141,8 +210,8 @@ export class VectorLayer extends AbstractLayer {
);
return {
icon: this.getCurrentStyle().getIcon(),
- tooltipContent: tooltipContent,
- areResultsTrimmed: areResultsTrimmed,
+ tooltipContent,
+ areResultsTrimmed,
};
}
@@ -158,7 +227,12 @@ export class VectorLayer extends AbstractLayer {
return this.getCurrentStyle().renderLegendDetails();
}
- async getBounds({ startLoading, stopLoading, registerCancelCallback, dataFilters }) {
+ async getBounds({
+ startLoading,
+ stopLoading,
+ registerCancelCallback,
+ dataFilters,
+ }: DataRequestContext) {
const isStaticLayer = !this.getSource().isBoundsAware();
if (isStaticLayer || this.hasJoins()) {
return getFeatureCollectionBounds(this._getSourceFeatureCollection(), this.hasJoins());
@@ -190,7 +264,7 @@ export class VectorLayer extends AbstractLayer {
} finally {
// Use stopLoading callback instead of onLoadError callback.
// Function is loading bounds and not feature data.
- stopLoading(SOURCE_BOUNDS_DATA_REQUEST_ID, requestToken, bounds, boundsFilters);
+ stopLoading(SOURCE_BOUNDS_DATA_REQUEST_ID, requestToken, bounds ? bounds : {}, boundsFilters);
}
return bounds;
}
@@ -205,7 +279,7 @@ export class VectorLayer extends AbstractLayer {
}
_getJoinFields() {
- const joinFields = [];
+ const joinFields: IField[] = [];
this.getValidJoins().forEach((join) => {
const fields = join.getJoinFields();
joinFields.push(...fields);
@@ -219,7 +293,7 @@ export class VectorLayer extends AbstractLayer {
}
async getStyleEditorFields() {
- const sourceFields = await this.getSourceForEditing().getFields();
+ const sourceFields = await (this.getSourceForEditing() as IVectorSource).getFields();
return [...sourceFields, ...this._getJoinFields()];
}
@@ -246,7 +320,7 @@ export class VectorLayer extends AbstractLayer {
onLoadError,
registerCancelCallback,
dataFilters,
- }) {
+ }: { join: IJoin } & DataRequestContext): Promise {
const joinSource = join.getRightJoinSource();
const sourceDataId = join.getSourceDataRequestId();
const requestToken = Symbol(`layer-join-refresh:${this.getId()} - ${sourceDataId}`);
@@ -266,15 +340,15 @@ export class VectorLayer extends AbstractLayer {
if (canSkipFetch) {
return {
dataHasChanged: false,
- join: join,
- propertiesMap: prevDataRequest.getData(),
+ join,
+ propertiesMap: prevDataRequest?.getData() as PropertiesMap,
};
}
try {
startLoading(sourceDataId, requestToken, searchFilters);
const leftSourceName = await this._source.getDisplayName();
- const { propertiesMap } = await joinSource.getPropertiesMap(
+ const propertiesMap = await joinSource.getPropertiesMap(
searchFilters,
leftSourceName,
join.getLeftField().getName(),
@@ -283,8 +357,8 @@ export class VectorLayer extends AbstractLayer {
stopLoading(sourceDataId, requestToken, propertiesMap);
return {
dataHasChanged: true,
- join: join,
- propertiesMap: propertiesMap,
+ join,
+ propertiesMap,
};
} catch (e) {
if (!(e instanceof DataRequestAbortError)) {
@@ -292,13 +366,12 @@ export class VectorLayer extends AbstractLayer {
}
return {
dataHasChanged: true,
- join: join,
- propertiesMap: null,
+ join,
};
}
}
- async _syncJoins(syncContext, style) {
+ async _syncJoins(syncContext: DataRequestContext, style: IVectorStyle) {
const joinSyncs = this.getValidJoins().map(async (join) => {
await this._syncJoinStyleMeta(syncContext, join, style);
await this._syncJoinFormatters(syncContext, join, style);
@@ -308,28 +381,37 @@ export class VectorLayer extends AbstractLayer {
return await Promise.all(joinSyncs);
}
- _getSearchFilters(dataFilters, source, style) {
+ _getSearchFilters(
+ dataFilters: MapFilters,
+ source: IVectorSource,
+ style: IVectorStyle
+ ): VectorSourceRequestMeta {
const fieldNames = [
...source.getFieldNames(),
...(style.getType() === LAYER_STYLE_TYPE.VECTOR ? style.getSourceFieldNames() : []),
...this.getValidJoins().map((join) => join.getLeftField().getName()),
];
+ const sourceQuery = this.getQuery() as MapQuery;
return {
...dataFilters,
fieldNames: _.uniq(fieldNames).sort(),
geogridPrecision: source.getGeoGridPrecision(dataFilters.zoom),
- sourceQuery: this.getQuery(),
+ sourceQuery: sourceQuery ? sourceQuery : undefined,
applyGlobalQuery: source.getApplyGlobalQuery(),
sourceMeta: source.getSyncMeta(),
};
}
- async _performInnerJoins(sourceResult, joinStates, updateSourceData) {
- //should update the store if
- //-- source result was refreshed
- //-- any of the join configurations changed (joinState changed)
- //-- visibility of any of the features has changed
+ async _performInnerJoins(
+ sourceResult: SourceResult,
+ joinStates: JoinState[],
+ updateSourceData: DataRequestContext['updateSourceData']
+ ) {
+ // should update the store if
+ // -- source result was refreshed
+ // -- any of the join configurations changed (joinState changed)
+ // -- visibility of any of the features has changed
let shouldUpdateStore =
sourceResult.refreshed || joinStates.some((joinState) => joinState.dataHasChanged);
@@ -338,8 +420,11 @@ export class VectorLayer extends AbstractLayer {
return;
}
- for (let i = 0; i < sourceResult.featureCollection.features.length; i++) {
- const feature = sourceResult.featureCollection.features[i];
+ for (let i = 0; i < sourceResult.featureCollection!.features.length; i++) {
+ const feature = sourceResult.featureCollection!.features[i];
+ if (!feature.properties) {
+ feature.properties = {};
+ }
const oldVisbility = feature.properties[FEATURE_VISIBLE_PROPERTY_NAME];
let isFeatureVisible = true;
for (let j = 0; j < joinStates.length; j++) {
@@ -364,7 +449,11 @@ export class VectorLayer extends AbstractLayer {
}
}
- async _syncSource(syncContext, source, style) {
+ async _syncSource(
+ syncContext: DataRequestContext,
+ source: IVectorSource,
+ style: IVectorStyle
+ ): Promise {
const {
startLoading,
stopLoading,
@@ -385,7 +474,9 @@ export class VectorLayer extends AbstractLayer {
if (canSkipFetch) {
return {
refreshed: false,
- featureCollection: prevDataRequest.getData(),
+ featureCollection: prevDataRequest
+ ? (prevDataRequest.getData() as FeatureCollection)
+ : EMPTY_FEATURE_COLLECTION,
};
}
@@ -416,15 +507,20 @@ export class VectorLayer extends AbstractLayer {
}
}
- async _syncSourceStyleMeta(syncContext, source, style) {
+ async _syncSourceStyleMeta(
+ syncContext: DataRequestContext,
+ source: IVectorSource,
+ style: IVectorStyle
+ ) {
if (this.getCurrentStyle().getType() !== LAYER_STYLE_TYPE.VECTOR) {
return;
}
+ const sourceQuery = this.getQuery() as MapQuery;
return this._syncStyleMeta({
source,
style,
- sourceQuery: this.getQuery(),
+ sourceQuery: sourceQuery ? sourceQuery : undefined,
dataRequestId: SOURCE_META_DATA_REQUEST_ID,
dynamicStyleProps: style.getDynamicPropertiesArray().filter((dynamicStyleProp) => {
return (
@@ -436,7 +532,7 @@ export class VectorLayer extends AbstractLayer {
});
}
- async _syncJoinStyleMeta(syncContext, join, style) {
+ async _syncJoinStyleMeta(syncContext: DataRequestContext, join: IJoin, style: IVectorStyle) {
const joinSource = join.getRightJoinSource();
return this._syncStyleMeta({
source: joinSource,
@@ -446,9 +542,7 @@ export class VectorLayer extends AbstractLayer {
dynamicStyleProps: this.getCurrentStyle()
.getDynamicPropertiesArray()
.filter((dynamicStyleProp) => {
- const matchingField = joinSource.getMetricFieldForName(
- dynamicStyleProp.getField().getName()
- );
+ const matchingField = joinSource.getMetricFieldForName(dynamicStyleProp.getFieldName());
return (
dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.JOIN &&
!!matchingField &&
@@ -470,13 +564,19 @@ export class VectorLayer extends AbstractLayer {
stopLoading,
onLoadError,
registerCancelCallback,
- }) {
+ }: {
+ dataRequestId: string;
+ dynamicStyleProps: Array>;
+ source: IVectorSource;
+ sourceQuery?: MapQuery;
+ style: IVectorStyle;
+ } & DataRequestContext) {
if (!source.isESSource() || dynamicStyleProps.length === 0) {
return;
}
const dynamicStyleFields = dynamicStyleProps.map((dynamicStyleProp) => {
- return `${dynamicStyleProp.getField().getName()}${dynamicStyleProp.getNumberOfCategories()}`;
+ return `${dynamicStyleProp.getFieldName()}${dynamicStyleProp.getNumberOfCategories()}`;
});
const nextMeta = {
@@ -484,7 +584,7 @@ export class VectorLayer extends AbstractLayer {
sourceQuery,
isTimeAware: this.getCurrentStyle().isTimeAware() && (await source.isTimeAware()),
timeFilters: dataFilters.timeFilters,
- };
+ } as VectorStyleRequestMeta;
const prevDataRequest = this.getDataRequest(dataRequestId);
const canSkipFetch = canSkipStyleMetaUpdate({ prevDataRequest, nextMeta });
if (canSkipFetch) {
@@ -496,14 +596,14 @@ export class VectorLayer extends AbstractLayer {
startLoading(dataRequestId, requestToken, nextMeta);
const layerName = await this.getDisplayName(source);
- //todo: cast source to ESSource when migrating to TS
- const styleMeta = await source.loadStylePropsMeta(
+ const styleMeta = await (source as IESSource).loadStylePropsMeta({
layerName,
style,
dynamicStyleProps,
- registerCancelCallback.bind(null, requestToken),
- nextMeta
- );
+ registerCancelCallback: registerCancelCallback.bind(null, requestToken),
+ sourceQuery: nextMeta.sourceQuery,
+ timeFilters: nextMeta.timeFilters,
+ });
stopLoading(dataRequestId, requestToken, styleMeta, nextMeta);
} catch (error) {
if (!(error instanceof DataRequestAbortError)) {
@@ -512,7 +612,11 @@ export class VectorLayer extends AbstractLayer {
}
}
- async _syncSourceFormatters(syncContext, source, style) {
+ async _syncSourceFormatters(
+ syncContext: DataRequestContext,
+ source: IVectorSource,
+ style: IVectorStyle
+ ) {
if (style.getType() !== LAYER_STYLE_TYPE.VECTOR) {
return;
}
@@ -526,13 +630,13 @@ export class VectorLayer extends AbstractLayer {
return dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.SOURCE;
})
.map((dynamicStyleProp) => {
- return dynamicStyleProp.getField();
+ return dynamicStyleProp.getField()!;
}),
...syncContext,
});
}
- async _syncJoinFormatters(syncContext, join, style) {
+ async _syncJoinFormatters(syncContext: DataRequestContext, join: IJoin, style: IVectorStyle) {
const joinSource = join.getRightJoinSource();
return this._syncFormatters({
source: joinSource,
@@ -540,19 +644,28 @@ export class VectorLayer extends AbstractLayer {
fields: style
.getDynamicPropertiesArray()
.filter((dynamicStyleProp) => {
- const matchingField = joinSource.getMetricFieldForName(
- dynamicStyleProp.getField().getName()
- );
+ const matchingField = joinSource.getMetricFieldForName(dynamicStyleProp.getFieldName());
return dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.JOIN && !!matchingField;
})
.map((dynamicStyleProp) => {
- return dynamicStyleProp.getField();
+ return dynamicStyleProp.getField()!;
}),
...syncContext,
});
}
- async _syncFormatters({ source, dataRequestId, fields, startLoading, stopLoading, onLoadError }) {
+ async _syncFormatters({
+ source,
+ dataRequestId,
+ fields,
+ startLoading,
+ stopLoading,
+ onLoadError,
+ }: {
+ dataRequestId: string;
+ fields: IField[];
+ source: IVectorSource;
+ } & DataRequestContext) {
if (fields.length === 0) {
return;
}
@@ -573,7 +686,7 @@ export class VectorLayer extends AbstractLayer {
try {
startLoading(dataRequestId, requestToken, nextMeta);
- const formatters = {};
+ const formatters: { [key: string]: FieldFormatter | null } = {};
const promises = fields
.filter((field) => {
return field.canValueBeFormatted();
@@ -589,7 +702,7 @@ export class VectorLayer extends AbstractLayer {
}
}
- async syncData(syncContext) {
+ async syncData(syncContext: DataRequestContext) {
await this._syncData(syncContext, this.getSource(), this.getCurrentStyle());
}
@@ -603,7 +716,7 @@ export class VectorLayer extends AbstractLayer {
// Given 1 above, which source/style to use can not be stored in Layer instance state.
// Given 2 above, which source/style to use can not be pulled from data request state.
// Therefore, source and style are provided as arugments and must be used instead of calling getSource or getCurrentStyle.
- async _syncData(syncContext, source, style) {
+ async _syncData(syncContext: DataRequestContext, source: IVectorSource, style: IVectorStyle) {
if (this.isLoadingBounds()) {
return;
}
@@ -624,11 +737,11 @@ export class VectorLayer extends AbstractLayer {
_getSourceFeatureCollection() {
const sourceDataRequest = this.getSourceDataRequest();
- return sourceDataRequest ? sourceDataRequest.getData() : null;
+ return sourceDataRequest ? (sourceDataRequest.getData() as FeatureCollection) : null;
}
- _syncFeatureCollectionWithMb(mbMap) {
- const mbGeoJSONSource = mbMap.getSource(this.getId());
+ _syncFeatureCollectionWithMb(mbMap: MbMap) {
+ const mbGeoJSONSource = mbMap.getSource(this.getId()) as MbGeoJSONSource;
const featureCollection = this._getSourceFeatureCollection();
const featureCollectionOnMap = AbstractLayer.getBoundDataForSource(mbMap, this.getId());
@@ -653,7 +766,7 @@ export class VectorLayer extends AbstractLayer {
}
}
- _setMbPointsProperties(mbMap, mvtSourceLayer) {
+ _setMbPointsProperties(mbMap: MbMap, mvtSourceLayer?: string) {
const pointLayerId = this._getMbPointLayerId();
const symbolLayerId = this._getMbSymbolLayerId();
const pointLayer = mbMap.getLayer(pointLayerId);
@@ -689,12 +802,12 @@ export class VectorLayer extends AbstractLayer {
}
}
- _setMbCircleProperties(mbMap, mvtSourceLayer) {
+ _setMbCircleProperties(mbMap: MbMap, mvtSourceLayer?: string) {
const sourceId = this.getId();
const pointLayerId = this._getMbPointLayerId();
const pointLayer = mbMap.getLayer(pointLayerId);
if (!pointLayer) {
- const mbLayer = {
+ const mbLayer: MbLayer = {
id: pointLayerId,
type: 'circle',
source: sourceId,
@@ -710,7 +823,7 @@ export class VectorLayer extends AbstractLayer {
const textLayerId = this._getMbTextLayerId();
const textLayer = mbMap.getLayer(textLayerId);
if (!textLayer) {
- const mbLayer = {
+ const mbLayer: MbLayer = {
id: textLayerId,
type: 'symbol',
source: sourceId,
@@ -740,13 +853,13 @@ export class VectorLayer extends AbstractLayer {
});
}
- _setMbSymbolProperties(mbMap, mvtSourceLayer) {
+ _setMbSymbolProperties(mbMap: MbMap, mvtSourceLayer?: string) {
const sourceId = this.getId();
const symbolLayerId = this._getMbSymbolLayerId();
const symbolLayer = mbMap.getLayer(symbolLayerId);
if (!symbolLayer) {
- const mbLayer = {
+ const mbLayer: MbLayer = {
id: symbolLayerId,
type: 'symbol',
source: sourceId,
@@ -775,7 +888,7 @@ export class VectorLayer extends AbstractLayer {
});
}
- _setMbLinePolygonProperties(mbMap, mvtSourceLayer) {
+ _setMbLinePolygonProperties(mbMap: MbMap, mvtSourceLayer?: string) {
const sourceId = this.getId();
const fillLayerId = this._getMbPolygonLayerId();
const lineLayerId = this._getMbLineLayerId();
@@ -783,7 +896,7 @@ export class VectorLayer extends AbstractLayer {
const hasJoins = this.hasJoins();
if (!mbMap.getLayer(fillLayerId)) {
- const mbLayer = {
+ const mbLayer: MbLayer = {
id: fillLayerId,
type: 'fill',
source: sourceId,
@@ -795,7 +908,7 @@ export class VectorLayer extends AbstractLayer {
mbMap.addLayer(mbLayer);
}
if (!mbMap.getLayer(lineLayerId)) {
- const mbLayer = {
+ const mbLayer: MbLayer = {
id: lineLayerId,
type: 'line',
source: sourceId,
@@ -807,7 +920,7 @@ export class VectorLayer extends AbstractLayer {
mbMap.addLayer(mbLayer);
}
if (!mbMap.getLayer(tooManyFeaturesLayerId)) {
- const mbLayer = {
+ const mbLayer: MbLayer = {
id: tooManyFeaturesLayerId,
type: 'fill',
source: sourceId,
@@ -855,12 +968,12 @@ export class VectorLayer extends AbstractLayer {
mbMap.setLayerZoomRange(tooManyFeaturesLayerId, this.getMinZoom(), this.getMaxZoom());
}
- _syncStylePropertiesWithMb(mbMap) {
+ _syncStylePropertiesWithMb(mbMap: MbMap) {
this._setMbPointsProperties(mbMap);
this._setMbLinePolygonProperties(mbMap);
}
- _syncSourceBindingWithMb(mbMap) {
+ _syncSourceBindingWithMb(mbMap: MbMap) {
const mbSource = mbMap.getSource(this._getMbSourceId());
if (!mbSource) {
mbMap.addSource(this._getMbSourceId(), {
@@ -883,7 +996,7 @@ export class VectorLayer extends AbstractLayer {
}
}
- syncLayerWithMB(mbMap) {
+ syncLayerWithMB(mbMap: MbMap) {
this._syncSourceBindingWithMb(mbMap);
this._syncFeatureCollectionWithMb(mbMap);
this._syncStylePropertiesWithMb(mbMap);
@@ -924,15 +1037,15 @@ export class VectorLayer extends AbstractLayer {
];
}
- ownsMbLayerId(mbLayerId) {
+ ownsMbLayerId(mbLayerId: string) {
return this.getMbLayerIds().includes(mbLayerId);
}
- ownsMbSourceId(mbSourceId) {
+ ownsMbSourceId(mbSourceId: string) {
return this.getId() === mbSourceId;
}
- _addJoinsToSourceTooltips(tooltipsFromSource) {
+ _addJoinsToSourceTooltips(tooltipsFromSource: ITooltipProperty[]) {
for (let i = 0; i < tooltipsFromSource.length; i++) {
const tooltipProperty = tooltipsFromSource[i];
const matchingJoins = [];
@@ -947,7 +1060,7 @@ export class VectorLayer extends AbstractLayer {
}
}
- async getPropertiesForTooltip(properties) {
+ async getPropertiesForTooltip(properties: GeoJsonProperties) {
const vectorSource = this.getSource();
let allProperties = await vectorSource.getTooltipProperties(properties);
this._addJoinsToSourceTooltips(allProperties);
@@ -961,18 +1074,20 @@ export class VectorLayer extends AbstractLayer {
canShowTooltip() {
return (
- this.isVisible() && (this.getSource().canFormatFeatureProperties() || this.getJoins().length)
+ this.isVisible() &&
+ (this.getSource().canFormatFeatureProperties() || this.getJoins().length > 0)
);
}
- getFeatureById(id) {
+ getFeatureById(id: string | number) {
const featureCollection = this._getSourceFeatureCollection();
if (!featureCollection) {
return null;
}
- return featureCollection.features.find((feature) => {
- return feature.properties[FEATURE_ID_PROPERTY_NAME] === id;
+ const targetFeature = featureCollection.features.find((feature) => {
+ return feature.properties?.[FEATURE_ID_PROPERTY_NAME] === id;
});
+ return targetFeature ? targetFeature : null;
}
}
diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts
index be947d79f4e39..5c062f3419e28 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts
+++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts
@@ -29,7 +29,7 @@ export interface IESAggSource extends IESSource {
getValueAggsDsl(indexPattern: IndexPattern): { [key: string]: unknown };
}
-export class AbstractESAggSource extends AbstractESSource {
+export abstract class AbstractESAggSource extends AbstractESSource {
private readonly _metricFields: IESAggField[];
private readonly _canReadFromGeoJson: boolean;
diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.d.ts b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.d.ts
index ada76b8e4e674..b221d13bb0f8a 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.d.ts
+++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.d.ts
@@ -10,6 +10,7 @@ import {
MapFilters,
MapQuery,
VectorSourceSyncMeta,
+ VectorSourceRequestMeta,
} from '../../../../common/descriptor_types';
import { GRID_RESOLUTION } from '../../../../common/constants';
import { IField } from '../../fields/field';
@@ -35,13 +36,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements ITiledSingle
getLayerName(): string;
getUrlTemplateWithMeta(
- searchFilters: MapFilters & {
- applyGlobalQuery: boolean;
- fieldNames: string[];
- geogridPrecision?: number;
- sourceQuery: MapQuery;
- sourceMeta: VectorSourceSyncMeta;
- }
+ searchFilters: VectorSourceRequestMeta
): Promise<{
layerName: string;
urlTemplate: string;
diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts
index 189e7dea1b0c1..06df68283c434 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts
+++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts
@@ -160,7 +160,8 @@ describe('ESGeoGridSource', () => {
const { data, meta } = await geogridSource.getGeoJsonWithMeta(
'foobarLayer',
vectorSourceRequestMeta,
- () => {}
+ () => {},
+ () => true
);
expect(meta && meta.areResultsTrimmed).toEqual(false);
diff --git a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.d.ts b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.d.ts
index 01fde589dcb84..c11b6f0853cc7 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.d.ts
+++ b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.d.ts
@@ -6,12 +6,14 @@
import { AbstractVectorSource } from '../vector_source';
import { IVectorSource } from '../vector_source';
+import { TimeRange } from '../../../../../../../src/plugins/data/common';
import { IndexPattern, ISearchSource } from '../../../../../../../src/plugins/data/public';
import {
DynamicStylePropertyOptions,
+ MapQuery,
VectorSourceRequestMeta,
} from '../../../../common/descriptor_types';
-import { VectorStyle } from '../../styles/vector/vector_style';
+import { IVectorStyle } from '../../styles/vector/vector_style';
import { IDynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property';
export interface IESSource extends IVectorSource {
@@ -25,13 +27,21 @@ export interface IESSource extends IVectorSource {
limit: number,
initialSearchContext?: object
): Promise;
- loadStylePropsMeta(
- layerName: string,
- style: VectorStyle,
- dynamicStyleProps: Array>,
- registerCancelCallback: (requestToken: symbol, callback: () => void) => void,
- searchFilters: VectorSourceRequestMeta
- ): Promise;
+ loadStylePropsMeta({
+ layerName,
+ style,
+ dynamicStyleProps,
+ registerCancelCallback,
+ sourceQuery,
+ timeFilters,
+ }: {
+ layerName: string;
+ style: IVectorStyle;
+ dynamicStyleProps: Array>;
+ registerCancelCallback: (callback: () => void) => void;
+ sourceQuery?: MapQuery;
+ timeFilters: TimeRange;
+ }): Promise;
}
export class AbstractESSource extends AbstractVectorSource implements IESSource {
@@ -45,13 +55,21 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
limit: number,
initialSearchContext?: object
): Promise;
- loadStylePropsMeta(
- layerName: string,
- style: VectorStyle,
- dynamicStyleProps: Array>,
- registerCancelCallback: (requestToken: symbol, callback: () => void) => void,
- searchFilters: VectorSourceRequestMeta
- ): Promise;
+ loadStylePropsMeta({
+ layerName,
+ style,
+ dynamicStyleProps,
+ registerCancelCallback,
+ sourceQuery,
+ timeFilters,
+ }: {
+ layerName: string;
+ style: IVectorStyle;
+ dynamicStyleProps: Array>;
+ registerCancelCallback: (callback: () => void) => void;
+ sourceQuery?: MapQuery;
+ timeFilters: TimeRange;
+ }): Promise;
_runEsQuery: ({
requestId,
requestName,
diff --git a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js
index ab56ceeab4e77..0c8cb5f514247 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js
+++ b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js
@@ -284,13 +284,14 @@ export class AbstractESSource extends AbstractVectorSource {
return indexPattern.getFormatterForField(fieldFromIndexPattern).getConverterFor('text');
}
- async loadStylePropsMeta(
+ async loadStylePropsMeta({
layerName,
style,
dynamicStyleProps,
registerCancelCallback,
- searchFilters
- ) {
+ sourceQuery,
+ timeFilters,
+ }) {
const promises = dynamicStyleProps.map((dynamicStyleProp) => {
return dynamicStyleProp.getFieldMetaRequest();
});
@@ -307,13 +308,11 @@ export class AbstractESSource extends AbstractVectorSource {
searchSource.setField('index', indexPattern);
searchSource.setField('size', 0);
searchSource.setField('aggs', aggs);
- if (searchFilters.sourceQuery) {
- searchSource.setField('query', searchFilters.sourceQuery);
+ if (sourceQuery) {
+ searchSource.setField('query', sourceQuery);
}
if (style.isTimeAware() && (await this.isTimeAware())) {
- searchSource.setField('filter', [
- getTimeFilter().createFilter(indexPattern, searchFilters.timeFilters),
- ]);
+ searchSource.setField('filter', [getTimeFilter().createFilter(indexPattern, timeFilters)]);
}
const resp = await this._runEsQuery({
diff --git a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.d.ts b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.d.ts
index 248ca2b9212b4..ef1ada8da8289 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.d.ts
+++ b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.d.ts
@@ -4,10 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { MapQuery, VectorJoinSourceRequestMeta } from '../../../../common/descriptor_types';
import { IField } from '../../fields/field';
import { IESAggSource } from '../es_agg_source';
+import { PropertiesMap } from '../../joins/join';
export interface IESTermSource extends IESAggSource {
- getTermField(): IField;
- hasCompleteConfig(): boolean;
+ getTermField: () => IField;
+ hasCompleteConfig: () => boolean;
+ getWhereQuery: () => MapQuery;
+ getPropertiesMap: (
+ searchFilters: VectorJoinSourceRequestMeta,
+ leftSourceName: string,
+ leftFieldName: string,
+ registerCancelCallback: (callback: () => void) => void
+ ) => PropertiesMap;
}
diff --git a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.js b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.js
index 359d22d2c44ce..ff52dccdd2ef4 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.js
+++ b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.js
@@ -119,9 +119,7 @@ export class ESTermSource extends AbstractESAggSource {
});
const countPropertyName = this.getAggKey(AGG_TYPE.COUNT);
- return {
- propertiesMap: extractPropertiesMap(rawEsData, countPropertyName),
- };
+ return extractPropertiesMap(rawEsData, countPropertyName);
}
isFilterByMapBounds() {
diff --git a/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/kibana_regionmap_source.js b/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/kibana_regionmap_source.js
index eeb34ed672221..d937edb4ed362 100644
--- a/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/kibana_regionmap_source.js
+++ b/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/kibana_regionmap_source.js
@@ -74,6 +74,7 @@ export class KibanaRegionmapSource extends AbstractVectorSource {
});
return {
data: featureCollection,
+ meta: {},
};
}
diff --git a/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/mvt_single_layer_vector_source.tsx b/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/mvt_single_layer_vector_source.tsx
index 3e515613b3fd0..440f0cb4457e8 100644
--- a/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/mvt_single_layer_vector_source.tsx
+++ b/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/mvt_single_layer_vector_source.tsx
@@ -179,7 +179,7 @@ export class MVTSingleLayerVectorSource
getBoundsForFilters(
boundsFilters: BoundsFilters,
- registerCancelCallback: (requestToken: symbol, callback: () => void) => void
+ registerCancelCallback: (callback: () => void) => void
): MapExtent | null {
return null;
}
@@ -192,6 +192,18 @@ export class MVTSingleLayerVectorSource
return false;
}
+ isBoundsAware() {
+ return false;
+ }
+
+ getSourceTooltipContent() {
+ return { tooltipContent: null, areResultsTrimmed: false };
+ }
+
+ async getLeftJoinFields() {
+ return [];
+ }
+
async getTooltipProperties(
properties: GeoJsonProperties,
featureId?: string | number
diff --git a/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.d.ts b/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.d.ts
index a481e273bc33e..7bf1db43c2871 100644
--- a/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.d.ts
+++ b/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.d.ts
@@ -19,6 +19,12 @@ import {
} from '../../../../common/descriptor_types';
import { VECTOR_SHAPE_TYPE } from '../../../../common/constants';
import { ITooltipProperty } from '../../tooltips/tooltip_property';
+import { DataRequest } from '../../util/data_request';
+
+export interface SourceTooltipConfig {
+ tooltipContent: string | null;
+ areResultsTrimmed: boolean;
+}
export type GeoJsonFetchMeta = ESSearchSourceResponseMeta;
@@ -30,8 +36,8 @@ export type GeoJsonWithMeta = {
export type BoundsFilters = {
applyGlobalQuery: boolean;
filters: Filter[];
- query: MapQuery;
- sourceQuery: MapQuery;
+ query?: MapQuery;
+ sourceQuery?: MapQuery;
timeFilters: TimeRange;
};
@@ -39,44 +45,52 @@ export interface IVectorSource extends ISource {
getTooltipProperties(properties: GeoJsonProperties): Promise;
getBoundsForFilters(
boundsFilters: BoundsFilters,
- registerCancelCallback: (requestToken: symbol, callback: () => void) => void
+ registerCancelCallback: (callback: () => void) => void
): MapExtent | null;
getGeoJsonWithMeta(
- layerName: 'string',
+ layerName: string,
searchFilters: MapFilters,
- registerCancelCallback: (callback: () => void) => void
+ registerCancelCallback: (callback: () => void) => void,
+ isRequestStillActive: () => boolean
): Promise;
getFields(): Promise;
getFieldByName(fieldName: string): IField | null;
+ getLeftJoinFields(): Promise;
getSyncMeta(): VectorSourceSyncMeta;
getFieldNames(): string[];
getApplyGlobalQuery(): boolean;
createField({ fieldName }: { fieldName: string }): IField;
canFormatFeatureProperties(): boolean;
getSupportedShapeTypes(): Promise;
+ isBoundsAware(): boolean;
+ getSourceTooltipContent(sourceDataRequest?: DataRequest): SourceTooltipConfig;
}
export class AbstractVectorSource extends AbstractSource implements IVectorSource {
getTooltipProperties(properties: GeoJsonProperties): Promise;
getBoundsForFilters(
boundsFilters: BoundsFilters,
- registerCancelCallback: (requestToken: symbol, callback: () => void) => void
+ registerCancelCallback: (callback: () => void) => void
): MapExtent | null;
getGeoJsonWithMeta(
layerName: string,
searchFilters: VectorSourceRequestMeta,
- registerCancelCallback: (callback: () => void) => void
+ registerCancelCallback: (callback: () => void) => void,
+ isRequestStillActive: () => boolean
): Promise;
getFields(): Promise;
getFieldByName(fieldName: string): IField | null;
+ getLeftJoinFields(): Promise;
getSyncMeta(): VectorSourceSyncMeta;
getSupportedShapeTypes(): Promise;
canFormatFeatureProperties(): boolean;
getApplyGlobalQuery(): boolean;
getFieldNames(): string[];
createField({ fieldName }: { fieldName: string }): IField;
+ isBoundsAware(): boolean;
+ getSourceTooltipContent(sourceDataRequest?: DataRequest): SourceTooltipConfig;
}
export interface ITiledSingleLayerVectorSource extends IVectorSource {
diff --git a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx
index 1244c53afe9a6..5d0d9712ef988 100644
--- a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx
+++ b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx
@@ -5,7 +5,7 @@
*/
import _ from 'lodash';
-import React from 'react';
+import React, { ReactElement } from 'react';
import { Map as MbMap, FeatureIdentifier } from 'mapbox-gl';
import { FeatureCollection } from 'geojson';
// @ts-expect-error
@@ -92,6 +92,55 @@ export interface IVectorStyle extends IStyle {
mapColors: string[]
): { hasChanges: boolean; nextStyleDescriptor?: VectorStyleDescriptor };
pluckStyleMetaFromSourceDataRequest(sourceDataRequest: DataRequest): Promise;
+ isTimeAware: () => boolean;
+ getIcon: () => ReactElement;
+ hasLegendDetails: () => Promise;
+ renderLegendDetails: () => ReactElement;
+ clearFeatureState: (featureCollection: FeatureCollection, mbMap: MbMap, sourceId: string) => void;
+ setFeatureStateAndStyleProps: (
+ featureCollection: FeatureCollection,
+ mbMap: MbMap,
+ mbSourceId: string
+ ) => boolean;
+ arePointsSymbolizedAsCircles: () => boolean;
+ setMBPaintProperties: ({
+ alpha,
+ mbMap,
+ fillLayerId,
+ lineLayerId,
+ }: {
+ alpha: number;
+ mbMap: MbMap;
+ fillLayerId: string;
+ lineLayerId: string;
+ }) => void;
+ setMBPaintPropertiesForPoints: ({
+ alpha,
+ mbMap,
+ pointLayerId,
+ }: {
+ alpha: number;
+ mbMap: MbMap;
+ pointLayerId: string;
+ }) => void;
+ setMBPropertiesForLabelText: ({
+ alpha,
+ mbMap,
+ textLayerId,
+ }: {
+ alpha: number;
+ mbMap: MbMap;
+ textLayerId: string;
+ }) => void;
+ setMBSymbolPropertiesForPoints: ({
+ mbMap,
+ symbolLayerId,
+ alpha,
+ }: {
+ alpha: number;
+ mbMap: MbMap;
+ symbolLayerId: string;
+ }) => void;
}
export class VectorStyle implements IVectorStyle {
@@ -594,12 +643,12 @@ export class VectorStyle implements IVectorStyle {
mbSourceId: string
) {
if (!featureCollection) {
- return;
+ return false;
}
const dynamicStyleProps = this.getDynamicPropertiesArray();
if (dynamicStyleProps.length === 0) {
- return;
+ return false;
}
const tmpFeatureIdentifier: FeatureIdentifier = {
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx
index 6c6cb6ba143cd..24728465de3bd 100644
--- a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx
+++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx
@@ -9,7 +9,6 @@ import React from 'react';
import { shallow } from 'enzyme';
import { AbstractLayer, ILayer } from '../../../../../../classes/layers/layer';
import { AbstractSource, ISource } from '../../../../../../classes/sources/source';
-import { IStyle } from '../../../../../../classes/styles/style';
import { TOCEntryActionsPopover } from './toc_entry_actions_popover';
@@ -17,28 +16,17 @@ let supportsFitToBounds: boolean;
class MockSource extends AbstractSource implements ISource {}
-class MockStyle implements IStyle {
- renderEditor() {
- return null;
- }
-
- getType() {
- return 'mockStyle';
- }
-}
-
class LayerMock extends AbstractLayer implements ILayer {
constructor() {
const sourceDescriptor = {
type: 'mySourceType',
};
const source = new MockSource(sourceDescriptor);
- const style = new MockStyle();
const layerDescriptor = {
id: 'testLayer',
sourceDescriptor,
};
- super({ layerDescriptor, source, style });
+ super({ layerDescriptor, source });
}
async supportsFitToBounds(): Promise {
diff --git a/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_features.ts b/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_features.ts
index 1bab51e70a494..c501ad82954a3 100644
--- a/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_features.ts
+++ b/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_features.ts
@@ -14,14 +14,15 @@ export const createFeature = (
excludeFromBaseAll?: boolean;
excludeFromBaseRead?: boolean;
privileges?: KibanaFeatureConfig['privileges'];
+ category?: KibanaFeatureConfig['category'];
}
) => {
- const { excludeFromBaseAll, excludeFromBaseRead, privileges, ...rest } = config;
+ const { excludeFromBaseAll, excludeFromBaseRead, privileges, category, ...rest } = config;
return new KibanaFeature({
icon: 'discoverApp',
navLinkId: 'discover',
app: [],
- category: { id: 'foo', label: 'foo' },
+ category: category ?? { id: 'foo', label: 'foo' },
catalogue: [],
privileges:
privileges === null
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/__fixtures__/index.ts b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/__fixtures__/index.ts
index 9df50b198bde0..7cfa50f6204fb 100644
--- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/__fixtures__/index.ts
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/__fixtures__/index.ts
@@ -6,30 +6,37 @@
import { ReactWrapper } from 'enzyme';
-import {
- EuiTableRow,
- EuiCheckbox,
- EuiCheckboxProps,
- EuiButtonGroup,
- EuiButtonGroupProps,
-} from '@elastic/eui';
+import { EuiCheckbox, EuiCheckboxProps, EuiButtonGroup, EuiButtonGroupProps } from '@elastic/eui';
import { findTestSubject } from 'test_utils/find_test_subject';
+import { EuiAccordion } from '@elastic/eui';
import { SubFeatureForm } from '../sub_feature_form';
export function getDisplayedFeaturePrivileges(wrapper: ReactWrapper) {
- const allExpanderButtons = findTestSubject(wrapper, 'expandFeaturePrivilegeRow');
+ const categoryExpander = findTestSubject(wrapper, 'featureCategoryButton_foo');
+ categoryExpander.simulate('click');
+
+ const allExpanderButtons = findTestSubject(wrapper, 'featureTableCell');
allExpanderButtons.forEach((button) => button.simulate('click'));
- // each expanded row renders its own `EuiTableRow`, so there are 2 rows
- // for each feature: one for the primary feature privilege, and one for the sub privilege form
- const rows = wrapper.find(EuiTableRow);
+ const featurePrivilegeControls = wrapper
+ .find(EuiAccordion)
+ .filter('[data-test-subj="featurePrivilegeControls"]');
+
+ return featurePrivilegeControls.reduce((acc, featureControls) => {
+ const buttonGroup = featureControls
+ .find(EuiButtonGroup)
+ .filter('[data-test-subj="primaryFeaturePrivilegeControl"]');
+ const { name, idSelected } = buttonGroup.props();
+ expect(name).toBeDefined();
+ expect(idSelected).toBeDefined();
- return rows.reduce((acc, row) => {
+ const featureId = name!.substr(`featurePrivilege_`.length);
+ const primaryFeaturePrivilege = idSelected!.substr(`${featureId}_`.length);
const subFeaturePrivileges = [];
- const subFeatureForm = row.find(SubFeatureForm);
+
+ const subFeatureForm = featureControls.find(SubFeatureForm);
if (subFeatureForm.length > 0) {
- const { featureId } = subFeatureForm.props();
const independentPrivileges = (subFeatureForm.find(EuiCheckbox) as ReactWrapper<
EuiCheckboxProps
>).reduce((acc2, checkbox) => {
@@ -47,30 +54,15 @@ export function getDisplayedFeaturePrivileges(wrapper: ReactWrapper) {
}, [] as string[]);
subFeaturePrivileges.push(...independentPrivileges, ...mutuallyExclusivePrivileges);
-
- return {
- ...acc,
- [featureId]: {
- ...acc[featureId],
- subFeaturePrivileges,
- },
- };
- } else {
- const buttonGroup = row.find(EuiButtonGroup);
- const { name, idSelected } = buttonGroup.props();
- expect(name).toBeDefined();
- expect(idSelected).toBeDefined();
-
- const featureId = name!.substr(`featurePrivilege_`.length);
- const primaryFeaturePrivilege = idSelected!.substr(`${featureId}_`.length);
-
- return {
- ...acc,
- [featureId]: {
- ...acc[featureId],
- primaryFeaturePrivilege,
- },
- };
}
+
+ return {
+ ...acc,
+ [featureId]: {
+ ...acc[featureId],
+ primaryFeaturePrivilege,
+ subFeaturePrivileges,
+ },
+ };
}, {} as Record);
}
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/change_all_privileges.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/change_all_privileges.tsx
index 14375587c8497..426d9d7bf336b 100644
--- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/change_all_privileges.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/change_all_privileges.tsx
@@ -6,7 +6,14 @@
import './change_all_privileges.scss';
-import { EuiContextMenuItem, EuiContextMenuPanel, EuiLink, EuiPopover } from '@elastic/eui';
+import {
+ EuiContextMenuItem,
+ EuiContextMenuPanel,
+ EuiLink,
+ EuiPopover,
+ EuiIcon,
+ EuiText,
+} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import _ from 'lodash';
import React, { Component } from 'react';
@@ -34,10 +41,13 @@ export class ChangeAllPrivilegesControl extends Component {
className={'secPrivilegeFeatureChangeAllLink'}
data-test-subj="changeAllPrivilegesButton"
>
-
+
+ {' '}
+
+
);
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.scss
new file mode 100644
index 0000000000000..e5c026d317034
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.scss
@@ -0,0 +1,5 @@
+.subFeaturePrivilegeExpandedRegion {
+ background-color: $euiColorLightestShade;
+ padding-left: $euiSizeXXL;
+ padding-top: $euiSizeS;
+}
\ No newline at end of file
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx
index 02d692bf9f507..002b13609005a 100644
--- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx
@@ -13,7 +13,7 @@ import { createKibanaPrivileges } from '../../../../__fixtures__/kibana_privileg
import { PrivilegeFormCalculator } from '../privilege_form_calculator';
import { getDisplayedFeaturePrivileges } from './__fixtures__';
import { findTestSubject } from 'test_utils/find_test_subject';
-import { FeatureTableExpandedRow } from './feature_table_expanded_row';
+import { EuiAccordion } from '@elastic/eui';
const createRole = (kibana: Role['kibana'] = []): Role => {
return {
@@ -86,18 +86,19 @@ describe('FeatureTable', () => {
expect(displayedPrivileges).toEqual({
excluded_from_base: {
primaryFeaturePrivilege: 'none',
- ...(canCustomizeSubFeaturePrivileges ? { subFeaturePrivileges: [] } : {}),
+ subFeaturePrivileges: [],
},
no_sub_features: {
primaryFeaturePrivilege: 'none',
+ subFeaturePrivileges: [],
},
with_excluded_sub_features: {
primaryFeaturePrivilege: 'none',
- ...(canCustomizeSubFeaturePrivileges ? { subFeaturePrivileges: [] } : {}),
+ subFeaturePrivileges: [],
},
with_sub_features: {
primaryFeaturePrivilege: 'none',
- ...(canCustomizeSubFeaturePrivileges ? { subFeaturePrivileges: [] } : {}),
+ subFeaturePrivileges: [],
},
});
});
@@ -125,14 +126,15 @@ describe('FeatureTable', () => {
expect(displayedPrivileges).toEqual({
excluded_from_base: {
primaryFeaturePrivilege: 'none',
- ...(canCustomizeSubFeaturePrivileges ? { subFeaturePrivileges: [] } : {}),
+ subFeaturePrivileges: [],
},
no_sub_features: {
primaryFeaturePrivilege: 'all',
+ subFeaturePrivileges: [],
},
with_excluded_sub_features: {
primaryFeaturePrivilege: 'all',
- ...(canCustomizeSubFeaturePrivileges ? { subFeaturePrivileges: [] } : {}),
+ subFeaturePrivileges: [],
},
with_sub_features: {
primaryFeaturePrivilege: 'all',
@@ -144,7 +146,7 @@ describe('FeatureTable', () => {
'cool_all',
],
}
- : {}),
+ : { subFeaturePrivileges: [] }),
},
});
});
@@ -175,14 +177,15 @@ describe('FeatureTable', () => {
expect(displayedPrivileges).toEqual({
excluded_from_base: {
primaryFeaturePrivilege: 'none',
- ...(canCustomizeSubFeaturePrivileges ? { subFeaturePrivileges: [] } : {}),
+ subFeaturePrivileges: [],
},
no_sub_features: {
primaryFeaturePrivilege: 'none',
+ subFeaturePrivileges: [],
},
with_excluded_sub_features: {
primaryFeaturePrivilege: 'none',
- ...(canCustomizeSubFeaturePrivileges ? { subFeaturePrivileges: [] } : {}),
+ subFeaturePrivileges: [],
},
with_sub_features: {
primaryFeaturePrivilege: 'all',
@@ -194,7 +197,7 @@ describe('FeatureTable', () => {
'cool_all',
],
}
- : {}),
+ : { subFeaturePrivileges: [] }),
},
});
});
@@ -279,6 +282,7 @@ describe('FeatureTable', () => {
},
no_sub_features: {
primaryFeaturePrivilege: 'none',
+ subFeaturePrivileges: [],
},
with_excluded_sub_features: {
primaryFeaturePrivilege: 'none',
@@ -313,43 +317,18 @@ describe('FeatureTable', () => {
});
kibanaFeatures.forEach((feature) => {
- const rowExpander = findTestSubject(wrapper, `expandFeaturePrivilegeRow-${feature.id}`);
+ const { arrowDisplay } = wrapper
+ .find(EuiAccordion)
+ .filter(`#featurePrivilegeControls_${feature.id}`)
+ .props();
if (!feature.subFeatures || feature.subFeatures.length === 0) {
- expect(rowExpander).toHaveLength(0);
+ expect(arrowDisplay).toEqual('none');
} else {
- expect(rowExpander).toHaveLength(1);
+ expect(arrowDisplay).toEqual('left');
}
});
});
- it('renders the when the row is expanded', () => {
- const role = createRole([
- {
- spaces: ['*'],
- base: ['read'],
- feature: {},
- },
- {
- spaces: ['foo'],
- base: [],
- feature: {},
- },
- ]);
- const { wrapper } = setup({
- role,
- features: kibanaFeatures,
- privilegeIndex: 1,
- calculateDisplayedPrivileges: false,
- canCustomizeSubFeaturePrivileges: true,
- });
-
- expect(wrapper.find(FeatureTableExpandedRow)).toHaveLength(0);
-
- findTestSubject(wrapper, 'expandFeaturePrivilegeRow').first().simulate('click');
-
- expect(wrapper.find(FeatureTableExpandedRow)).toHaveLength(1);
- });
-
it('renders with sub-feature privileges granted when primary feature privilege is "all"', () => {
const role = createRole([
{
@@ -679,6 +658,7 @@ describe('FeatureTable', () => {
},
no_sub_features: {
primaryFeaturePrivilege: 'none',
+ subFeaturePrivileges: [],
},
with_excluded_sub_features: {
primaryFeaturePrivilege: 'none',
@@ -716,15 +696,19 @@ describe('FeatureTable', () => {
expect(displayedPrivileges).toEqual({
excluded_from_base: {
primaryFeaturePrivilege: 'none',
+ subFeaturePrivileges: [],
},
no_sub_features: {
primaryFeaturePrivilege: 'none',
+ subFeaturePrivileges: [],
},
with_excluded_sub_features: {
primaryFeaturePrivilege: 'none',
+ subFeaturePrivileges: [],
},
with_sub_features: {
primaryFeaturePrivilege: 'none',
+ subFeaturePrivileges: [],
},
});
});
@@ -750,15 +734,19 @@ describe('FeatureTable', () => {
expect(displayedPrivileges).toEqual({
excluded_from_base: {
primaryFeaturePrivilege: 'none',
+ subFeaturePrivileges: [],
},
no_sub_features: {
primaryFeaturePrivilege: 'none',
+ subFeaturePrivileges: [],
},
with_excluded_sub_features: {
primaryFeaturePrivilege: 'none',
+ subFeaturePrivileges: [],
},
with_sub_features: {
primaryFeaturePrivilege: 'none',
+ subFeaturePrivileges: [],
},
});
});
@@ -843,6 +831,7 @@ describe('FeatureTable', () => {
expect(displayedPrivileges).toEqual({
reserved_feature: {
primaryFeaturePrivilege: 'none',
+ subFeaturePrivileges: [],
},
});
});
@@ -873,16 +862,79 @@ describe('FeatureTable', () => {
expect(displayedPrivileges).toEqual({
excluded_from_base: {
primaryFeaturePrivilege: 'none',
+ subFeaturePrivileges: [],
},
no_sub_features: {
primaryFeaturePrivilege: 'none',
+ subFeaturePrivileges: [],
},
with_excluded_sub_features: {
primaryFeaturePrivilege: 'none',
+ subFeaturePrivileges: [],
},
with_sub_features: {
primaryFeaturePrivilege: 'none',
+ subFeaturePrivileges: [],
},
});
});
+
+ it('renders features by category, indicating how many features are granted within', async () => {
+ const role = createRole([
+ {
+ spaces: ['foo'],
+ base: [],
+ feature: {
+ feature_1: ['all'],
+ feature_3: ['all'],
+ feature_4: ['all'],
+ },
+ },
+ ]);
+
+ const features = [
+ createFeature({
+ id: 'feature_1',
+ name: 'Feature1',
+ category: { id: 'foo', label: 'foo' },
+ }),
+ createFeature({
+ id: 'feature_2',
+ name: 'Feature2',
+ category: { id: 'foo', label: 'foo' },
+ }),
+ createFeature({
+ id: 'feature_3',
+ name: 'Feature3',
+ category: { id: 'bar', label: 'bar' },
+ }),
+ createFeature({
+ id: 'feature_4',
+ name: 'Feature4',
+ category: { id: 'bar', label: 'bar' },
+ }),
+ ];
+
+ const { wrapper } = setup({
+ role,
+ features,
+ privilegeIndex: 0,
+ calculateDisplayedPrivileges: false,
+ canCustomizeSubFeaturePrivileges: false,
+ });
+
+ const fooCategory = findTestSubject(wrapper, 'featureCategory_foo');
+ const barCategory = findTestSubject(wrapper, 'featureCategory_bar');
+
+ expect(fooCategory).toHaveLength(1);
+ expect(barCategory).toHaveLength(1);
+
+ expect(findTestSubject(fooCategory, 'categoryLabel').text()).toMatchInlineSnapshot(
+ `"1 / 2 features granted"`
+ );
+
+ expect(findTestSubject(barCategory, 'categoryLabel').text()).toMatchInlineSnapshot(
+ `"2 / 2 features granted"`
+ );
+ });
});
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx
index 57e24f2838226..a07c2e1c14ac4 100644
--- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx
@@ -5,24 +5,31 @@
*/
import {
+ EuiAccordionProps,
EuiButtonGroup,
EuiIconTip,
- EuiInMemoryTable,
EuiText,
- EuiButtonIcon,
EuiFlexGroup,
EuiFlexItem,
+ EuiSpacer,
+ EuiCallOut,
+ EuiHorizontalRule,
+ EuiAccordion,
+ EuiIcon,
+ EuiTitle,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
-import React, { Component } from 'react';
+import React, { Component, ReactElement } from 'react';
+import { AppCategory } from 'kibana/public';
import { Role } from '../../../../../../../common/model';
import { ChangeAllPrivilegesControl } from './change_all_privileges';
import { FeatureTableExpandedRow } from './feature_table_expanded_row';
import { NO_PRIVILEGE_VALUE } from '../constants';
import { PrivilegeFormCalculator } from '../privilege_form_calculator';
import { FeatureTableCell } from '../feature_table_cell';
-import { KibanaPrivileges, SecuredFeature, KibanaPrivilege } from '../../../../model';
+import { KibanaPrivileges, SecuredFeature } from '../../../../model';
+import './feature_table.scss';
interface Props {
role: Role;
@@ -35,250 +42,296 @@ interface Props {
disabled?: boolean;
}
-interface State {
- expandedFeatures: string[];
-}
-
-interface TableRow {
- featureId: string;
- feature: SecuredFeature;
- inherited: KibanaPrivilege[];
- effective: KibanaPrivilege[];
- role: Role;
-}
-
-export class FeatureTable extends Component {
+export class FeatureTable extends Component {
public static defaultProps = {
privilegeIndex: -1,
showLocks: true,
};
+ private featureCategories: Map = new Map();
+
constructor(props: Props) {
super(props);
- this.state = {
- expandedFeatures: [],
- };
+
+ // features are static for the lifetime of the page, so this is safe to do here in a non-reactive manner
+ props.kibanaPrivileges
+ .getSecuredFeatures()
+ .filter((feature) => feature.privileges != null || feature.reserved != null)
+ .forEach((feature) => {
+ if (!this.featureCategories.has(feature.category.id)) {
+ this.featureCategories.set(feature.category.id, []);
+ }
+ this.featureCategories.get(feature.category.id)!.push(feature);
+ });
}
public render() {
- const { role, kibanaPrivileges } = this.props;
+ const basePrivileges = this.props.kibanaPrivileges.getBasePrivileges(
+ this.props.role.kibana[this.props.privilegeIndex]
+ );
- const featurePrivileges = kibanaPrivileges
- .getSecuredFeatures()
- .filter((feature) => feature.privileges != null || feature.reserved != null);
+ const accordions: Array<{ order: number; element: ReactElement }> = [];
+ this.featureCategories.forEach((featuresInCategory) => {
+ const { category } = featuresInCategory[0];
- const items: TableRow[] = featurePrivileges
- .sort((feature1, feature2) => {
- if (feature1.reserved && !feature2.reserved) {
- return 1;
- }
+ const featureCount = featuresInCategory.length;
+ const grantedCount = featuresInCategory.filter(
+ (feature) =>
+ this.props.privilegeCalculator.getEffectivePrimaryFeaturePrivilege(
+ feature.id,
+ this.props.privilegeIndex
+ ) != null
+ ).length;
+
+ const canExpandCategory = true; // featuresInCategory.length > 1;
+
+ const buttonContent = (
+
+ {category.euiIconType ? (
+
+
+
+ ) : null}
+
+
+ {category.label}
+
+
+
+ );
- if (feature2.reserved && !feature1.reserved) {
- return -1;
+ const label: string = i18n.translate(
+ 'xpack.security.management.editRole.featureTable.featureAccordionSwitchLabel',
+ {
+ defaultMessage:
+ '{grantedCount} / {featureCount} {featureCount, plural, one {feature} other {features}} granted',
+ values: {
+ grantedCount,
+ featureCount,
+ },
}
+ );
+ const extraAction = (
+
+ {label}
+
+ );
- return 0;
- })
- .map((feature) => {
- return {
- featureId: feature.id,
- feature,
- inherited: [],
- effective: [],
- role,
- };
+ const helpText = this.getCategoryHelpText(category);
+
+ const accordion = (
+
+
+
+ {helpText && (
+ <>
+
+ {helpText}
+
+
+ >
+ )}
+
+ {featuresInCategory.map((feature) => (
+
+ {this.renderPrivilegeControlsForFeature(feature)}
+
+ ))}
+
+
+
+ );
+
+ accordions.push({
+ order: category.order ?? Number.MAX_SAFE_INTEGER,
+ element: accordion,
});
+ });
+
+ accordions.sort((a1, a2) => a1.order - a2.order);
return (
- {
- return {
- ...acc,
- [featureId]: (
- f.id === featureId)!}
- privilegeIndex={this.props.privilegeIndex}
- onChange={this.props.onChange}
- privilegeCalculator={this.props.privilegeCalculator}
- selectedFeaturePrivileges={
- this.props.role.kibana[this.props.privilegeIndex].feature[featureId] ?? []
- }
- disabled={this.props.disabled}
+
+
+
+
+
+ {i18n.translate(
+ 'xpack.security.management.editRole.featureTable.featureVisibilityTitle',
+ {
+ defaultMessage: 'Customize feature privileges',
+ }
+ )}
+
+
+
+ {!this.props.disabled && (
+
+
- ),
- };
- }, {})}
- items={items}
- />
+
+ )}
+
+
+ {accordions.flatMap((a, idx) => [
+ a.element,
+ ,
+ ])}
+
);
}
- public onChange = (featureId: string) => (featurePrivilegeId: string) => {
- const privilege = featurePrivilegeId.substr(`${featureId}_`.length);
- if (privilege === NO_PRIVILEGE_VALUE) {
- this.props.onChange(featureId, []);
- } else {
- this.props.onChange(featureId, [privilege]);
+ private renderPrivilegeControlsForFeature = (feature: SecuredFeature) => {
+ const renderFeatureMarkup = (
+ buttonContent: EuiAccordionProps['buttonContent'],
+ extraAction: EuiAccordionProps['extraAction'],
+ warningIcon: JSX.Element
+ ) => {
+ const { canCustomizeSubFeaturePrivileges } = this.props;
+ const hasSubFeaturePrivileges = feature.getSubFeaturePrivileges().length > 0;
+
+ return (
+
+ {warningIcon}
+
+
+
+
+
+
+
+
+ );
+ };
+
+ const primaryFeaturePrivileges = feature.getPrimaryFeaturePrivileges();
+
+ if (feature.reserved && primaryFeaturePrivileges.length === 0) {
+ const buttonContent = (
+ <>
+ { }
+ >
+ );
+
+ const extraAction = (
+
+ {feature.reserved.description}
+
+ );
+
+ return renderFeatureMarkup(buttonContent, extraAction, );
}
- };
- private getColumns = () => {
- const basePrivileges = this.props.kibanaPrivileges.getBasePrivileges(
- this.props.role.kibana[this.props.privilegeIndex]
+ if (primaryFeaturePrivileges.length === 0) {
+ return null;
+ }
+
+ const selectedPrivilegeId = this.props.privilegeCalculator.getDisplayedPrimaryFeaturePrivilegeId(
+ feature.id,
+ this.props.privilegeIndex
);
- const columns = [];
-
- if (this.props.canCustomizeSubFeaturePrivileges) {
- columns.push({
- width: '30px',
- isExpander: true,
- field: 'featureId',
- name: '',
- render: (featureId: string, record: TableRow) => {
- const { feature } = record;
- const hasSubFeaturePrivileges = feature.getSubFeaturePrivileges().length > 0;
- if (!hasSubFeaturePrivileges) {
- return null;
- }
- return (
- this.toggleExpandedFeature(featureId)}
- data-test-subj={`expandFeaturePrivilegeRow expandFeaturePrivilegeRow-${featureId}`}
- aria-label={this.state.expandedFeatures.includes(featureId) ? 'Collapse' : 'Expand'}
- iconType={this.state.expandedFeatures.includes(featureId) ? 'arrowUp' : 'arrowDown'}
- />
- );
- },
- });
- }
+ const options = primaryFeaturePrivileges.map((privilege) => {
+ return {
+ id: `${feature.id}_${privilege.id}`,
+ label: privilege.name,
+ isDisabled: this.props.disabled,
+ };
+ });
- columns.push(
- {
- field: 'feature',
- width: '200px',
- name: i18n.translate(
- 'xpack.security.management.editRole.featureTable.enabledRoleFeaturesFeatureColumnTitle',
- {
- defaultMessage: 'Feature',
- }
- ),
- render: (feature: SecuredFeature) => {
- return ;
- },
- },
- {
- field: 'privilege',
- width: '200px',
- name: (
-
+ options.push({
+ id: `${feature.id}_${NO_PRIVILEGE_VALUE}`,
+ label: 'None',
+ isDisabled: this.props.disabled,
+ });
+
+ let warningIcon = ;
+ if (
+ this.props.privilegeCalculator.hasCustomizedSubFeaturePrivileges(
+ feature.id,
+ this.props.privilegeIndex
+ )
+ ) {
+ warningIcon = (
+
- {!this.props.disabled && (
-
- )}
-
- ),
- mobileOptions: {
- // Table isn't responsive, so skip rendering this for mobile. isn't free...
- header: false,
- },
- render: (roleEntry: Role, record: TableRow) => {
- const { feature } = record;
-
- const primaryFeaturePrivileges = feature.getPrimaryFeaturePrivileges();
-
- if (feature.reserved && primaryFeaturePrivileges.length === 0) {
- return (
-
- {feature.reserved.description}
-
- );
}
+ />
+ );
+ }
- if (primaryFeaturePrivileges.length === 0) {
- return null;
- }
+ const { canCustomizeSubFeaturePrivileges } = this.props;
+ const hasSubFeaturePrivileges = feature.getSubFeaturePrivileges().length > 0;
- const selectedPrivilegeId = this.props.privilegeCalculator.getDisplayedPrimaryFeaturePrivilegeId(
- feature.id,
- this.props.privilegeIndex
- );
-
- const options = primaryFeaturePrivileges.map((privilege) => {
- return {
- id: `${feature.id}_${privilege.id}`,
- label: privilege.name,
- isDisabled: this.props.disabled,
- };
- });
-
- options.push({
- id: `${feature.id}_${NO_PRIVILEGE_VALUE}`,
- label: 'None',
- isDisabled: this.props.disabled,
- });
-
- let warningIcon = ;
- if (
- this.props.privilegeCalculator.hasCustomizedSubFeaturePrivileges(
- feature.id,
- this.props.privilegeIndex
- )
- ) {
- warningIcon = (
-
- }
- />
- );
- }
+ const showAccordionArrow = canCustomizeSubFeaturePrivileges && hasSubFeaturePrivileges;
- return (
-
- {warningIcon}
-
-
-
-
- );
- },
- }
+ const buttonContent = (
+ <>
+ {!showAccordionArrow && }{' '}
+
+ >
);
- return columns;
+
+ const extraAction = (
+
+ );
+
+ return renderFeatureMarkup(buttonContent, extraAction, warningIcon);
};
- private toggleExpandedFeature = (featureId: string) => {
- if (this.state.expandedFeatures.includes(featureId)) {
- this.setState({
- expandedFeatures: this.state.expandedFeatures.filter((ef) => ef !== featureId),
- });
+ private onChange = (featureId: string) => (featurePrivilegeId: string) => {
+ const privilege = featurePrivilegeId.substr(`${featureId}_`.length);
+ if (privilege === NO_PRIVILEGE_VALUE) {
+ this.props.onChange(featureId, []);
} else {
- this.setState({
- expandedFeatures: [...this.state.expandedFeatures, featureId],
- });
+ this.props.onChange(featureId, [privilege]);
}
};
@@ -289,4 +342,16 @@ export class FeatureTable extends Component {
this.props.onChangeAll([privilege]);
}
};
+
+ private getCategoryHelpText = (category: AppCategory) => {
+ if (category.id === 'management') {
+ return i18n.translate(
+ 'xpack.security.management.editRole.featureTable.managementCategoryHelpText',
+ {
+ defaultMessage:
+ 'Access to Stack Management is determined by both Elasticsearch and Kibana privileges, and cannot be explicitly disabled.',
+ }
+ );
+ }
+ };
}
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.scss
deleted file mode 100644
index a7f24c96a2821..0000000000000
--- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.scss
+++ /dev/null
@@ -1,4 +0,0 @@
-.secPrivilegeFeatureIcon {
- flex-shrink: 0;
- margin-right: $euiSizeS;
-}
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.test.tsx
index 155e41baeba1e..1514677c82457 100644
--- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.test.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.test.tsx
@@ -9,10 +9,10 @@ import { createFeature } from '../../../../__fixtures__/kibana_features';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { FeatureTableCell } from '.';
import { SecuredFeature } from '../../../../model';
-import { EuiIcon, EuiIconTip } from '@elastic/eui';
+import { EuiIconTip } from '@elastic/eui';
describe('FeatureTableCell', () => {
- it('renders an icon and feature name', () => {
+ it('renders the feature name', () => {
const feature = createFeature({
id: 'test-feature',
name: 'Test Feature',
@@ -23,13 +23,10 @@ describe('FeatureTableCell', () => {
);
expect(wrapper.text()).toMatchInlineSnapshot(`"Test Feature "`);
- expect(wrapper.find(EuiIcon).props()).toMatchObject({
- type: feature.icon,
- });
expect(wrapper.find(EuiIconTip)).toHaveLength(0);
});
- it('renders an icon and feature name with tooltip when configured', () => {
+ it('renders a feature name with tooltip when configured', () => {
const feature = createFeature({
id: 'test-feature',
name: 'Test Feature',
@@ -41,9 +38,7 @@ describe('FeatureTableCell', () => {
);
expect(wrapper.text()).toMatchInlineSnapshot(`"Test Feature "`);
- expect(wrapper.find(EuiIcon).first().props()).toMatchObject({
- type: feature.icon,
- });
+
expect(wrapper.find(EuiIconTip).props().content).toMatchInlineSnapshot(`
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.tsx
index 77445952f3d69..869be7f6a583a 100644
--- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.tsx
@@ -4,10 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import './feature_table_cell.scss';
-
import React from 'react';
-import { EuiText, EuiIconTip, EuiIcon, IconType } from '@elastic/eui';
+import { EuiText, EuiIconTip } from '@elastic/eui';
import { SecuredFeature } from '../../../../model';
interface Props {
@@ -35,8 +33,7 @@ export const FeatureTableCell = ({ feature }: Props) => {
}
return (
-
-
+
{feature.name} {tooltipElement}
);
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx
index e0889d91d759a..aa37b95ba3f2a 100644
--- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx
@@ -6,16 +6,9 @@
import React, { useState, Fragment } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
-import {
- EuiModal,
- EuiButtonEmpty,
- EuiOverlayMask,
- EuiModalHeader,
- EuiModalHeaderTitle,
- EuiModalBody,
- EuiModalFooter,
- EuiButton,
-} from '@elastic/eui';
+import { EuiButtonEmpty, EuiOverlayMask, EuiButton } from '@elastic/eui';
+import { EuiFlyout } from '@elastic/eui';
+import { EuiFlyoutHeader, EuiTitle, EuiFlyoutBody, EuiFlyoutFooter } from '@elastic/eui';
import { Space } from '../../../../../../../../spaces/common/model/space';
import { Role } from '../../../../../../../common/model';
import { PrivilegeSummaryTable } from './privilege_summary_table';
@@ -30,6 +23,9 @@ interface Props {
export const PrivilegeSummary = (props: Props) => {
const [isOpen, setIsOpen] = useState(false);
+ const numberOfPrivilegeDefinitions = props.role.kibana.length;
+ const flyoutSize = numberOfPrivilegeDefinitions > 5 ? 'l' : 'm';
+
return (
setIsOpen(true)} data-test-subj="viewPrivilegeSummaryButton">
@@ -39,33 +35,35 @@ export const PrivilegeSummary = (props: Props) => {
/>
{isOpen && (
-
- setIsOpen(false)} maxWidth={false}>
-
-
-
-
-
-
+
+ setIsOpen(false)} size={flyoutSize}>
+
+
+
+
+
+
+
+
-
-
+
+
setIsOpen(false)}>
-
-
+
+
)}
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx
index 4b5169de3dfc3..8d24c0b220863 100644
--- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx
@@ -4,14 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { useState } from 'react';
+import React, { useMemo, useState, Fragment } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import {
+ EuiFlexGroup,
+ EuiFlexItem,
EuiInMemoryTable,
EuiBasicTableColumn,
EuiButtonIcon,
EuiIcon,
EuiIconTip,
+ EuiSpacer,
+ EuiAccordion,
+ EuiTitle,
} from '@elastic/eui';
import { Space } from '../../../../../../../../spaces/common/model/space';
import { Role, RoleKibanaPrivilege } from '../../../../../../../common/model';
@@ -39,6 +44,22 @@ function getColumnKey(entry: RoleKibanaPrivilege) {
export const PrivilegeSummaryTable = (props: Props) => {
const [expandedFeatures, setExpandedFeatures] = useState([]);
+ const featureCategories = useMemo(() => {
+ const featureCategoryMap = new Map();
+
+ props.kibanaPrivileges
+ .getSecuredFeatures()
+ .filter((feature) => feature.privileges != null || feature.reserved != null)
+ .forEach((feature) => {
+ if (!featureCategoryMap.has(feature.category.id)) {
+ featureCategoryMap.set(feature.category.id, []);
+ }
+ featureCategoryMap.get(feature.category.id)!.push(feature);
+ });
+
+ return featureCategoryMap;
+ }, [props.kibanaPrivileges]);
+
const calculator = new PrivilegeSummaryCalculator(props.kibanaPrivileges, props.role);
const toggleExpandedFeature = (featureId: string) => {
@@ -140,35 +161,80 @@ export const PrivilegeSummaryTable = (props: Props) => {
};
}, {} as Record);
- const items = props.kibanaPrivileges.getSecuredFeatures().map((feature) => {
- return {
- feature,
- featureId: feature.id,
- ...privileges,
- };
+ const accordions: any[] = [];
+
+ featureCategories.forEach((featuresInCategory) => {
+ const { category } = featuresInCategory[0];
+
+ const buttonContent = (
+
+ {category.euiIconType ? (
+
+
+
+ ) : null}
+
+
+ {category.label}
+
+
+
+ );
+
+ const categoryItems = featuresInCategory.map((feature) => {
+ return {
+ feature,
+ featureId: feature.id,
+ ...privileges,
+ };
+ });
+
+ accordions.push(
+
+ {
+ return {
+ 'data-test-subj': `summaryTableRow-${record.featureId}`,
+ };
+ }}
+ itemIdToExpandedRowMap={expandedFeatures.reduce((acc, featureId) => {
+ return {
+ ...acc,
+ [featureId]: (
+ p[featureId])}
+ />
+ ),
+ };
+ }, {})}
+ />
+
+ );
});
return (
- {
- return {
- 'data-test-subj': `summaryTableRow-${record.featureId}`,
- };
- }}
- itemIdToExpandedRowMap={expandedFeatures.reduce((acc, featureId) => {
- return {
- ...acc,
- [featureId]: (
- p[featureId])}
- />
- ),
- };
- }, {})}
- />
+ <>
+ {accordions.map((a, idx) => (
+
+ {a}
+
+
+ ))}
+ >
);
};
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.test.tsx
index b691056528498..3c9d1789fa5ab 100644
--- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.test.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.test.tsx
@@ -43,7 +43,7 @@ const spaces = [
];
describe('SpaceColumnHeader', () => {
- it('renders the Global privilege definition with a special label and popover control', () => {
+ it('renders the Global privilege definition with a special label', () => {
const wrapper = mountWithIntl(
{
/>
);
- expect(wrapper.find(SpacesPopoverList)).toHaveLength(1);
// Snapshot includes space avatar (The first "G"), followed by the "Global" label,
// followed by the (all spaces) text as part of the SpacesPopoverList
- expect(wrapper.text()).toMatchInlineSnapshot(`"G Global(all spaces)"`);
+ expect(wrapper.text()).toMatchInlineSnapshot(`"G All Spaces"`);
});
it('renders a placeholder space when the requested space no longer exists', () => {
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx
index 24ac0022b12af..a1641577dbb2f 100644
--- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx
@@ -39,17 +39,7 @@ export const SpaceColumnHeader = (props: Props) => {
-
- s.id !== '*')}
- buttonText={i18n.translate(
- 'xpack.security.management.editRole.spacePrivilegeMatrix.showAllSpacesLink',
- {
- defaultMessage: '(all spaces)',
- }
- )}
+ defaultMessage="All Spaces"
/>
)}
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx
index 32eed6c878016..e4dc0606ebf67 100644
--- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx
@@ -11,11 +11,11 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { PrivilegeSpaceForm } from './privilege_space_form';
import React from 'react';
import { Space } from '../../../../../../../../spaces/public';
-import { EuiSuperSelect } from '@elastic/eui';
import { FeatureTable } from '../feature_table';
import { getDisplayedFeaturePrivileges } from '../feature_table/__fixtures__';
import { findTestSubject } from 'test_utils/find_test_subject';
import { SpaceSelector } from './space_selector';
+import { EuiButtonGroup } from '@elastic/eui';
const createRole = (kibana: Role['kibana'] = []): Role => {
return {
@@ -59,7 +59,9 @@ describe('PrivilegeSpaceForm', () => {
/>
);
- expect(wrapper.find(EuiSuperSelect).props().valueOfSelected).toEqual(`basePrivilege_custom`);
+ expect(
+ wrapper.find(EuiButtonGroup).filter('[name="basePrivilegeButtonGroup"]').props().idSelected
+ ).toEqual(`basePrivilege_custom`);
expect(wrapper.find(FeatureTable).props().disabled).toEqual(true);
expect(getDisplayedFeaturePrivileges(wrapper)).toMatchInlineSnapshot(`
Object {
@@ -69,6 +71,7 @@ describe('PrivilegeSpaceForm', () => {
},
"no_sub_features": Object {
"primaryFeaturePrivilege": "none",
+ "subFeaturePrivileges": Array [],
},
"with_excluded_sub_features": Object {
"primaryFeaturePrivilege": "none",
@@ -106,7 +109,9 @@ describe('PrivilegeSpaceForm', () => {
/>
);
- expect(wrapper.find(EuiSuperSelect).props().valueOfSelected).toEqual(`basePrivilege_all`);
+ expect(
+ wrapper.find(EuiButtonGroup).filter('[name="basePrivilegeButtonGroup"]').props().idSelected
+ ).toEqual(`basePrivilege_all`);
expect(wrapper.find(FeatureTable).props().disabled).toEqual(true);
expect(getDisplayedFeaturePrivileges(wrapper)).toMatchInlineSnapshot(`
Object {
@@ -116,6 +121,7 @@ describe('PrivilegeSpaceForm', () => {
},
"no_sub_features": Object {
"primaryFeaturePrivilege": "all",
+ "subFeaturePrivileges": Array [],
},
"with_excluded_sub_features": Object {
"primaryFeaturePrivilege": "all",
@@ -159,7 +165,9 @@ describe('PrivilegeSpaceForm', () => {
/>
);
- expect(wrapper.find(EuiSuperSelect).props().valueOfSelected).toEqual(`basePrivilege_custom`);
+ expect(
+ wrapper.find(EuiButtonGroup).filter('[name="basePrivilegeButtonGroup"]').props().idSelected
+ ).toEqual(`basePrivilege_custom`);
expect(wrapper.find(FeatureTable).props().disabled).toEqual(false);
expect(getDisplayedFeaturePrivileges(wrapper)).toMatchInlineSnapshot(`
Object {
@@ -169,6 +177,7 @@ describe('PrivilegeSpaceForm', () => {
},
"no_sub_features": Object {
"primaryFeaturePrivilege": "none",
+ "subFeaturePrivileges": Array [],
},
"with_excluded_sub_features": Object {
"primaryFeaturePrivilege": "none",
@@ -256,7 +265,10 @@ describe('PrivilegeSpaceForm', () => {
/>
);
- expect(wrapper.find(EuiSuperSelect).props().valueOfSelected).toEqual(`basePrivilege_custom`);
+ expect(
+ wrapper.find(EuiButtonGroup).filter('[name="basePrivilegeButtonGroup"]').props().idSelected
+ ).toEqual(`basePrivilege_custom`);
+
expect(wrapper.find(FeatureTable).props().disabled).toEqual(false);
expect(getDisplayedFeaturePrivileges(wrapper)).toMatchInlineSnapshot(`
Object {
@@ -266,6 +278,7 @@ describe('PrivilegeSpaceForm', () => {
},
"no_sub_features": Object {
"primaryFeaturePrivilege": "none",
+ "subFeaturePrivileges": Array [],
},
"with_excluded_sub_features": Object {
"primaryFeaturePrivilege": "none",
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx
index 6c43f2f7ea734..28bbd55c7d544 100644
--- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx
@@ -18,7 +18,6 @@ import {
EuiFormRow,
EuiOverlayMask,
EuiSpacer,
- EuiSuperSelect,
EuiText,
EuiTitle,
EuiErrorBoundary,
@@ -26,6 +25,7 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { Component, Fragment } from 'react';
+import { EuiButtonGroup } from '@elastic/eui';
import { Space } from '../../../../../../../../spaces/public';
import { Role, copyRole } from '../../../../../../../common/model';
import { SpaceSelector } from './space_selector';
@@ -95,7 +95,7 @@ export class PrivilegeSpaceForm extends Component {
@@ -164,6 +164,13 @@ export class PrivilegeSpaceForm extends Component {
defaultMessage: 'Spaces',
}
)}
+ helpText={i18n.translate(
+ 'xpack.security.management.editRole.spacePrivilegeForm.spaceSelectorFormHelpText',
+ {
+ defaultMessage:
+ 'Select one or more Kibana spaces to which you wish to assign privileges.',
+ }
+ )}
>
{
label={i18n.translate(
'xpack.security.management.editRole.spacePrivilegeForm.privilegeSelectorFormLabel',
{
- defaultMessage: 'Privilege',
+ defaultMessage: 'Privileges for all features',
+ }
+ )}
+ helpText={i18n.translate(
+ 'xpack.security.management.editRole.spacePrivilegeForm.privilegeSelectorFormHelpText',
+ {
+ defaultMessage:
+ 'Assign the privilege level you wish to grant to all present and future features across this space.',
}
)}
>
-
-
-
- ),
- dropdownDisplay: (
-
-
-
-
-
-
-
-
- ),
+ id: 'basePrivilege_all',
+ label: 'All',
+ ['data-test-subj']: 'basePrivilege_all',
},
{
- value: 'basePrivilege_read',
- inputDisplay: (
-
-
-
- ),
- dropdownDisplay: (
-
-
-
-
-
-
-
-
- ),
+ id: 'basePrivilege_read',
+ label: 'Read',
+ ['data-test-subj']: 'basePrivilege_read',
},
{
- value: 'basePrivilege_all',
- inputDisplay: (
-
-
-
- ),
- dropdownDisplay: (
-
-
-
-
-
-
-
-
- ),
+ id: 'basePrivilege_custom',
+ label: 'Customize',
+ ['data-test-subj']: 'basePrivilege_custom',
},
]}
- hasDividers
- valueOfSelected={this.getDisplayedBasePrivilege()}
- disabled={!hasSelectedSpaces}
+ idSelected={this.getDisplayedBasePrivilege()}
+ isDisabled={!hasSelectedSpaces}
+ onChange={this.onSpaceBasePrivilegeChange}
/>
-
+
{this.getFeatureListLabel(this.state.selectedBasePrivilege.length > 0)}
@@ -338,7 +287,7 @@ export class PrivilegeSpaceForm extends Component {
buttonText = (
);
}
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx
index 64b7fe3e2e3a9..6bb9840fd343c 100644
--- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx
@@ -23,7 +23,6 @@ import { FormattedMessage } from '@kbn/i18n/react';
import React, { Component } from 'react';
import { Space, getSpaceColor } from '../../../../../../../../spaces/public';
import { FeaturesPrivileges, Role, copyRole } from '../../../../../../../common/model';
-import { SpacesPopoverList } from '../../../spaces_popover_list';
import { PrivilegeDisplay } from './privilege_display';
import { isGlobalPrivilegeDefinition } from '../../../privilege_utils';
import { PrivilegeFormCalculator } from '../privilege_form_calculator';
@@ -118,19 +117,7 @@ export class PrivilegeSpaceTable extends Component {
const displayedSpaces = isExpanded ? spaces : spaces.slice(0, SPACES_DISPLAY_COUNT);
let button = null;
- if (record.isGlobal) {
- button = (
- s.id !== '*')}
- buttonText={i18n.translate(
- 'xpack.security.management.editRole.spacePrivilegeTable.showAllSpacesLink',
- {
- defaultMessage: 'show spaces',
- }
- )}
- />
- );
- } else if (spaces.length > displayedSpaces.length) {
+ if (spaces.length > displayedSpaces.length) {
button = (
{
name: i18n.translate(
'xpack.security.management.editRole.spaceAwarePrivilegeForm.globalSpacesName',
{
- defaultMessage: '* Global (all spaces)',
+ defaultMessage: '* All Spaces',
}
),
color: '#D3DAE6',
@@ -198,7 +198,7 @@ export class SpaceAwarePrivilegeSection extends Component {
>
);
diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts
index 13a3fb96e10f7..ef1d9a99b0aeb 100644
--- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts
@@ -76,7 +76,7 @@ describe('When invoking Trusted Apps Schema', () => {
os: 'windows',
entries: [
{
- field: 'process.path.text',
+ field: 'process.executable.text',
type: 'match',
operator: 'included',
value: 'c:/programs files/Anti-Virus',
@@ -204,7 +204,7 @@ describe('When invoking Trusted Apps Schema', () => {
field: 'process.hash.*',
value: 'A4370C0CF81686C0B696FA6261c9d3e0d810ae704ab8301839dffd5d5112f476',
},
- { field: 'process.path.text', value: '/tmp/dir1' },
+ { field: 'process.executable.text', value: '/tmp/dir1' },
].forEach((partialEntry) => {
const bodyMsg3 = {
...getCreateTrustedAppItem(),
diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts
index 912468b52adc0..25456115b3713 100644
--- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts
@@ -35,7 +35,7 @@ export const PostTrustedAppCreateRequestSchema = {
schema.object({
field: schema.oneOf([
schema.literal('process.hash.*'),
- schema.literal('process.path.text'),
+ schema.literal('process.executable.text'),
]),
type: schema.literal('match'),
operator: schema.literal('included'),
diff --git a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts
index c0afe3b612d82..75e0347b10078 100644
--- a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts
@@ -33,7 +33,7 @@ export interface PostTrustedAppCreateResponse {
}
export interface MacosLinuxConditionEntry {
- field: 'process.hash.*' | 'process.path.text';
+ field: 'process.hash.*' | 'process.executable.text';
type: 'match';
operator: 'included';
value: string;
diff --git a/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts
index 0503a9c327467..f673fca290a29 100644
--- a/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts
+++ b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts
@@ -6,7 +6,7 @@
import { IEsSearchResponse } from '../../../../../../../../src/plugins/data/common';
import { Ecs } from '../../../../ecs';
-import { CursorType, Inspect, Maybe } from '../../../common';
+import { CursorType, Inspect, Maybe, PageInfoPaginated } from '../../../common';
import { TimelineRequestOptionsPaginated } from '../..';
export interface TimelineEdges {
@@ -29,10 +29,7 @@ export interface TimelineNonEcsData {
export interface TimelineEventsAllStrategyResponse extends IEsSearchResponse {
edges: TimelineEdges[];
totalCount: number;
- pageInfo: {
- activePage: number;
- totalPages: number;
- };
+ pageInfo: PageInfoPaginated;
inspect?: Maybe;
}
diff --git a/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts b/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts
index 773ee60855886..6b96783adc25a 100644
--- a/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts
+++ b/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts
@@ -14,7 +14,13 @@ import {
TimelineEventsLastEventTimeRequestOptions,
TimelineEventsLastEventTimeStrategyResponse,
} from './events';
-import { DocValueFields, TimerangeInput, SortField } from '../common';
+import {
+ DocValueFields,
+ PaginationInput,
+ PaginationInputPaginated,
+ TimerangeInput,
+ SortField,
+} from '../common';
export * from './events';
@@ -29,19 +35,13 @@ export interface TimelineRequestBasicOptions extends IEsSearchRequest {
}
export interface TimelineRequestOptions extends TimelineRequestBasicOptions {
- pagination: {
- activePage: number;
- querySize: number;
- };
+ pagination: PaginationInput;
sort: SortField;
}
export interface TimelineRequestOptionsPaginated
extends TimelineRequestBasicOptions {
- pagination: {
- activePage: number;
- querySize: number;
- };
+ pagination: PaginationInputPaginated;
sort: SortField;
}
diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts
index e2ff51dd544a2..ca7832603f13d 100644
--- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts
@@ -6,6 +6,14 @@
import { eqlRule, indexPatterns } from '../objects/rule';
+import {
+ ALERT_RULE_METHOD,
+ ALERT_RULE_NAME,
+ ALERT_RULE_RISK_SCORE,
+ ALERT_RULE_SEVERITY,
+ ALERT_RULE_VERSION,
+ NUMBER_OF_ALERTS,
+} from '../screens/alerts';
import {
CUSTOM_RULES_BTN,
RISK_SCORE,
@@ -59,9 +67,11 @@ import {
fillDefineEqlRuleAndContinue,
fillScheduleRuleAndContinue,
selectEqlRuleType,
+ waitForTheRuleToBeExecuted,
} from '../tasks/create_new_rule';
import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver';
import { loginAndWaitForPageWithoutDateRange } from '../tasks/login';
+import { refreshPage } from '../tasks/security_header';
import { DETECTIONS_URL } from '../urls/navigation';
@@ -74,6 +84,7 @@ const expectedMitre = eqlRule.mitre
})
.join('');
const expectedNumberOfRules = 1;
+const expectedNumberOfAlerts = 7;
describe('Detection rules, EQL', () => {
before(() => {
@@ -146,5 +157,19 @@ describe('Detection rules, EQL', () => {
`${eqlRule.lookBack.interval}${eqlRule.lookBack.type}`
);
});
+
+ refreshPage();
+ waitForTheRuleToBeExecuted();
+
+ cy.get(NUMBER_OF_ALERTS)
+ .invoke('text')
+ .then((numberOfAlertsText) => {
+ cy.wrap(parseInt(numberOfAlertsText, 10)).should('eql', expectedNumberOfAlerts);
+ });
+ cy.get(ALERT_RULE_NAME).first().should('have.text', eqlRule.name);
+ cy.get(ALERT_RULE_VERSION).first().should('have.text', '1');
+ cy.get(ALERT_RULE_METHOD).first().should('have.text', 'eql');
+ cy.get(ALERT_RULE_SEVERITY).first().should('have.text', eqlRule.severity.toLowerCase());
+ cy.get(ALERT_RULE_RISK_SCORE).first().should('have.text', eqlRule.riskScore);
});
});
diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts
index e84e2b7b1669f..f375eccd902c4 100644
--- a/x-pack/plugins/security_solution/cypress/objects/rule.ts
+++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts
@@ -215,7 +215,7 @@ export const machineLearningRule: MachineLearningRule = {
};
export const eqlRule: CustomRule = {
- customQuery: 'process where process_name == "explorer.exe"',
+ customQuery: 'any where process.name == "which"',
name: 'New EQL Rule',
description: 'New EQL rule description.',
severity: 'High',
diff --git a/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.tsx b/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.tsx
index 8a294ec1b71fd..9f273b4f293ba 100644
--- a/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.tsx
@@ -5,7 +5,7 @@
*/
import { EuiButtonIcon, EuiToolTip } from '@elastic/eui';
-import React, { useCallback } from 'react';
+import React, { useCallback, useMemo } from 'react';
import { Filter } from '../../../../../../../src/plugins/data/public';
import { WithHoverActions } from '../with_hover_actions';
@@ -47,34 +47,36 @@ export const AddFilterToGlobalSearchBar = React.memo(
}
}, [filterManager, filter, onFilterAdded]);
- return (
-
-
-
-
+ const HoverContent = useMemo(
+ () => (
+
+
+
+
-
-
-
-
- }
- render={() => children}
- />
+
+
+
+
+ ),
+ [filterForValue, filterOutValue]
);
+
+ const render = useCallback(() => children, [children]);
+
+ return ;
}
);
diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx
index 74efe2d34fcca..4efb662a4aab6 100644
--- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx
@@ -9,6 +9,7 @@ import React, { useCallback } from 'react';
import { DropResult, DragDropContext } from 'react-beautiful-dnd';
import { connect, ConnectedProps } from 'react-redux';
import { Dispatch } from 'redux';
+import deepEqual from 'fast-deep-equal';
import { BeforeCapture } from './drag_drop_context';
import { BrowserFields } from '../../containers/source';
@@ -134,13 +135,11 @@ export const DragDropContextWrapperComponent = React.memo
);
},
- (prevProps, nextProps) => {
- return (
- prevProps.children === nextProps.children &&
- prevProps.dataProviders === nextProps.dataProviders &&
- prevProps.activeTimelineDataProviders === nextProps.activeTimelineDataProviders
- ); // prevent re-renders when data providers are added or removed, but all other props are the same
- }
+ // prevent re-renders when data providers are added or removed, but all other props are the same
+ (prevProps, nextProps) =>
+ prevProps.children === nextProps.children &&
+ deepEqual(prevProps.dataProviders, nextProps.dataProviders) &&
+ prevProps.activeTimelineDataProviders === nextProps.activeTimelineDataProviders
);
DragDropContextWrapperComponent.displayName = 'DragDropContextWrapperComponent';
diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx
index 64f6699d21dac..bd22811612a67 100644
--- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx
@@ -196,76 +196,93 @@ const DraggableWrapperComponent: React.FC = ({
]
);
- const renderContent = useCallback(
+ const RenderClone = useCallback(
+ (provided, snapshot) => (
+
+
+
+ {render(dataProvider, provided, snapshot)}
+
+
+
+ ),
+ [dataProvider, registerProvider, render]
+ );
+
+ const DraggableContent = useCallback(
+ (provided, snapshot) => (
+ {
+ provided.innerRef(e);
+ draggableRef.current = e;
+ }}
+ data-test-subj="providerContainer"
+ isDragging={snapshot.isDragging}
+ registerProvider={registerProvider}
+ >
+ {truncate && !snapshot.isDragging ? (
+
+ {render(dataProvider, provided, snapshot)}
+
+ ) : (
+
+ {render(dataProvider, provided, snapshot)}
+
+ )}
+
+ ),
+ [dataProvider, registerProvider, render, truncate]
+ );
+
+ const DroppableContent = useCallback(
+ (droppableProvided) => (
+
+
+ {DraggableContent}
+
+ {droppableProvided.placeholder}
+
+ ),
+ [DraggableContent, dataProvider.id, isDisabled]
+ );
+
+ const content = useMemo(
() => (
(
-
-
-
- {render(dataProvider, provided, snapshot)}
-
-
-
- )}
+ renderClone={RenderClone}
>
- {(droppableProvided) => (
-
-
- {(provided, snapshot) => (
- {
- provided.innerRef(e);
- draggableRef.current = e;
- }}
- data-test-subj="providerContainer"
- isDragging={snapshot.isDragging}
- registerProvider={registerProvider}
- >
- {truncate && !snapshot.isDragging ? (
-
- {render(dataProvider, provided, snapshot)}
-
- ) : (
-
- {render(dataProvider, provided, snapshot)}
-
- )}
-
- )}
-
- {droppableProvided.placeholder}
-
- )}
+ {DroppableContent}
),
- [dataProvider, registerProvider, render, isDisabled, truncate]
+ [DroppableContent, RenderClone, dataProvider.id, isDisabled]
);
- if (isDisabled) return <>{renderContent()}>;
+ const renderContent = useCallback(() => content, [content]);
+
+ if (isDisabled) return <>{content}>;
return (
(
type,
render = null,
renderClone,
- }) => (
-
- {(provided, snapshot) => (
+ }) => {
+ const DroppableContent = useCallback(
+ (provided, snapshot) => (
(
{render == null ? children : render({ isDraggingOver: snapshot.isDraggingOver })}
{provided.placeholder}
- )}
-
- )
+ ),
+ [children, height, render]
+ );
+
+ return (
+
+ {DroppableContent}
+
+ );
+ }
);
DroppableWrapper.displayName = 'DroppableWrapper';
diff --git a/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx b/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx
index 4dc3c6fcbe440..d37de2cd3ec3d 100644
--- a/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx
@@ -135,7 +135,7 @@ DefaultDraggable.displayName = 'DefaultDraggable';
export const Badge = styled(EuiBadge)`
vertical-align: top;
-` as any; // eslint-disable-line @typescript-eslint/no-explicit-any
+`;
Badge.displayName = 'Badge';
diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx
index 2c8c8136a4733..7859f5584b0e5 100644
--- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx
@@ -5,7 +5,7 @@
*/
import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui';
-import { getOr, isEmpty, union } from 'lodash/fp';
+import { isEmpty, union } from 'lodash/fp';
import React, { useEffect, useMemo, useState } from 'react';
import styled from 'styled-components';
import deepEqual from 'fast-deep-equal';
@@ -239,6 +239,19 @@ const EventsViewerComponent: React.FC = ({
events,
]);
+ const HeaderSectionContent = useMemo(
+ () =>
+ headerFilterGroup && (
+
+ {headerFilterGroup}
+
+ ),
+ [graphEventId, headerFilterGroup]
+ );
+
useEffect(() => {
setIsQueryLoading(loading);
}, [loading]);
@@ -257,14 +270,7 @@ const EventsViewerComponent: React.FC = ({
subtitle={utilityBar ? undefined : subtitle}
title={inspect ? justTitle : titleWithExitFullScreen}
>
- {headerFilterGroup && (
-
- {headerFilterGroup}
-
- )}
+ {HeaderSectionContent}
{utilityBar && !resolverIsShowing(graphEventId) && (
{utilityBar?.(refetch, totalCountMinusDeleted)}
@@ -293,7 +299,7 @@ const EventsViewerComponent: React.FC = ({
/** Hide the footer if Resolver is showing. */
!graphEventId && (
= ({
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadPage}
serverSideEventCount={totalCountMinusDeleted}
- totalPages={getOr(0, 'totalPages', pageInfo)}
+ totalCount={pageInfo.fakeTotalCount}
/>
)
}
diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts b/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts
index f6fb01be4371f..d2bd940dcc266 100644
--- a/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts
+++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts
@@ -8,7 +8,7 @@ export const mockEventViewerResponse = {
totalCount: 12,
pageInfo: {
activePage: 0,
- totalPages: 10,
+ fakeTotalCount: 100,
},
events: [],
};
diff --git a/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.tsx b/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.tsx
index 9024aec17400c..1c5b13acb0c22 100644
--- a/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.tsx
@@ -4,9 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React from 'react';
+import React, { useCallback, useMemo } from 'react';
import { DraggableWrapper, DragEffects } from '../drag_and_drop/draggable_wrapper';
-import { IS_OPERATOR } from '../../../timelines/components/timeline/data_providers/data_provider';
+import {
+ IS_OPERATOR,
+ QueryOperator,
+} from '../../../timelines/components/timeline/data_providers/data_provider';
import { Provider } from '../../../timelines/components/timeline/data_providers/provider';
import { escapeDataProviderId } from '../drag_and_drop/helpers';
@@ -16,39 +19,43 @@ interface Props {
entityValue: string;
}
-export const EntityDraggableComponent = ({
+export const EntityDraggableComponent: React.FC = ({
idPrefix,
entityName,
entityValue,
-}: Props): JSX.Element => {
+}) => {
const id = escapeDataProviderId(`entity-draggable-${idPrefix}-${entityName}-${entityValue}`);
- return (
-
- snapshot.isDragging ? (
-
-
-
- ) : (
- <>{`${entityName}: "${entityValue}"`}>
- )
- }
- />
+
+ const dataProviderProp = useMemo(
+ () => ({
+ and: [],
+ enabled: true,
+ id,
+ name: entityValue,
+ excluded: false,
+ kqlQuery: '',
+ queryMatch: {
+ field: entityName,
+ value: entityValue,
+ operator: IS_OPERATOR as QueryOperator,
+ },
+ }),
+ [entityName, entityValue, id]
+ );
+
+ const render = useCallback(
+ (dataProvider, _, snapshot) =>
+ snapshot.isDragging ? (
+
+
+
+ ) : (
+ <>{`${entityName}: "${entityValue}"`}>
+ ),
+ [entityName, entityValue]
);
+
+ return ;
};
EntityDraggableComponent.displayName = 'EntityDraggableComponent';
diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/draggable_score.tsx b/x-pack/plugins/security_solution/public/common/components/ml/score/draggable_score.tsx
index c849476f0c3db..668a374e57f0d 100644
--- a/x-pack/plugins/security_solution/public/common/components/ml/score/draggable_score.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/ml/score/draggable_score.tsx
@@ -4,10 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React from 'react';
+import React, { useCallback, useMemo } from 'react';
import { DraggableWrapper, DragEffects } from '../../drag_and_drop/draggable_wrapper';
import { Anomaly } from '../types';
-import { IS_OPERATOR } from '../../../../timelines/components/timeline/data_providers/data_provider';
+import {
+ IS_OPERATOR,
+ QueryOperator,
+} from '../../../../timelines/components/timeline/data_providers/data_provider';
import { Provider } from '../../../../timelines/components/timeline/data_providers/provider';
import { Spacer } from '../../page';
import { getScoreString } from './score_health';
@@ -23,39 +26,48 @@ export const DraggableScoreComponent = ({
}): JSX.Element => {
const scoreString = getScoreString(score.severity);
+ const dataProviderProp = useMemo(
+ () => ({
+ and: [],
+ enabled: true,
+ id,
+ name: score.entityName,
+ excluded: false,
+ kqlQuery: '',
+ queryMatch: {
+ field: score.entityName,
+ value: score.entityValue,
+ operator: IS_OPERATOR as QueryOperator,
+ },
+ }),
+ [id, score.entityName, score.entityValue]
+ );
+
+ const render = useCallback(
+ (dataProvider, _, snapshot) =>
+ snapshot.isDragging ? (
+
+
+
+ ) : (
+ <>
+ {index !== 0 && (
+ <>
+ {','}
+
+ >
+ )}
+ {scoreString}
+ >
+ ),
+ [index, scoreString]
+ );
+
return (
- snapshot.isDragging ? (
-
-
-
- ) : (
- <>
- {index !== 0 && (
- <>
- {','}
-
- >
- )}
- {scoreString}
- >
- )
- }
+ dataProvider={dataProviderProp}
+ render={render}
/>
);
};
diff --git a/x-pack/plugins/security_solution/public/management/common/routing.test.ts b/x-pack/plugins/security_solution/public/management/common/routing.test.ts
index 7a36654dcffc3..7082ab0ce5c4f 100644
--- a/x-pack/plugins/security_solution/public/management/common/routing.test.ts
+++ b/x-pack/plugins/security_solution/public/management/common/routing.test.ts
@@ -4,57 +4,57 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { extractListPaginationParams, getTrustedAppsListPath } from './routing';
+import { extractTrustedAppsListPageLocation, getTrustedAppsListPath } from './routing';
import { MANAGEMENT_DEFAULT_PAGE, MANAGEMENT_DEFAULT_PAGE_SIZE } from './constants';
describe('routing', () => {
describe('extractListPaginationParams()', () => {
it('extracts default page index when not provided', () => {
- expect(extractListPaginationParams({}).page_index).toBe(MANAGEMENT_DEFAULT_PAGE);
+ expect(extractTrustedAppsListPageLocation({}).page_index).toBe(MANAGEMENT_DEFAULT_PAGE);
});
it('extracts default page index when too small value provided', () => {
- expect(extractListPaginationParams({ page_index: '-1' }).page_index).toBe(
+ expect(extractTrustedAppsListPageLocation({ page_index: '-1' }).page_index).toBe(
MANAGEMENT_DEFAULT_PAGE
);
});
it('extracts default page index when not a number provided', () => {
- expect(extractListPaginationParams({ page_index: 'a' }).page_index).toBe(
+ expect(extractTrustedAppsListPageLocation({ page_index: 'a' }).page_index).toBe(
MANAGEMENT_DEFAULT_PAGE
);
});
it('extracts only last page index when multiple values provided', () => {
- expect(extractListPaginationParams({ page_index: ['1', '2'] }).page_index).toBe(2);
+ expect(extractTrustedAppsListPageLocation({ page_index: ['1', '2'] }).page_index).toBe(2);
});
it('extracts proper page index when single valid value provided', () => {
- expect(extractListPaginationParams({ page_index: '2' }).page_index).toBe(2);
+ expect(extractTrustedAppsListPageLocation({ page_index: '2' }).page_index).toBe(2);
});
it('extracts default page size when not provided', () => {
- expect(extractListPaginationParams({}).page_size).toBe(MANAGEMENT_DEFAULT_PAGE_SIZE);
+ expect(extractTrustedAppsListPageLocation({}).page_size).toBe(MANAGEMENT_DEFAULT_PAGE_SIZE);
});
it('extracts default page size when invalid option provided', () => {
- expect(extractListPaginationParams({ page_size: '25' }).page_size).toBe(
+ expect(extractTrustedAppsListPageLocation({ page_size: '25' }).page_size).toBe(
MANAGEMENT_DEFAULT_PAGE_SIZE
);
});
it('extracts default page size when not a number provided', () => {
- expect(extractListPaginationParams({ page_size: 'a' }).page_size).toBe(
+ expect(extractTrustedAppsListPageLocation({ page_size: 'a' }).page_size).toBe(
MANAGEMENT_DEFAULT_PAGE_SIZE
);
});
it('extracts only last page size when multiple values provided', () => {
- expect(extractListPaginationParams({ page_size: ['10', '20'] }).page_size).toBe(20);
+ expect(extractTrustedAppsListPageLocation({ page_size: ['10', '20'] }).page_size).toBe(20);
});
it('extracts proper page size when single valid value provided', () => {
- expect(extractListPaginationParams({ page_size: '20' }).page_size).toBe(20);
+ expect(extractTrustedAppsListPageLocation({ page_size: '20' }).page_size).toBe(20);
});
});
diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts
index cb4ed9b098fce..9acf4a1613c0b 100644
--- a/x-pack/plugins/security_solution/public/management/common/routing.ts
+++ b/x-pack/plugins/security_solution/public/management/common/routing.ts
@@ -21,7 +21,7 @@ import {
import { AdministrationSubTab } from '../types';
import { appendSearch } from '../../common/components/link_to/helpers';
import { EndpointIndexUIQueryParams } from '../pages/endpoint_hosts/types';
-import { TrustedAppsUrlParams } from '../pages/trusted_apps/types';
+import { TrustedAppsListPageLocation } from '../pages/trusted_apps/state';
// Taken from: https://github.com/microsoft/TypeScript/issues/12936#issuecomment-559034150
type ExactKeys = Exclude extends never ? T1 : never;
@@ -94,18 +94,18 @@ const isDefaultOrMissing = (value: T | undefined, defaultValue: T) => {
return value === undefined || value === defaultValue;
};
-const normalizeListPaginationParams = (
- params?: Partial
-): Partial => {
- if (params) {
+const normalizeTrustedAppsPageLocation = (
+ location?: Partial
+): Partial => {
+ if (location) {
return {
- ...(!isDefaultOrMissing(params.page_index, MANAGEMENT_DEFAULT_PAGE)
- ? { page_index: params.page_index }
+ ...(!isDefaultOrMissing(location.page_index, MANAGEMENT_DEFAULT_PAGE)
+ ? { page_index: location.page_index }
: {}),
- ...(!isDefaultOrMissing(params.page_size, MANAGEMENT_DEFAULT_PAGE_SIZE)
- ? { page_size: params.page_size }
+ ...(!isDefaultOrMissing(location.page_size, MANAGEMENT_DEFAULT_PAGE_SIZE)
+ ? { page_size: location.page_size }
: {}),
- ...(!isDefaultOrMissing(params.show, undefined) ? { show: params.show } : {}),
+ ...(!isDefaultOrMissing(location.show, undefined) ? { show: location.show } : {}),
};
} else {
return {};
@@ -135,17 +135,22 @@ const extractPageSize = (query: querystring.ParsedUrlQuery): number => {
return MANAGEMENT_PAGE_SIZE_OPTIONS.includes(pageSize) ? pageSize : MANAGEMENT_DEFAULT_PAGE_SIZE;
};
-export const extractListPaginationParams = (
- query: querystring.ParsedUrlQuery
-): TrustedAppsUrlParams => ({
+export const extractListPaginationParams = (query: querystring.ParsedUrlQuery) => ({
page_index: extractPageIndex(query),
page_size: extractPageSize(query),
});
-export const getTrustedAppsListPath = (params?: Partial): string => {
+export const extractTrustedAppsListPageLocation = (
+ query: querystring.ParsedUrlQuery
+): TrustedAppsListPageLocation => ({
+ ...extractListPaginationParams(query),
+ show: extractFirstParamValue(query, 'show') === 'create' ? 'create' : undefined,
+});
+
+export const getTrustedAppsListPath = (params?: Partial): string => {
const path = generatePath(MANAGEMENT_ROUTING_TRUSTED_APPS_PATH, {
tabName: AdministrationSubTab.trustedApps,
});
- return `${path}${appendSearch(querystring.stringify(normalizeListPaginationParams(params)))}`;
+ return `${path}${appendSearch(querystring.stringify(normalizeTrustedAppsPageLocation(params)))}`;
};
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts
index 4c38ac0c4239a..a98ec03a006f5 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts
@@ -7,7 +7,6 @@
import { ServerApiError } from '../../../../common/types';
import { NewTrustedApp, TrustedApp } from '../../../../../common/endpoint/types/trusted_apps';
import { AsyncResourceState } from '.';
-import { TrustedAppsUrlParams } from '../types';
export interface PaginationInfo {
index: number;
@@ -39,12 +38,16 @@ export interface TrustedAppCreateFailure {
data: ServerApiError;
}
+export interface TrustedAppsListPageLocation {
+ page_index: number;
+ page_size: number;
+ show?: 'create';
+}
+
export interface TrustedAppsListPageState {
listView: {
- currentListResourceState: AsyncResourceState;
- currentPaginationInfo: PaginationInfo;
+ listResourceState: AsyncResourceState;
freshDataTimestamp: number;
- show: TrustedAppsUrlParams['show'] | undefined;
};
deletionDialog: {
entry?: TrustedApp;
@@ -56,5 +59,6 @@ export interface TrustedAppsListPageState {
| TrustedAppCreatePending
| TrustedAppCreateSuccess
| TrustedAppCreateFailure;
+ location: TrustedAppsListPageLocation;
active: boolean;
}
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts
index 19c2d3a62781f..2143b5135c575 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts
@@ -9,13 +9,12 @@ import { applyMiddleware, createStore } from 'redux';
import { createSpyMiddleware } from '../../../../common/store/test_utils';
import {
- createFailedListViewWithPagination,
createListLoadedResourceState,
createLoadedListViewWithPagination,
- createLoadingListViewWithPagination,
createSampleTrustedApp,
createSampleTrustedApps,
createServerApiError,
+ createUninitialisedResourceState,
createUserChangedUrlAction,
} from '../test_utils';
@@ -76,6 +75,7 @@ describe('middleware', () => {
describe('refreshing list resource state', () => {
it('refreshes the list when location changes and data gets outdated', async () => {
const pagination = { index: 2, size: 50 };
+ const location = { page_index: 2, page_size: 50, show: undefined };
const service = createTrustedAppsServiceMock();
const { store, spyMiddleware } = createStoreSetup(service);
@@ -87,21 +87,30 @@ describe('middleware', () => {
expect(store.getState()).toStrictEqual({
...initialState,
- listView: createLoadingListViewWithPagination(initialNow, pagination),
+ listView: {
+ listResourceState: {
+ type: 'LoadingResourceState',
+ previousState: createUninitialisedResourceState(),
+ },
+ freshDataTimestamp: initialNow,
+ },
active: true,
+ location,
});
await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged');
expect(store.getState()).toStrictEqual({
...initialState,
- listView: createLoadedListViewWithPagination(initialNow, pagination, pagination, 500),
+ listView: createLoadedListViewWithPagination(initialNow, pagination, 500),
active: true,
+ location,
});
});
it('does not refresh the list when location changes and data does not get outdated', async () => {
const pagination = { index: 2, size: 50 };
+ const location = { page_index: 2, page_size: 50, show: undefined };
const service = createTrustedAppsServiceMock();
const { store, spyMiddleware } = createStoreSetup(service);
@@ -118,14 +127,16 @@ describe('middleware', () => {
expect(service.getTrustedAppsList).toBeCalledTimes(1);
expect(store.getState()).toStrictEqual({
...initialState,
- listView: createLoadedListViewWithPagination(initialNow, pagination, pagination, 500),
+ listView: createLoadedListViewWithPagination(initialNow, pagination, 500),
active: true,
+ location,
});
});
it('refreshes the list when data gets outdated with and outdate action', async () => {
const newNow = 222222;
const pagination = { index: 0, size: 10 };
+ const location = { page_index: 0, page_size: 10, show: undefined };
const service = createTrustedAppsServiceMock();
const { store, spyMiddleware } = createStoreSetup(service);
@@ -143,20 +154,24 @@ describe('middleware', () => {
expect(store.getState()).toStrictEqual({
...initialState,
- listView: createLoadingListViewWithPagination(
- newNow,
- pagination,
- createListLoadedResourceState(pagination, 500, initialNow)
- ),
+ listView: {
+ listResourceState: {
+ type: 'LoadingResourceState',
+ previousState: createListLoadedResourceState(pagination, 500, initialNow),
+ },
+ freshDataTimestamp: newNow,
+ },
active: true,
+ location,
});
await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged');
expect(store.getState()).toStrictEqual({
...initialState,
- listView: createLoadedListViewWithPagination(newNow, pagination, pagination, 500),
+ listView: createLoadedListViewWithPagination(newNow, pagination, 500),
active: true,
+ location,
});
});
@@ -172,12 +187,16 @@ describe('middleware', () => {
expect(store.getState()).toStrictEqual({
...initialState,
- listView: createFailedListViewWithPagination(
- initialNow,
- { index: 2, size: 50 },
- createServerApiError('Internal Server Error')
- ),
+ listView: {
+ listResourceState: {
+ type: 'FailedResourceState',
+ error: createServerApiError('Internal Server Error'),
+ lastLoadedState: undefined,
+ },
+ freshDataTimestamp: initialNow,
+ },
active: true,
+ location: { page_index: 2, page_size: 50, show: undefined },
});
const infiniteLoopTest = async () => {
@@ -193,10 +212,11 @@ describe('middleware', () => {
const entry = createSampleTrustedApp(3);
const notFoundError = createServerApiError('Not Found');
const pagination = { index: 0, size: 10 };
+ const location = { page_index: 0, page_size: 10, show: undefined };
const getTrustedAppsListResponse = createGetTrustedListAppsResponse(pagination, 500);
- const listView = createLoadedListViewWithPagination(initialNow, pagination, pagination, 500);
- const listViewNew = createLoadedListViewWithPagination(newNow, pagination, pagination, 500);
- const testStartState = { ...initialState, listView, active: true };
+ const listView = createLoadedListViewWithPagination(initialNow, pagination, 500);
+ const listViewNew = createLoadedListViewWithPagination(newNow, pagination, 500);
+ const testStartState = { ...initialState, listView, active: true, location };
it('does not submit when entry is undefined', async () => {
const service = createTrustedAppsServiceMock();
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts
index dd96c8d807048..9fa456dc5ffe2 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts
@@ -29,12 +29,12 @@ import {
} from './action';
import {
- getCurrentListResourceState,
+ getListResourceState,
getDeletionDialogEntry,
getDeletionSubmissionResourceState,
getLastLoadedListResourceState,
- getListCurrentPageIndex,
- getListCurrentPageSize,
+ getCurrentLocationPageIndex,
+ getCurrentLocationPageSize,
getTrustedAppCreateData,
isCreatePending,
needsRefreshOfListData,
@@ -56,15 +56,15 @@ const refreshListIfNeeded = async (
createTrustedAppsListResourceStateChangedAction({
type: 'LoadingResourceState',
// need to think on how to avoid the casting
- previousState: getCurrentListResourceState(store.getState()) as Immutable<
+ previousState: getListResourceState(store.getState()) as Immutable<
StaleResourceState
>,
})
);
try {
- const pageIndex = getListCurrentPageIndex(store.getState());
- const pageSize = getListCurrentPageSize(store.getState());
+ const pageIndex = getCurrentLocationPageIndex(store.getState());
+ const pageSize = getCurrentLocationPageSize(store.getState());
const response = await trustedAppsService.getTrustedAppsList({
page: pageIndex + 1,
per_page: pageSize,
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts
index 228f0932edd28..94fcdb39bb169 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts
@@ -32,17 +32,14 @@ describe('reducer', () => {
expect(result).toStrictEqual({
...initialState,
- listView: { ...initialState.listView, currentPaginationInfo: { index: 5, size: 50 } },
+ location: { page_index: 5, page_size: 50, show: undefined },
active: true,
});
});
it('extracts default pagination parameters when none provided', () => {
const result = trustedAppsPageReducer(
- {
- ...initialState,
- listView: { ...initialState.listView, currentPaginationInfo: { index: 5, size: 50 } },
- },
+ { ...initialState, location: { page_index: 5, page_size: 50 } },
createUserChangedUrlAction('/trusted_apps', '?page_index=b&page_size=60')
);
@@ -51,10 +48,7 @@ describe('reducer', () => {
it('extracts default pagination parameters when invalid provided', () => {
const result = trustedAppsPageReducer(
- {
- ...initialState,
- listView: { ...initialState.listView, currentPaginationInfo: { index: 5, size: 50 } },
- },
+ { ...initialState, location: { page_index: 5, page_size: 50 } },
createUserChangedUrlAction('/trusted_apps')
);
@@ -85,7 +79,7 @@ describe('reducer', () => {
expect(result).toStrictEqual({
...initialState,
- listView: { ...initialState.listView, currentListResourceState: listResourceState },
+ listView: { ...initialState.listView, listResourceState },
});
});
});
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts
index ec210254bf76f..f4056f02a4140 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts
@@ -11,7 +11,8 @@ import { ImmutableReducer } from '../../../../common/store';
import { AppLocation, Immutable } from '../../../../../common/endpoint/types';
import { UserChangedUrl } from '../../../../common/store/routing/action';
import { AppAction } from '../../../../common/store/actions';
-import { extractFirstParamValue, extractListPaginationParams } from '../../../common/routing';
+import { extractTrustedAppsListPageLocation } from '../../../common/routing';
+
import {
MANAGEMENT_ROUTING_TRUSTED_APPS_PATH,
MANAGEMENT_DEFAULT_PAGE,
@@ -29,6 +30,7 @@ import {
ServerReturnedCreateTrustedAppSuccess,
UserClickedSaveNewTrustedAppButton,
} from './action';
+
import { TrustedAppsListPageState } from '../state';
type StateReducer = ImmutableReducer;
@@ -64,7 +66,7 @@ const trustedAppsListResourceStateChanged: CaseReducer = (state, action) => {
if (isTrustedAppsPageLocation(action.payload)) {
const parsedUrlsParams = parse(action.payload.search.slice(1));
- const paginationParams = extractListPaginationParams(parsedUrlsParams);
- const show =
- extractFirstParamValue(parsedUrlsParams, 'show') === 'create' ? 'create' : undefined;
+ const location = extractTrustedAppsListPageLocation(parsedUrlsParams);
return {
...state,
- listView: {
- ...state.listView,
- currentPaginationInfo: {
- index: paginationParams.page_index,
- size: paginationParams.page_size,
- },
- show,
- },
- createView: show ? state.createView : undefined,
+ createView: location.show ? state.createView : undefined,
active: true,
+ location,
};
} else {
return initialTrustedAppsPageState();
@@ -150,16 +143,16 @@ const initialDeletionDialogState = (): TrustedAppsListPageState['deletionDialog'
export const initialTrustedAppsPageState = (): TrustedAppsListPageState => ({
listView: {
- currentListResourceState: { type: 'UninitialisedResourceState' },
- currentPaginationInfo: {
- index: MANAGEMENT_DEFAULT_PAGE,
- size: MANAGEMENT_DEFAULT_PAGE_SIZE,
- },
+ listResourceState: { type: 'UninitialisedResourceState' },
freshDataTimestamp: Date.now(),
- show: undefined,
},
deletionDialog: initialDeletionDialogState(),
createView: undefined,
+ location: {
+ page_index: MANAGEMENT_DEFAULT_PAGE,
+ page_size: MANAGEMENT_DEFAULT_PAGE_SIZE,
+ show: undefined,
+ },
active: false,
});
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts
index 0be4d0b05acc4..01fe3e5bf202e 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts
@@ -7,10 +7,10 @@
import { AsyncResourceState, TrustedAppsListPageState } from '../state';
import { initialTrustedAppsPageState } from './reducer';
import {
- getCurrentListResourceState,
+ getListResourceState,
getLastLoadedListResourceState,
- getListCurrentPageIndex,
- getListCurrentPageSize,
+ getCurrentLocationPageIndex,
+ getCurrentLocationPageSize,
getListErrorMessage,
getListItems,
getListTotalItemsCount,
@@ -25,7 +25,6 @@ import {
} from './selectors';
import {
- createDefaultListView,
createDefaultPaginationInfo,
createListComplexLoadingResourceState,
createListFailedResourceState,
@@ -85,16 +84,17 @@ describe('selectors', () => {
it('returns false when current loaded data is up to date', () => {
const listView = createLoadedListViewWithPagination(initialNow);
+ const location = { page_index: 0, page_size: 10 };
- expect(needsRefreshOfListData({ ...initialState, listView, active: true })).toBe(false);
+ expect(needsRefreshOfListData({ ...initialState, listView, active: true, location })).toBe(
+ false
+ );
});
});
- describe('getCurrentListResourceState()', () => {
+ describe('getListResourceState()', () => {
it('returns current list resource state', () => {
- const state = { ...initialState, listView: createDefaultListView(initialNow) };
-
- expect(getCurrentListResourceState(state)).toStrictEqual(createUninitialisedResourceState());
+ expect(getListResourceState(initialState)).toStrictEqual(createUninitialisedResourceState());
});
});
@@ -103,14 +103,12 @@ describe('selectors', () => {
const state = {
...initialState,
listView: {
- currentListResourceState: createListComplexLoadingResourceState(
+ listResourceState: createListComplexLoadingResourceState(
createDefaultPaginationInfo(),
200,
initialNow
),
- currentPaginationInfo: createDefaultPaginationInfo(),
freshDataTimestamp: initialNow,
- show: undefined,
},
};
@@ -122,23 +120,19 @@ describe('selectors', () => {
describe('getListItems()', () => {
it('returns empty list when no valid data loaded', () => {
- const state = { ...initialState, listView: createDefaultListView(initialNow) };
-
- expect(getListItems(state)).toStrictEqual([]);
+ expect(getListItems(initialState)).toStrictEqual([]);
});
it('returns last loaded list items', () => {
const state = {
...initialState,
listView: {
- currentListResourceState: createListComplexLoadingResourceState(
+ listResourceState: createListComplexLoadingResourceState(
createDefaultPaginationInfo(),
200,
initialNow
),
- currentPaginationInfo: createDefaultPaginationInfo(),
freshDataTimestamp: initialNow,
- show: undefined,
},
};
@@ -150,23 +144,19 @@ describe('selectors', () => {
describe('getListTotalItemsCount()', () => {
it('returns 0 when no valid data loaded', () => {
- const state = { ...initialState, listView: createDefaultListView(initialNow) };
-
- expect(getListTotalItemsCount(state)).toBe(0);
+ expect(getListTotalItemsCount(initialState)).toBe(0);
});
it('returns last loaded total items count', () => {
const state = {
...initialState,
listView: {
- currentListResourceState: createListComplexLoadingResourceState(
+ listResourceState: createListComplexLoadingResourceState(
createDefaultPaginationInfo(),
200,
initialNow
),
- currentPaginationInfo: createDefaultPaginationInfo(),
freshDataTimestamp: initialNow,
- show: undefined,
},
};
@@ -176,17 +166,17 @@ describe('selectors', () => {
describe('getListCurrentPageIndex()', () => {
it('returns page index', () => {
- const state = { ...initialState, listView: createDefaultListView(initialNow) };
+ const state = { ...initialState, location: { page_index: 3, page_size: 10 } };
- expect(getListCurrentPageIndex(state)).toBe(0);
+ expect(getCurrentLocationPageIndex(state)).toBe(3);
});
});
describe('getListCurrentPageSize()', () => {
it('returns page size', () => {
- const state = { ...initialState, listView: createDefaultListView(initialNow) };
+ const state = { ...initialState, location: { page_index: 0, page_size: 20 } };
- expect(getListCurrentPageSize(state)).toBe(20);
+ expect(getCurrentLocationPageSize(state)).toBe(20);
});
});
@@ -195,14 +185,12 @@ describe('selectors', () => {
const state = {
...initialState,
listView: {
- currentListResourceState: createListComplexLoadingResourceState(
+ listResourceState: createListComplexLoadingResourceState(
createDefaultPaginationInfo(),
200,
initialNow
),
- currentPaginationInfo: createDefaultPaginationInfo(),
freshDataTimestamp: initialNow,
- show: undefined,
},
};
@@ -213,10 +201,8 @@ describe('selectors', () => {
const state = {
...initialState,
listView: {
- currentListResourceState: createListFailedResourceState('Internal Server Error'),
- currentPaginationInfo: createDefaultPaginationInfo(),
+ listResourceState: createListFailedResourceState('Internal Server Error'),
freshDataTimestamp: initialNow,
- show: undefined,
},
};
@@ -233,14 +219,12 @@ describe('selectors', () => {
const state = {
...initialState,
listView: {
- currentListResourceState: createListComplexLoadingResourceState(
+ listResourceState: createListComplexLoadingResourceState(
createDefaultPaginationInfo(),
200,
initialNow
),
- currentPaginationInfo: createDefaultPaginationInfo(),
freshDataTimestamp: initialNow,
- show: undefined,
},
};
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts
index 6239b425efe2f..62ffa364e4a6c 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts
@@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { createSelector } from 'reselect';
import { ServerApiError } from '../../../../common/types';
import { Immutable, NewTrustedApp, TrustedApp } from '../../../../../common/endpoint/types';
@@ -17,97 +16,76 @@ import {
isLoadingResourceState,
isOutdatedResourceState,
LoadedResourceState,
- PaginationInfo,
TrustedAppCreateFailure,
TrustedAppsListData,
+ TrustedAppsListPageLocation,
TrustedAppsListPageState,
} from '../state';
-import { TrustedAppsUrlParams } from '../types';
import {
isTrustedAppCreateFailureState,
isTrustedAppCreatePendingState,
isTrustedAppCreateSuccessState,
} from '../state/type_guards';
-const pageInfosEqual = (pageInfo1: PaginationInfo, pageInfo2: PaginationInfo): boolean =>
- pageInfo1.index === pageInfo2.index && pageInfo1.size === pageInfo2.size;
-
export const needsRefreshOfListData = (state: Immutable): boolean => {
- const currentPageInfo = state.listView.currentPaginationInfo;
- const currentPage = state.listView.currentListResourceState;
const freshDataTimestamp = state.listView.freshDataTimestamp;
+ const currentPage = state.listView.listResourceState;
+ const location = state.location;
return (
state.active &&
isOutdatedResourceState(currentPage, (data) => {
return (
- pageInfosEqual(currentPageInfo, data.paginationInfo) && data.timestamp >= freshDataTimestamp
+ data.paginationInfo.index === location.page_index &&
+ data.paginationInfo.size === location.page_size &&
+ data.timestamp >= freshDataTimestamp
);
})
);
};
-export const getCurrentListResourceState = (
+export const getListResourceState = (
state: Immutable
): Immutable> | undefined => {
- return state.listView.currentListResourceState;
+ return state.listView.listResourceState;
};
export const getLastLoadedListResourceState = (
state: Immutable
): Immutable> | undefined => {
- return getLastLoadedResourceState(state.listView.currentListResourceState);
+ return getLastLoadedResourceState(state.listView.listResourceState);
};
export const getListItems = (
state: Immutable
): Immutable => {
- return getLastLoadedResourceState(state.listView.currentListResourceState)?.data.items || [];
+ return getLastLoadedResourceState(state.listView.listResourceState)?.data.items || [];
};
-export const getListCurrentPageIndex = (state: Immutable): number => {
- return state.listView.currentPaginationInfo.index;
+export const getCurrentLocationPageIndex = (state: Immutable): number => {
+ return state.location.page_index;
};
-export const getListCurrentPageSize = (state: Immutable): number => {
- return state.listView.currentPaginationInfo.size;
+export const getCurrentLocationPageSize = (state: Immutable): number => {
+ return state.location.page_size;
};
export const getListTotalItemsCount = (state: Immutable): number => {
- return (
- getLastLoadedResourceState(state.listView.currentListResourceState)?.data.totalItemsCount || 0
- );
-};
-
-export const getListCurrentShowValue: (
- state: Immutable
-) => TrustedAppsListPageState['listView']['show'] = (state) => {
- return state.listView.show;
+ return getLastLoadedResourceState(state.listView.listResourceState)?.data.totalItemsCount || 0;
};
-export const getListUrlSearchParams: (
+export const getCurrentLocation = (
state: Immutable
-) => TrustedAppsUrlParams = createSelector(
- getListCurrentPageIndex,
- getListCurrentPageSize,
- getListCurrentShowValue,
- (pageIndex, pageSize, showValue) => {
- return {
- page_index: pageIndex,
- page_size: pageSize,
- show: showValue,
- };
- }
-);
+): TrustedAppsListPageLocation => state.location;
export const getListErrorMessage = (
state: Immutable
): string | undefined => {
- return getCurrentResourceError(state.listView.currentListResourceState)?.message;
+ return getCurrentResourceError(state.listView.listResourceState)?.message;
};
export const isListLoading = (state: Immutable): boolean => {
- return isLoadingResourceState(state.listView.currentListResourceState);
+ return isLoadingResourceState(state.listView.listResourceState);
};
export const isDeletionDialogOpen = (state: Immutable): boolean => {
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts
index 020a87f526e52..c23b6ceae7b07 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts
@@ -5,11 +5,12 @@
*/
import { combineReducers, createStore } from 'redux';
-import { ServerApiError } from '../../../../common/types';
import { TrustedApp } from '../../../../../common/endpoint/types';
import { RoutingAction } from '../../../../common/store/routing';
import {
+ MANAGEMENT_DEFAULT_PAGE,
+ MANAGEMENT_DEFAULT_PAGE_SIZE,
MANAGEMENT_STORE_GLOBAL_NAMESPACE,
MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE,
} from '../../../common/constants';
@@ -105,54 +106,22 @@ export const createListComplexLoadingResourceState = (
)
);
-export const createDefaultPaginationInfo = () => ({ index: 0, size: 20 });
-
-export const createDefaultListView = (
- freshDataTimestamp: number
-): TrustedAppsListPageState['listView'] => ({
- currentListResourceState: createUninitialisedResourceState(),
- currentPaginationInfo: createDefaultPaginationInfo(),
- freshDataTimestamp,
- show: undefined,
-});
-
-export const createLoadingListViewWithPagination = (
- freshDataTimestamp: number,
- currentPaginationInfo: PaginationInfo,
- previousState: StaleResourceState = createUninitialisedResourceState()
-): TrustedAppsListPageState['listView'] => ({
- currentListResourceState: { type: 'LoadingResourceState', previousState },
- currentPaginationInfo,
- freshDataTimestamp,
- show: undefined,
+export const createDefaultPaginationInfo = () => ({
+ index: MANAGEMENT_DEFAULT_PAGE,
+ size: MANAGEMENT_DEFAULT_PAGE_SIZE,
});
export const createLoadedListViewWithPagination = (
freshDataTimestamp: number,
paginationInfo: PaginationInfo = createDefaultPaginationInfo(),
- currentPaginationInfo: PaginationInfo = createDefaultPaginationInfo(),
totalItemsCount: number = 200
): TrustedAppsListPageState['listView'] => ({
- currentListResourceState: createListLoadedResourceState(
+ listResourceState: createListLoadedResourceState(
paginationInfo,
totalItemsCount,
freshDataTimestamp
),
- currentPaginationInfo,
- freshDataTimestamp,
- show: undefined,
-});
-
-export const createFailedListViewWithPagination = (
- freshDataTimestamp: number,
- currentPaginationInfo: PaginationInfo,
- error: ServerApiError,
- lastLoadedState?: LoadedResourceState
-): TrustedAppsListPageState['listView'] => ({
- currentListResourceState: { type: 'FailedResourceState', error, lastLoadedState },
- currentPaginationInfo,
freshDataTimestamp,
- show: undefined,
});
export const createUserChangedUrlAction = (path: string, search: string = ''): RoutingAction => {
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx
index 7f7eae18b0816..7d30e81898cf2 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx
@@ -83,7 +83,7 @@ export const ConditionEntry = memo(
'xpack.securitySolution.trustedapps.logicalConditionBuilder.entry.field.path',
{ defaultMessage: 'Path' }
),
- value: 'process.path.text',
+ value: 'process.executable.text',
},
];
}, []);
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx
index 713e5e7095e12..4b64030a702c5 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx
@@ -30,7 +30,7 @@ storiesOf('TrustedApps|TrustedAppCard', module)
trustedApp.created_at = '2020-09-17T14:52:33.899Z';
trustedApp.entries = [
{
- field: 'process.path.text',
+ field: 'process.executable.text',
operator: 'included',
type: 'match',
value: '/some/path/on/file/system',
@@ -44,7 +44,7 @@ storiesOf('TrustedApps|TrustedAppCard', module)
trustedApp.created_at = '2020-09-17T14:52:33.899Z';
trustedApp.entries = [
{
- field: 'process.path.text',
+ field: 'process.executable.text',
operator: 'included',
type: 'match',
value: '/some/path/on/file/system',
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_list.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_list.tsx
index d0c1fb477ea46..ae1f314842aab 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_list.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_list.tsx
@@ -23,8 +23,8 @@ import { MANAGEMENT_PAGE_SIZE_OPTIONS } from '../../../common/constants';
import { getTrustedAppsListPath } from '../../../common/routing';
import {
- getListCurrentPageIndex,
- getListCurrentPageSize,
+ getCurrentLocationPageIndex,
+ getCurrentLocationPageSize,
getListErrorMessage,
getListItems,
getListTotalItemsCount,
@@ -149,8 +149,8 @@ const getColumnDefinitions = (context: TrustedAppsListContext): ColumnsList => {
export const TrustedAppsList = memo(() => {
const [detailsMap, setDetailsMap] = useState({});
- const pageIndex = useTrustedAppsSelector(getListCurrentPageIndex);
- const pageSize = useTrustedAppsSelector(getListCurrentPageSize);
+ const pageIndex = useTrustedAppsSelector(getCurrentLocationPageIndex);
+ const pageSize = useTrustedAppsSelector(getCurrentLocationPageSize);
const totalItemCount = useTrustedAppsSelector(getListTotalItemsCount);
const listItems = useTrustedAppsSelector(getListItems);
const dispatch = useDispatch();
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx
index 878818d9b77fe..d63cda5b513dc 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx
@@ -15,27 +15,26 @@ import { TrustedAppsNotifications } from './trusted_apps_notifications';
import { CreateTrustedAppFlyout } from './components/create_trusted_app_flyout';
import { getTrustedAppsListPath } from '../../../common/routing';
import { useTrustedAppsSelector } from './hooks';
-import { getListCurrentShowValue, getListUrlSearchParams } from '../store/selectors';
+import { getCurrentLocation } from '../store/selectors';
import { TrustedAppsListPageRouteState } from '../../../../../common/endpoint/types';
import { useNavigateToAppEventHandler } from '../../../../common/hooks/endpoint/use_navigate_to_app_event_handler';
export const TrustedAppsPage = memo(() => {
const history = useHistory();
const { state: routeState } = useLocation();
- const urlParams = useTrustedAppsSelector(getListUrlSearchParams);
- const showAddFlout = useTrustedAppsSelector(getListCurrentShowValue) === 'create';
+ const location = useTrustedAppsSelector(getCurrentLocation);
const handleAddButtonClick = useCallback(() => {
history.push(
getTrustedAppsListPath({
- ...urlParams,
+ ...location,
show: 'create',
})
);
- }, [history, urlParams]);
+ }, [history, location]);
const handleAddFlyoutClose = useCallback(() => {
- const { show, ...paginationParamsOnly } = urlParams;
+ const { show, ...paginationParamsOnly } = location;
history.push(getTrustedAppsListPath(paginationParamsOnly));
- }, [history, urlParams]);
+ }, [history, location]);
const backButton = useMemo(() => {
if (routeState && routeState.onBackButtonNavigateTo) {
@@ -50,7 +49,7 @@ export const TrustedAppsPage = memo(() => {
@@ -82,7 +81,7 @@ export const TrustedAppsPage = memo(() => {
>
- {showAddFlout && (
+ {location.show === 'create' && (
(({ noTimelinesMessage, onOpenTimeline, timelines }) => {
- if (timelines.length === 0) {
+ isLastItem: boolean;
+}
+
+const RecentTimelinesItem = React.memo(
+ ({ timeline, onOpenTimeline, isLastItem }) => {
+ const handleClick = useCallback(
+ () =>
+ onOpenTimeline({
+ duplicate: true,
+ timelineId: `${timeline.savedObjectId}`,
+ }),
+ [onOpenTimeline, timeline.savedObjectId]
+ );
+
+ const render = useCallback(
+ (showHoverContent) => (
+
+
+
+
+ {timeline.description && timeline.description.length && (
+
+ {timeline.description}
+
+ )}
+
+
+ {showHoverContent && (
+
+
+
+
+
+ )}
+
+ ),
+ [handleClick, onOpenTimeline, timeline]
+ );
+
return (
<>
-
- {noTimelinesMessage}
-
+
+ <>{!isLastItem && }>
>
);
}
+);
- return (
- <>
- {timelines.map((t, i) => (
-
- (
-
-
-
-
- {t.description && t.description.length && (
-
- {t.description}
-
- )}
-
+RecentTimelinesItem.displayName = 'RecentTimelinesItem';
- {showHoverContent && (
-
-
-
- onOpenTimeline({
- duplicate: true,
- timelineId: `${t.savedObjectId}`,
- })
- }
- size="s"
- />
-
-
- )}
-
- )}
+interface RecentTimelinesProps {
+ noTimelinesMessage: string;
+ onOpenTimeline: OnOpenTimeline;
+ timelines: OpenTimelineResult[];
+}
+
+export const RecentTimelines = React.memo(
+ ({ noTimelinesMessage, onOpenTimeline, timelines }) => {
+ const content = useMemo(
+ () =>
+ timelines.map((timeline, index) => (
+
- <>{i !== timelines.length - 1 && }>
-
- ))}
- >
- );
-});
+ )),
+ [onOpenTimeline, timelines]
+ );
+
+ if (timelines.length === 0) {
+ return (
+ <>
+
+ {noTimelinesMessage}
+
+ >
+ );
+ }
+
+ return <>{content}>;
+ }
+);
RecentTimelines.displayName = 'RecentTimelines';
diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/button/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/button/index.tsx
index 954ae0b6a0d40..72fa20c9f152d 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/flyout/button/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/button/index.tsx
@@ -4,11 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { noop } from 'lodash/fp';
import { EuiButton, EuiNotificationBadge, EuiPanel } from '@elastic/eui';
import { rgba } from 'polished';
import React, { useMemo } from 'react';
import styled from 'styled-components';
+import deepEqual from 'fast-deep-equal';
import { IS_DRAGGING_CLASS_NAME } from '../../../../common/components/drag_and_drop/helpers';
import { DataProvider } from '../../timeline/data_providers/data_provider';
@@ -88,6 +88,18 @@ export const FlyoutButton = React.memo(
const badgeCount = useMemo(() => getBadgeCount(dataProviders), [dataProviders]);
const { browserFields } = useSourcererScope(SourcererScopeName.timeline);
+ const badgeStyles: React.CSSProperties = useMemo(
+ () => ({
+ left: '-9px',
+ position: 'relative',
+ top: '-6px',
+ transform: 'rotate(90deg)',
+ visibility: dataProviders.length !== 0 ? 'inherit' : 'hidden',
+ zIndex: 10,
+ }),
+ [dataProviders.length]
+ );
+
if (!show) {
return null;
}
@@ -108,18 +120,7 @@ export const FlyoutButton = React.memo(
>
{i18n.FLYOUT_BUTTON}
-
+
{badgeCount}
@@ -128,11 +129,6 @@ export const FlyoutButton = React.memo(
browserFields={browserFields}
timelineId={timelineId}
dataProviders={dataProviders}
- onDataProviderEdited={noop}
- onDataProviderRemoved={noop}
- onToggleDataProviderEnabled={noop}
- onToggleDataProviderExcluded={noop}
- onToggleDataProviderType={noop}
/>
@@ -140,7 +136,7 @@ export const FlyoutButton = React.memo(
},
(prevProps, nextProps) =>
prevProps.show === nextProps.show &&
- prevProps.dataProviders === nextProps.dataProviders &&
+ deepEqual(prevProps.dataProviders, nextProps.dataProviders) &&
prevProps.timelineId === nextProps.timelineId
);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.tsx
index e4148b5581435..091bb41bc2080 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.tsx
@@ -5,7 +5,7 @@
*/
import { isArray, isEmpty, isString, uniq } from 'lodash/fp';
-import React from 'react';
+import React, { useCallback, useMemo } from 'react';
import {
DragEffects,
@@ -71,16 +71,25 @@ const NonDecoratedIpComponent: React.FC<{
fieldName: string;
truncate?: boolean;
value: string | object | null | undefined;
-}> = ({ contextId, eventId, fieldName, truncate, value }) => (
-
+}> = ({ contextId, eventId, fieldName, truncate, value }) => {
+ const key = useMemo(
+ () =>
+ `non-decorated-ip-draggable-wrapper-${getUniqueId({
+ contextId,
+ eventId,
+ fieldName,
+ address: value,
+ })}`,
+ [contextId, eventId, fieldName, value]
+ );
+
+ const dataProviderProp = useMemo(
+ () => getDataProvider({ contextId, eventId, fieldName, address: value }),
+ [contextId, eventId, fieldName, value]
+ );
+
+ const render = useCallback(
+ (dataProvider, _, snapshot) =>
snapshot.isDragging ? (
@@ -89,47 +98,107 @@ const NonDecoratedIpComponent: React.FC<{
getOrEmptyTagFromValue(value)
) : (
getOrEmptyTagFromValue(tryStringify(value))
- )
- }
- truncate={truncate}
- />
-);
+ ),
+ [value]
+ );
+
+ return (
+
+ );
+};
const NonDecoratedIp = React.memo(NonDecoratedIpComponent);
-const AddressLinksComponent: React.FC<{
+interface AddressLinksItemProps extends Omit {
+ address: string;
+}
+
+const AddressLinksItemComponent: React.FC = ({
+ address,
+ contextId,
+ eventId,
+ fieldName,
+ truncate,
+}) => {
+ const key = useMemo(
+ () =>
+ `address-links-draggable-wrapper-${getUniqueId({
+ contextId,
+ eventId,
+ fieldName,
+ address,
+ })}`,
+ [address, contextId, eventId, fieldName]
+ );
+
+ const dataProviderProp = useMemo(
+ () => getDataProvider({ contextId, eventId, fieldName, address }),
+ [address, contextId, eventId, fieldName]
+ );
+
+ const render = useCallback(
+ (_props, _provided, snapshot) =>
+ snapshot.isDragging ? (
+
+
+
+ ) : (
+
+ ),
+ [address, dataProviderProp]
+ );
+
+ return (
+
+ );
+};
+
+const AddressLinksItem = React.memo(AddressLinksItemComponent);
+
+interface AddressLinksProps {
addresses: string[];
contextId: string;
eventId: string;
fieldName: string;
truncate?: boolean;
-}> = ({ addresses, contextId, eventId, fieldName, truncate }) => (
- <>
- {uniq(addresses).map((address) => (
-
- snapshot.isDragging ? (
-
-
-
- ) : (
-
- )
- }
- truncate={truncate}
- />
- ))}
- >
-);
+}
+
+const AddressLinksComponent: React.FC = ({
+ addresses,
+ contextId,
+ eventId,
+ fieldName,
+ truncate,
+}) => {
+ const uniqAddresses = useMemo(() => uniq(addresses), [addresses]);
+
+ const content = useMemo(
+ () =>
+ uniqAddresses.map((address) => (
+
+ )),
+ [contextId, eventId, fieldName, truncate, uniqAddresses]
+ );
+
+ return <>{content}>;
+};
const AddressLinks = React.memo(AddressLinksComponent);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/__snapshots__/timeline.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/__snapshots__/timeline.test.tsx.snap
index 18a648f2abfaa..153128fb41826 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/__snapshots__/timeline.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/__snapshots__/timeline.test.tsx.snap
@@ -646,34 +646,7 @@ In other use cases the message field can be used to concatenate different values
dataProviders={
Array [
Object {
- "and": Array [
- Object {
- "and": Array [],
- "enabled": true,
- "excluded": false,
- "id": "id-Provider 2",
- "kqlQuery": "",
- "name": "Provider 2",
- "queryMatch": Object {
- "field": "name",
- "operator": ":",
- "value": "Provider 2",
- },
- },
- Object {
- "and": Array [],
- "enabled": true,
- "excluded": false,
- "id": "id-Provider 3",
- "kqlQuery": "",
- "name": "Provider 3",
- "queryMatch": Object {
- "field": "name",
- "operator": ":",
- "value": "Provider 3",
- },
- },
- ],
+ "and": Array [],
"enabled": true,
"excluded": false,
"id": "id-Provider 1",
@@ -921,11 +894,6 @@ In other use cases the message field can be used to concatenate different values
loadingSourcerer={false}
onChangeItemsPerPage={[MockFunction]}
onClose={[MockFunction]}
- onDataProviderEdited={[MockFunction]}
- onDataProviderRemoved={[MockFunction]}
- onToggleDataProviderEnabled={[MockFunction]}
- onToggleDataProviderExcluded={[MockFunction]}
- onToggleDataProviderType={[MockFunction]}
show={true}
showCallOutUnauthorizedMsg={false}
sort={
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/auto_save_warning/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/auto_save_warning/index.tsx
index 210af7a571569..98faa84db851e 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/auto_save_warning/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/auto_save_warning/index.tsx
@@ -11,84 +11,69 @@ import {
EuiGlobalToastListToast as Toast,
} from '@elastic/eui';
import { getOr } from 'lodash/fp';
-import React from 'react';
-import { connect, ConnectedProps } from 'react-redux';
+import React, { useCallback, useMemo } from 'react';
+import { useDispatch, useSelector, shallowEqual } from 'react-redux';
-import { State } from '../../../../common/store';
-import { setTimelineRangeDatePicker as dispatchSetTimelineRangeDatePicker } from '../../../../common/store/inputs/actions';
+import { setTimelineRangeDatePicker } from '../../../../common/store/inputs/actions';
import { timelineActions, timelineSelectors } from '../../../store/timeline';
-import { AutoSavedWarningMsg } from '../../../store/timeline/types';
import { useStateToaster } from '../../../../common/components/toasters';
import * as i18n from './translations';
-const AutoSaveWarningMsgComponent = React.memo(
- ({
- newTimelineModel,
- setTimelineRangeDatePicker,
- timelineId,
- updateAutoSaveMsg,
- updateTimeline,
- }) => {
- const dispatchToaster = useStateToaster()[1];
+const AutoSaveWarningMsgComponent = () => {
+ const dispatch = useDispatch();
+ const dispatchToaster = useStateToaster()[1];
+ const { timelineId, newTimelineModel } = useSelector(
+ timelineSelectors.autoSaveMsgSelector,
+ shallowEqual
+ );
+
+ const handleClick = useCallback(() => {
if (timelineId != null && newTimelineModel != null) {
- const toast: Toast = {
- id: 'AutoSaveWarningMsg',
- title: i18n.TITLE,
- color: 'warning',
- iconType: 'alert',
- toastLifeTimeMs: 10000,
- text: (
- <>
- {i18n.DESCRIPTION}
-
-
- {
- updateTimeline({ id: timelineId, timeline: newTimelineModel });
- updateAutoSaveMsg({ timelineId: null, newTimelineModel: null });
- setTimelineRangeDatePicker({
- from: getOr(0, 'dateRange.start', newTimelineModel),
- to: getOr(0, 'dateRange.end', newTimelineModel),
- });
- }}
- >
- {i18n.REFRESH_TIMELINE}
-
-
-
- >
- ),
- };
- dispatchToaster({
- type: 'addToaster',
- toast,
- });
+ dispatch(timelineActions.updateTimeline({ id: timelineId, timeline: newTimelineModel }));
+ dispatch(timelineActions.updateAutoSaveMsg({ timelineId: null, newTimelineModel: null }));
+ dispatch(
+ setTimelineRangeDatePicker({
+ from: getOr(0, 'dateRange.start', newTimelineModel),
+ to: getOr(0, 'dateRange.end', newTimelineModel),
+ })
+ );
}
+ }, [dispatch, newTimelineModel, timelineId]);
- return null;
- }
-);
-
-AutoSaveWarningMsgComponent.displayName = 'AutoSaveWarningMsgComponent';
-
-const mapStateToProps = (state: State) => {
- const autoSaveMessage: AutoSavedWarningMsg = timelineSelectors.autoSaveMsgSelector(state);
+ const TextComponent = useMemo(
+ () => (
+ <>
+ {i18n.DESCRIPTION}
+
+
+
+ {i18n.REFRESH_TIMELINE}
+
+
+
+ >
+ ),
+ [handleClick]
+ );
- return {
- timelineId: autoSaveMessage.timelineId,
- newTimelineModel: autoSaveMessage.newTimelineModel,
- };
-};
+ if (timelineId != null && newTimelineModel != null) {
+ const toast: Toast = {
+ id: 'AutoSaveWarningMsg',
+ title: i18n.TITLE,
+ color: 'warning',
+ iconType: 'alert',
+ toastLifeTimeMs: 10000,
+ text: TextComponent,
+ };
+ dispatchToaster({
+ type: 'addToaster',
+ toast,
+ });
+ }
-const mapDispatchToProps = {
- setTimelineRangeDatePicker: dispatchSetTimelineRangeDatePicker,
- updateAutoSaveMsg: timelineActions.updateAutoSaveMsg,
- updateTimeline: timelineActions.updateTimeline,
+ return null;
};
-const connector = connect(mapStateToProps, mapDispatchToProps);
-
-type PropsFromRedux = ConnectedProps;
+AutoSaveWarningMsgComponent.displayName = 'AutoSaveWarningMsgComponent';
-export const AutoSaveWarningMsg = connector(AutoSaveWarningMsgComponent);
+export const AutoSaveWarningMsg = React.memo(AutoSaveWarningMsgComponent);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx
index 3352639fa95f8..c4c4e0e0c7065 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx
@@ -5,7 +5,7 @@
*/
import { EuiButtonIcon } from '@elastic/eui';
-import React from 'react';
+import React, { useCallback } from 'react';
import { ColumnHeaderOptions } from '../../../../../../timelines/store/timeline/model';
import { OnColumnRemoved } from '../../../events';
@@ -26,20 +26,27 @@ interface Props {
export const CloseButton = React.memo<{
columnId: string;
onColumnRemoved: OnColumnRemoved;
-}>(({ columnId, onColumnRemoved }) => (
- ) => {
+}>(({ columnId, onColumnRemoved }) => {
+ const handleClick = useCallback(
+ (event: React.MouseEvent) => {
// To avoid a re-sorting when you delete a column
event.preventDefault();
event.stopPropagation();
onColumnRemoved(columnId);
- }}
- />
-));
+ },
+ [columnId, onColumnRemoved]
+ );
+
+ return (
+
+ );
+});
CloseButton.displayName = 'CloseButton';
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx
index 617b2935ee75c..6e21446944573 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx
@@ -78,6 +78,29 @@ const ColumnHeaderComponent: React.FC = ({
[timelineId, header.id]
);
+ const DraggableContent = useCallback(
+ (dragProvided) => (
+
+
+
+
+
+ ),
+ [header, onColumnRemoved, onColumnSorted, onFilterChange, sort, timelineId]
+ );
+
return (
= ({
index={draggableIndex}
key={header.id}
>
- {(dragProvided) => (
-
-
-
-
-
- )}
+ {DraggableContent}
);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx
index 6e802053ab29f..f4d4cf29ba38b 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx
@@ -200,6 +200,22 @@ export const ColumnHeadersComponent = ({
[globalFullScreen, timelineId, timelineFullScreen]
);
+ const DroppableContent = useCallback(
+ (dropProvided, snapshot) => (
+ <>
+
+ {ColumnHeaderList}
+
+ >
+ ),
+ [ColumnHeaderList]
+ );
+
return (
@@ -275,18 +291,7 @@ export const ColumnHeadersComponent = ({
type={DRAG_TYPE_FIELD}
renderClone={renderClone}
>
- {(dropProvided, snapshot) => (
- <>
-
- {ColumnHeaderList}
-
- >
- )}
+ {DroppableContent}
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx
index 3b6585013c8d3..df5c48ad012a6 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx
@@ -151,13 +151,17 @@ export const EventColumnView = React.memo(
/>,
]
: []),
- ,
+ ...(eventType !== 'raw'
+ ? [
+ ,
+ ]
+ : []),
],
[
associateNote,
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx
index d71af86c80247..ee68c270e9aba 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx
@@ -97,6 +97,8 @@ const TOP_OFFSET = 50;
*/
const BOTTOM_OFFSET = -500;
+const VISIBILITY_SENSOR_OFFSET = { top: TOP_OFFSET, bottom: BOTTOM_OFFSET };
+
const emptyNotes: string[] = [];
const EventsTrSupplementContainerWrapper = React.memo(({ children }) => {
@@ -173,105 +175,145 @@ const StatefulEventComponent: React.FC = ({
// Number of current columns plus one for actions.
const columnCount = columnHeaders.length + 1;
+ const VisibilitySensorContent = useCallback(
+ ({ isVisible }) => {
+ if (isVisible || disableSensorVisibility) {
+ return (
+
+
+
+
+
+
+
+
+ {getRowRenderer(event.ecs, rowRenderers).renderRow({
+ browserFields,
+ data: event.ecs,
+ timelineId,
+ })}
+
+
+
+
+
+
+ );
+ } else {
+ // Height place holder for visibility detection as well as re-rendering sections.
+ const height =
+ divElement.current != null && divElement.current!.clientHeight
+ ? `${divElement.current!.clientHeight}px`
+ : DEFAULT_ROW_HEIGHT;
+
+ return ;
+ }
+ },
+ [
+ actionsColumnWidth,
+ associateNote,
+ browserFields,
+ columnCount,
+ columnHeaders,
+ columnRenderers,
+ detailsData,
+ disableSensorVisibility,
+ event._id,
+ event.data,
+ event.ecs,
+ eventIdToNoteIds,
+ expanded,
+ getNotesByIds,
+ isEventPinned,
+ isEventViewer,
+ loading,
+ loadingEventIds,
+ onColumnResized,
+ onPinEvent,
+ onRowSelected,
+ onToggleExpanded,
+ onToggleShowNotes,
+ onUnPinEvent,
+ onUpdateColumns,
+ refetch,
+ rowRenderers,
+ selectedEventIds,
+ showCheckboxes,
+ showNotes,
+ timelineId,
+ timelineStatus,
+ toggleColumn,
+ updateNote,
+ ]
+ );
+
return (
- {({ isVisible }) => {
- if (isVisible || disableSensorVisibility) {
- return (
-
-
-
-
-
-
-
-
- {getRowRenderer(event.ecs, rowRenderers).renderRow({
- browserFields,
- data: event.ecs,
- timelineId,
- })}
-
-
-
-
-
-
- );
- } else {
- // Height place holder for visibility detection as well as re-rendering sections.
- const height =
- divElement.current != null && divElement.current!.clientHeight
- ? `${divElement.current!.clientHeight}px`
- : DEFAULT_ROW_HEIGHT;
-
- return ;
- }
- }}
+ {VisibilitySensorContent}
);
};
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_signature.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_signature.tsx
index 1cd78178d017f..5d4821757d774 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_signature.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_signature.tsx
@@ -5,7 +5,7 @@
*/
import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
-import React from 'react';
+import React, { useCallback, useMemo } from 'react';
import styled from 'styled-components';
import {
@@ -19,7 +19,7 @@ import { Provider } from '../../../data_providers/provider';
import { TokensFlexItem } from '../helpers';
import { getBeginningTokens } from './suricata_links';
import { DefaultDraggable } from '../../../../../../common/components/draggables';
-import { IS_OPERATOR } from '../../../data_providers/data_provider';
+import { IS_OPERATOR, QueryOperator } from '../../../data_providers/data_provider';
export const SURICATA_SIGNATURE_FIELD_NAME = 'suricata.eve.alert.signature';
export const SURICATA_SIGNATURE_ID_FIELD_NAME = 'suricata.eve.alert.signature_id';
@@ -57,41 +57,49 @@ export const Tokens = React.memo<{ tokens: string[] }>(({ tokens }) => (
Tokens.displayName = 'Tokens';
export const DraggableSignatureId = React.memo<{ id: string; signatureId: number }>(
- ({ id, signatureId }) => (
-
-
- snapshot.isDragging ? (
-
-
-
- ) : (
-
-
- {signatureId}
-
-
- )
- }
- />
-
- )
+ ({ id, signatureId }) => {
+ const dataProviderProp = useMemo(
+ () => ({
+ and: [],
+ enabled: true,
+ id: escapeDataProviderId(`suricata-draggable-signature-id-${id}-sig-${signatureId}`),
+ name: String(signatureId),
+ excluded: false,
+ kqlQuery: '',
+ queryMatch: {
+ field: SURICATA_SIGNATURE_ID_FIELD_NAME,
+ value: signatureId,
+ operator: IS_OPERATOR as QueryOperator,
+ },
+ }),
+ [id, signatureId]
+ );
+
+ const render = useCallback(
+ (dataProvider, _, snapshot) =>
+ snapshot.isDragging ? (
+
+
+
+ ) : (
+
+
+ {signatureId}
+
+
+ ),
+ [signatureId]
+ );
+
+ return (
+
+
+
+ );
+ }
);
DraggableSignatureId.displayName = 'DraggableSignatureId';
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx
index 07e32a9a4e5d1..9ef579706f118 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx
@@ -6,7 +6,7 @@
import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
import { get } from 'lodash/fp';
-import React from 'react';
+import React, { useCallback, useMemo } from 'react';
import styled from 'styled-components';
import { Ecs } from '../../../../../../../common/ecs';
@@ -17,7 +17,7 @@ import {
import { escapeDataProviderId } from '../../../../../../common/components/drag_and_drop/helpers';
import { GoogleLink, ReputationLink } from '../../../../../../common/components/links';
import { Provider } from '../../../data_providers/provider';
-import { IS_OPERATOR } from '../../../data_providers/data_provider';
+import { IS_OPERATOR, QueryOperator } from '../../../data_providers/data_provider';
import * as i18n from './translations';
@@ -68,42 +68,46 @@ export const DraggableZeekElement = React.memo<{
field: string;
value: string | null | undefined;
stringRenderer?: StringRenderer;
-}>(({ id, field, value, stringRenderer = defaultStringRenderer }) =>
- value != null ? (
+}>(({ id, field, value, stringRenderer = defaultStringRenderer }) => {
+ const dataProviderProp = useMemo(
+ () => ({
+ and: [],
+ enabled: true,
+ id: escapeDataProviderId(`draggable-zeek-element-draggable-wrapper-${id}-${field}-${value}`),
+ name: value!,
+ excluded: false,
+ kqlQuery: '',
+ queryMatch: {
+ field,
+ value: value!,
+ operator: IS_OPERATOR as QueryOperator,
+ },
+ }),
+ [field, id, value]
+ );
+
+ const render = useCallback(
+ (dataProvider, _, snapshot) =>
+ snapshot.isDragging ? (
+
+
+
+ ) : (
+
+
+ {stringRenderer(value!)}
+
+
+ ),
+ [field, stringRenderer, value]
+ );
+
+ return value != null ? (
-
- snapshot.isDragging ? (
-
-
-
- ) : (
-
-
- {stringRenderer(value)}
-
-
- )
- }
- />
+
- ) : null
-);
+ ) : null;
+});
DraggableZeekElement.displayName = 'DraggableZeekElement';
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/data_providers.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/data_providers.test.tsx.snap
index 14304b99263ac..a8818517fb94b 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/data_providers.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/data_providers.test.tsx.snap
@@ -144,11 +144,6 @@ exports[`DataProviders rendering renders correctly against snapshot 1`] = `
},
]
}
- onDataProviderEdited={[MockFunction]}
- onDataProviderRemoved={[MockFunction]}
- onToggleDataProviderEnabled={[MockFunction]}
- onToggleDataProviderExcluded={[MockFunction]}
- onToggleDataProviderType={[MockFunction]}
timelineId="foo"
/>
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/providers.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/providers.test.tsx.snap
index a86c99cbc094a..281a26b08df67 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/providers.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/providers.test.tsx.snap
@@ -2,556 +2,1717 @@
exports[`Providers rendering renders correctly against snapshot 1`] = `
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
+
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
`;
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/data_providers.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/data_providers.test.tsx
index d48be25b08897..a7ae14dea510f 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/data_providers.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/data_providers.test.tsx
@@ -41,11 +41,6 @@ describe('DataProviders', () => {
data-test-subj="dataProviders-container"
dataProviders={mockDataProviders}
timelineId="foo"
- onDataProviderEdited={jest.fn()}
- onDataProviderRemoved={jest.fn()}
- onToggleDataProviderEnabled={jest.fn()}
- onToggleDataProviderExcluded={jest.fn()}
- onToggleDataProviderType={jest.fn()}
/>
@@ -58,16 +53,7 @@ describe('DataProviders', () => {
const wrapper = mount(
-
+
);
@@ -77,16 +63,7 @@ describe('DataProviders', () => {
test('it renders the data providers', () => {
const wrapper = mount(
-
+
);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx
index c9e06f89af41c..b892ca089eb4c 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx
@@ -14,13 +14,6 @@ import {
droppableTimelineProvidersPrefix,
IS_DRAGGING_CLASS_NAME,
} from '../../../../common/components/drag_and_drop/helpers';
-import {
- OnDataProviderEdited,
- OnDataProviderRemoved,
- OnToggleDataProviderEnabled,
- OnToggleDataProviderExcluded,
- OnToggleDataProviderType,
-} from '../events';
import { DataProvider } from './data_provider';
import { Empty } from './empty';
@@ -31,11 +24,6 @@ interface Props {
browserFields: BrowserFields;
timelineId: string;
dataProviders: DataProvider[];
- onDataProviderEdited: OnDataProviderEdited;
- onDataProviderRemoved: OnDataProviderRemoved;
- onToggleDataProviderEnabled: OnToggleDataProviderEnabled;
- onToggleDataProviderExcluded: OnToggleDataProviderExcluded;
- onToggleDataProviderType: OnToggleDataProviderType;
}
const DropTargetDataProvidersContainer = styled.div`
@@ -91,48 +79,32 @@ const getDroppableId = (id: string): string => `${droppableTimelineProvidersPref
* the user to drop anything with a facet count into
* the data pro section.
*/
-export const DataProviders = React.memo(
- ({
- browserFields,
- dataProviders,
+export const DataProviders = React.memo(({ browserFields, dataProviders, timelineId }) => {
+ const { getManageTimelineById } = useManageTimeline();
+ const isLoading = useMemo(() => getManageTimelineById(timelineId).isLoading, [
+ getManageTimelineById,
timelineId,
- onDataProviderEdited,
- onDataProviderRemoved,
- onToggleDataProviderEnabled,
- onToggleDataProviderExcluded,
- onToggleDataProviderType,
- }) => {
- const { getManageTimelineById } = useManageTimeline();
- const isLoading = useMemo(() => getManageTimelineById(timelineId).isLoading, [
- getManageTimelineById,
- timelineId,
- ]);
- return (
-
-
- {dataProviders != null && dataProviders.length ? (
-
- ) : (
-
-
-
- )}
-
-
- );
- }
-);
+ ]);
+ return (
+
+
+ {dataProviders != null && dataProviders.length ? (
+
+ ) : (
+
+
+
+ )}
+
+
+ );
+});
DataProviders.displayName = 'DataProviders';
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_badge.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_badge.tsx
index bf2094e7659ee..b66a332fc977d 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_badge.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_badge.tsx
@@ -91,7 +91,7 @@ const ConvertFieldBadge = styled(ProviderFieldBadge)`
}
`;
-const TemplateFieldBadge: React.FC = ({ type, toggleType }) => {
+const TemplateFieldBadgeComponent: React.FC = ({ type, toggleType }) => {
if (type !== DataProviderType.template) {
return (
{i18n.CONVERT_TO_TEMPLATE_FIELD}
@@ -101,6 +101,8 @@ const TemplateFieldBadge: React.FC = ({ type, toggleTyp
return {i18n.TEMPLATE_FIELD_LABEL} ;
};
+const TemplateFieldBadge = React.memo(TemplateFieldBadgeComponent);
+
interface ProviderBadgeProps {
deleteProvider: () => void;
field: string;
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx
index 0093c43a17137..fc06d37b9663f 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx
@@ -117,21 +117,38 @@ export const ProviderItemBadge = React.memo(
[unRegisterProvider]
);
- const button = (
-
+ const button = useMemo(
+ () => (
+
+ ),
+ [
+ deleteProvider,
+ field,
+ isEnabled,
+ isExcluded,
+ isLoading,
+ kqlQuery,
+ onToggleTypeProvider,
+ operator,
+ providerId,
+ timelineType,
+ togglePopover,
+ type,
+ val,
+ ]
);
return (
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.test.tsx
index 3f371349aa750..2df19605f813c 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.test.tsx
@@ -12,6 +12,7 @@ import { TestProviders } from '../../../../common/mock/test_providers';
import { DroppableWrapper } from '../../../../common/components/drag_and_drop/droppable_wrapper';
import { FilterManager } from '../../../../../../../../src/plugins/data/public';
+import { timelineActions } from '../../../store/timeline';
import { mockDataProviders } from './mock/mock_data_providers';
import { Providers } from './providers';
import { DELETE_CLASS_NAME, ENABLE_CLASS_NAME, EXCLUDE_CLASS_NAME } from './provider_item_actions';
@@ -24,27 +25,24 @@ describe('Providers', () => {
const isLoading: boolean = true;
const mount = useMountAppended();
const filterManager = new FilterManager(mockUiSettingsForFilterManager);
+ const mockOnDataProviderRemoved = jest.spyOn(timelineActions, 'removeProvider');
const manageTimelineForTesting = {
- foo: {
- ...getTimelineDefaults('foo'),
+ test: {
+ ...getTimelineDefaults('test'),
filterManager,
isLoading,
},
};
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
describe('rendering', () => {
test('renders correctly against snapshot', () => {
const wrapper = shallow(
-
+
);
expect(wrapper).toMatchSnapshot();
});
@@ -53,16 +51,7 @@ describe('Providers', () => {
const wrapper = mount(
-
+
);
@@ -77,20 +66,10 @@ describe('Providers', () => {
describe('#onDataProviderRemoved', () => {
test('it invokes the onDataProviderRemoved callback when the close button is clicked', () => {
- const mockOnDataProviderRemoved = jest.fn();
const wrapper = mount(
-
+
);
@@ -98,25 +77,15 @@ describe('Providers', () => {
.find('[data-test-subj="providerBadge"] [data-euiicon-type]')
.first()
.simulate('click');
- expect(mockOnDataProviderRemoved.mock.calls[0][0]).toEqual('id-Provider 1');
+ expect(mockOnDataProviderRemoved.mock.calls[0][0].providerId).toEqual('id-Provider 1');
});
test('while loading data, it does NOT invoke the onDataProviderRemoved callback when the close button is clicked', () => {
- const mockOnDataProviderRemoved = jest.fn();
const wrapper = mount(
-
+
@@ -131,20 +100,10 @@ describe('Providers', () => {
});
test('it invokes the onDataProviderRemoved callback when you click on the option "Delete" in the provider menu', () => {
- const mockOnDataProviderRemoved = jest.fn();
const wrapper = mount(
-
+
);
@@ -156,25 +115,15 @@ describe('Providers', () => {
.find(`[data-test-subj="providerActions"] .${DELETE_CLASS_NAME}`)
.first()
.simulate('click');
- expect(mockOnDataProviderRemoved.mock.calls[0][0]).toEqual('id-Provider 1');
+ expect(mockOnDataProviderRemoved.mock.calls[0][0].providerId).toEqual('id-Provider 1');
});
test('while loading data, it does NOT invoke the onDataProviderRemoved callback when you click on the option "Delete" in the provider menu', () => {
- const mockOnDataProviderRemoved = jest.fn();
const wrapper = mount(
-
+
@@ -194,20 +143,14 @@ describe('Providers', () => {
describe('#onToggleDataProviderEnabled', () => {
test('it invokes the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the provider menu', () => {
- const mockOnToggleDataProviderEnabled = jest.fn();
+ const mockOnToggleDataProviderEnabled = jest.spyOn(
+ timelineActions,
+ 'updateDataProviderEnabled'
+ );
const wrapper = mount(
-
+
);
@@ -220,27 +163,23 @@ describe('Providers', () => {
.first()
.simulate('click');
expect(mockOnToggleDataProviderEnabled.mock.calls[0][0]).toEqual({
+ andProviderId: undefined,
enabled: false,
+ id: 'test',
providerId: 'id-Provider 1',
});
});
test('while loading data, it does NOT invoke the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the provider menu', () => {
- const mockOnToggleDataProviderEnabled = jest.fn();
+ const mockOnToggleDataProviderEnabled = jest.spyOn(
+ timelineActions,
+ 'updateDataProviderEnabled'
+ );
const wrapper = mount(
-
+
@@ -260,21 +199,15 @@ describe('Providers', () => {
describe('#onToggleDataProviderExcluded', () => {
test('it invokes the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the provider menu', () => {
- const onToggleDataProviderExcluded = jest.fn();
+ const mockOnToggleDataProviderExcluded = jest.spyOn(
+ timelineActions,
+ 'updateDataProviderExcluded'
+ );
const wrapper = mount(
-
+
);
@@ -288,29 +221,25 @@ describe('Providers', () => {
.first()
.simulate('click');
- expect(onToggleDataProviderExcluded.mock.calls[0][0]).toEqual({
+ expect(mockOnToggleDataProviderExcluded.mock.calls[0][0]).toEqual({
+ andProviderId: undefined,
excluded: true,
+ id: 'test',
providerId: 'id-Provider 1',
});
});
test('while loading data, it does NOT invoke the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the provider menu', () => {
- const onToggleDataProviderExcluded = jest.fn();
+ const mockOnToggleDataProviderExcluded = jest.spyOn(
+ timelineActions,
+ 'updateDataProviderExcluded'
+ );
const wrapper = mount(
-
+
@@ -325,7 +254,7 @@ describe('Providers', () => {
.first()
.simulate('click');
- expect(onToggleDataProviderExcluded).not.toBeCalled();
+ expect(mockOnToggleDataProviderExcluded).not.toBeCalled();
});
});
@@ -337,16 +266,7 @@ describe('Providers', () => {
const wrapper = mount(
-
+
);
@@ -364,21 +284,11 @@ describe('Providers', () => {
test('it invokes the onDataProviderRemoved callback when you click on the close button is clicked', () => {
const dataProviders = mockDataProviders.slice(0, 1);
dataProviders[0].and = mockDataProviders.slice(1, 3);
- const mockOnDataProviderRemoved = jest.fn();
const wrapper = mount(
-
+
);
@@ -392,28 +302,22 @@ describe('Providers', () => {
wrapper.update();
- expect(mockOnDataProviderRemoved.mock.calls[0]).toEqual(['id-Provider 1', 'id-Provider 2']);
+ expect(mockOnDataProviderRemoved.mock.calls[0][0]).toEqual({
+ andProviderId: 'id-Provider 2',
+ id: 'test',
+ providerId: 'id-Provider 1',
+ });
});
test('while loading data, it does NOT invoke the onDataProviderRemoved callback when you click on the close button is clicked', () => {
const dataProviders = mockDataProviders.slice(0, 1);
dataProviders[0].and = mockDataProviders.slice(1, 3);
- const mockOnDataProviderRemoved = jest.fn();
const wrapper = mount(
-
+
@@ -434,21 +338,15 @@ describe('Providers', () => {
test('it invokes the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the provider menu', () => {
const dataProviders = mockDataProviders.slice(0, 1);
dataProviders[0].and = mockDataProviders.slice(1, 3);
- const mockOnToggleDataProviderEnabled = jest.fn();
+ const mockOnToggleDataProviderEnabled = jest.spyOn(
+ timelineActions,
+ 'updateDataProviderEnabled'
+ );
const wrapper = mount(
-
+
);
@@ -470,6 +368,7 @@ describe('Providers', () => {
expect(mockOnToggleDataProviderEnabled.mock.calls[0][0]).toEqual({
andProviderId: 'id-Provider 2',
enabled: false,
+ id: 'test',
providerId: 'id-Provider 1',
});
});
@@ -477,22 +376,16 @@ describe('Providers', () => {
test('while loading data, it does NOT invoke the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the provider menu', () => {
const dataProviders = mockDataProviders.slice(0, 1);
dataProviders[0].and = mockDataProviders.slice(1, 3);
- const mockOnToggleDataProviderEnabled = jest.fn();
+ const mockOnToggleDataProviderEnabled = jest.spyOn(
+ timelineActions,
+ 'updateDataProviderEnabled'
+ );
const wrapper = mount(
-
+
@@ -518,21 +411,15 @@ describe('Providers', () => {
test('it invokes the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the provider menu', () => {
const dataProviders = mockDataProviders.slice(0, 1);
dataProviders[0].and = mockDataProviders.slice(1, 3);
- const mockOnToggleDataProviderExcluded = jest.fn();
+ const mockOnToggleDataProviderExcluded = jest.spyOn(
+ timelineActions,
+ 'updateDataProviderExcluded'
+ );
const wrapper = mount(
-
+
);
@@ -554,6 +441,7 @@ describe('Providers', () => {
expect(mockOnToggleDataProviderExcluded.mock.calls[0][0]).toEqual({
andProviderId: 'id-Provider 2',
excluded: true,
+ id: 'test',
providerId: 'id-Provider 1',
});
});
@@ -561,22 +449,16 @@ describe('Providers', () => {
test('while loading data, it does NOT invoke the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the provider menu', () => {
const dataProviders = mockDataProviders.slice(0, 1);
dataProviders[0].and = mockDataProviders.slice(1, 3);
- const mockOnToggleDataProviderExcluded = jest.fn();
+ const mockOnToggleDataProviderExcluded = jest.spyOn(
+ timelineActions,
+ 'updateDataProviderExcluded'
+ );
const wrapper = mount(
-
+
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx
index 1142bbc214d74..4b6f3c6701794 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx
@@ -6,9 +6,12 @@
import { EuiFlexGroup, EuiFlexItem, EuiFormHelpText, EuiSpacer } from '@elastic/eui';
import { rgba } from 'polished';
-import React, { Fragment, useMemo } from 'react';
+import React, { useCallback, useMemo } from 'react';
import { Draggable, DraggingStyle, Droppable, NotDraggingStyle } from 'react-beautiful-dnd';
-import styled, { css } from 'styled-components';
+import styled from 'styled-components';
+import { useDispatch } from 'react-redux';
+
+import { timelineActions } from '../../../store/timeline';
import { AndOrBadge } from '../../../../common/components/and_or_badge';
import { AddDataProviderPopover } from './add_data_provider_popover';
@@ -18,13 +21,6 @@ import {
IS_DRAGGING_CLASS_NAME,
getTimelineProviderDraggableId,
} from '../../../../common/components/drag_and_drop/helpers';
-import {
- OnDataProviderEdited,
- OnDataProviderRemoved,
- OnToggleDataProviderEnabled,
- OnToggleDataProviderExcluded,
- OnToggleDataProviderType,
-} from '../events';
import { DataProvider, DataProviderType, DataProvidersAnd, IS_OPERATOR } from './data_provider';
import { EMPTY_GROUP, flattenIntoAndGroups } from './helpers';
@@ -36,11 +32,6 @@ interface Props {
browserFields: BrowserFields;
timelineId: string;
dataProviders: DataProvider[];
- onDataProviderEdited: OnDataProviderEdited;
- onDataProviderRemoved: OnDataProviderRemoved;
- onToggleDataProviderEnabled: OnToggleDataProviderEnabled;
- onToggleDataProviderExcluded: OnToggleDataProviderExcluded;
- onToggleDataProviderType: OnToggleDataProviderType;
}
/**
@@ -74,12 +65,10 @@ const DroppableContainer = styled.div`
`;
const Parens = styled.span`
- ${({ theme }) => css`
- color: ${theme.eui.euiColorMediumShade};
- font-size: 32px;
- padding: 2px;
- user-select: none;
- `}
+ color: ${({ theme }) => theme.eui.euiColorMediumShade};
+ font-size: 32px;
+ padding: 2px;
+ user-select: none;
`;
const AndOrBadgeContainer = styled.div<{ hideBadge: boolean }>`
@@ -123,182 +112,269 @@ const getDataProviderValue = (dataProvider: DataProvidersAnd) =>
* 2) temporarily disabling a data provider
* 3) applying boolean negation to the data provider
*/
-export const Providers = React.memo(
- ({
- browserFields,
- timelineId,
- dataProviders,
- onDataProviderEdited,
- onDataProviderRemoved,
- onToggleDataProviderEnabled,
- onToggleDataProviderExcluded,
- onToggleDataProviderType,
- }) => {
- // Transform the dataProviders into flattened groups, and append an empty group
- const dataProviderGroups: DataProvidersAnd[][] = useMemo(
- () => [...flattenIntoAndGroups(dataProviders), ...EMPTY_GROUP],
- [dataProviders]
+export const Providers = React.memo(({ browserFields, timelineId, dataProviders }) => {
+ // Transform the dataProviders into flattened groups, and append an empty group
+ const dataProviderGroups: DataProvidersAnd[][] = useMemo(
+ () => [...flattenIntoAndGroups(dataProviders), ...EMPTY_GROUP],
+ [dataProviders]
+ );
+
+ const content = useMemo(
+ () =>
+ dataProviderGroups.map((group, groupIndex) => (
+
+ )),
+ [browserFields, dataProviderGroups, dataProviders, timelineId]
+ );
+
+ return {content}
;
+});
+
+Providers.displayName = 'Providers';
+
+interface DataProvidersGroupItem extends Omit {
+ index: number;
+ dataProvider: DataProvidersAnd;
+ group: DataProvidersAnd[];
+ groupIndex: number;
+}
+
+export const DataProvidersGroupItem = React.memo(
+ ({ browserFields, group, groupIndex, dataProvider, index, timelineId }) => {
+ const dispatch = useDispatch();
+ const draggableId = useMemo(
+ () =>
+ getTimelineProviderDraggableId({
+ dataProviderId: dataProvider.id,
+ groupIndex,
+ timelineId,
+ }),
+ [dataProvider.id, groupIndex, timelineId]
+ );
+
+ const handleDeleteProvider = useCallback(() => {
+ const payload = {
+ id: timelineId,
+ providerId: index > 0 ? group[0].id : dataProvider.id,
+ andProviderId: index > 0 ? dataProvider.id : undefined,
+ };
+
+ dispatch(timelineActions.removeProvider(payload));
+ }, [dataProvider.id, dispatch, group, index, timelineId]);
+
+ const handleToggleEnabledProvider = useCallback(() => {
+ const payload = {
+ id: timelineId,
+ providerId: index > 0 ? group[0].id : dataProvider.id,
+ enabled: !dataProvider.enabled,
+ andProviderId: index > 0 ? dataProvider.id : undefined,
+ };
+
+ dispatch(timelineActions.updateDataProviderEnabled(payload));
+ }, [dataProvider.enabled, dataProvider.id, dispatch, group, index, timelineId]);
+
+ const handleToggleExcludedProvider = useCallback(() => {
+ const payload = {
+ id: timelineId,
+ providerId: index > 0 ? group[0].id : dataProvider.id,
+ excluded: !dataProvider.excluded,
+ andProviderId: index > 0 ? dataProvider.id : undefined,
+ };
+
+ dispatch(timelineActions.updateDataProviderExcluded(payload));
+ }, [dataProvider.excluded, dataProvider.id, dispatch, group, index, timelineId]);
+
+ const handleToggleTypeProvider = useCallback(() => {
+ const payload = {
+ id: timelineId,
+ providerId: index > 0 ? group[0].id : dataProvider.id,
+ type:
+ dataProvider.type === DataProviderType.template
+ ? DataProviderType.default
+ : DataProviderType.template,
+ andProviderId: index > 0 ? dataProvider.id : undefined,
+ };
+
+ dispatch(timelineActions.updateDataProviderType(payload));
+ }, [dataProvider.id, dataProvider.type, dispatch, group, index, timelineId]);
+
+ const handleDataProviderEdited = useCallback(
+ ({ andProviderId, excluded, field, operator, providerId, value }) =>
+ dispatch(
+ timelineActions.dataProviderEdited({
+ andProviderId,
+ excluded,
+ field,
+ id: timelineId,
+ operator,
+ providerId,
+ value,
+ })
+ ),
+ [dispatch, timelineId]
+ );
+
+ const DraggableContent = useCallback(
+ (provided, snapshot) => (
+
+
+
+ 0 ? dataProvider.id : undefined}
+ browserFields={browserFields}
+ deleteProvider={handleDeleteProvider}
+ field={
+ index > 0
+ ? dataProvider.queryMatch.displayField ?? dataProvider.queryMatch.field
+ : group[0].queryMatch.displayField ?? group[0].queryMatch.field
+ }
+ kqlQuery={index > 0 ? dataProvider.kqlQuery : group[0].kqlQuery}
+ isEnabled={index > 0 ? dataProvider.enabled : group[0].enabled}
+ isExcluded={index > 0 ? dataProvider.excluded : group[0].excluded}
+ onDataProviderEdited={handleDataProviderEdited}
+ operator={
+ index > 0
+ ? dataProvider.queryMatch.operator ?? IS_OPERATOR
+ : group[0].queryMatch.operator ?? IS_OPERATOR
+ }
+ register={dataProvider}
+ providerId={index > 0 ? group[0].id : dataProvider.id}
+ timelineId={timelineId}
+ toggleEnabledProvider={handleToggleEnabledProvider}
+ toggleExcludedProvider={handleToggleExcludedProvider}
+ toggleTypeProvider={handleToggleTypeProvider}
+ val={getDataProviderValue(dataProvider)}
+ type={dataProvider.type}
+ />
+
+
+ {!snapshot.isDragging &&
+ (index < group.length - 1 ? (
+
+ ) : (
+
+
+
+ ))}
+
+
+
+ ),
+ [
+ browserFields,
+ dataProvider,
+ group,
+ handleDataProviderEdited,
+ handleDeleteProvider,
+ handleToggleEnabledProvider,
+ handleToggleExcludedProvider,
+ handleToggleTypeProvider,
+ index,
+ timelineId,
+ ]
);
return (
-
- {dataProviderGroups.map((group, groupIndex) => (
-
- {groupIndex !== 0 && }
-
-
-
-
-
-
-
-
- {'('}
-
-
-
- {(droppableProvided) => (
-
- {group.map((dataProvider, index) => (
-
- {(provided, snapshot) => (
-
-
-
- 0 ? dataProvider.id : undefined}
- browserFields={browserFields}
- deleteProvider={() =>
- index > 0
- ? onDataProviderRemoved(group[0].id, dataProvider.id)
- : onDataProviderRemoved(dataProvider.id)
- }
- field={
- index > 0
- ? dataProvider.queryMatch.displayField ??
- dataProvider.queryMatch.field
- : group[0].queryMatch.displayField ??
- group[0].queryMatch.field
- }
- kqlQuery={index > 0 ? dataProvider.kqlQuery : group[0].kqlQuery}
- isEnabled={index > 0 ? dataProvider.enabled : group[0].enabled}
- isExcluded={
- index > 0 ? dataProvider.excluded : group[0].excluded
- }
- onDataProviderEdited={onDataProviderEdited}
- operator={
- index > 0
- ? dataProvider.queryMatch.operator ?? IS_OPERATOR
- : group[0].queryMatch.operator ?? IS_OPERATOR
- }
- register={dataProvider}
- providerId={index > 0 ? group[0].id : dataProvider.id}
- timelineId={timelineId}
- toggleEnabledProvider={() =>
- index > 0
- ? onToggleDataProviderEnabled({
- providerId: group[0].id,
- enabled: !dataProvider.enabled,
- andProviderId: dataProvider.id,
- })
- : onToggleDataProviderEnabled({
- providerId: dataProvider.id,
- enabled: !dataProvider.enabled,
- })
- }
- toggleExcludedProvider={() =>
- index > 0
- ? onToggleDataProviderExcluded({
- providerId: group[0].id,
- excluded: !dataProvider.excluded,
- andProviderId: dataProvider.id,
- })
- : onToggleDataProviderExcluded({
- providerId: dataProvider.id,
- excluded: !dataProvider.excluded,
- })
- }
- toggleTypeProvider={() =>
- index > 0
- ? onToggleDataProviderType({
- providerId: group[0].id,
- type:
- dataProvider.type === DataProviderType.template
- ? DataProviderType.default
- : DataProviderType.template,
- andProviderId: dataProvider.id,
- })
- : onToggleDataProviderType({
- providerId: dataProvider.id,
- type:
- dataProvider.type === DataProviderType.template
- ? DataProviderType.default
- : DataProviderType.template,
- })
- }
- val={getDataProviderValue(dataProvider)}
- type={dataProvider.type}
- />
-
-
- {!snapshot.isDragging &&
- (index < group.length - 1 ? (
-
- ) : (
-
-
-
- ))}
-
-
-
- )}
-
- ))}
- {droppableProvided.placeholder}
-
- )}
-
-
-
- {')'}
-
- {groupIndex === dataProviderGroups.length - 1 && (
-
- )}
-
-
- ))}
-
+
+ {DraggableContent}
+
);
}
);
-Providers.displayName = 'Providers';
+DataProvidersGroupItem.displayName = 'DataProvidersGroupItem';
+
+interface DataProvidersGroup extends Props {
+ group: DataProvidersAnd[];
+ groupIndex: number;
+ isLastGroup: boolean;
+}
+
+const DataProvidersGroup = React.memo(
+ ({ browserFields, timelineId, group, groupIndex, isLastGroup }) => {
+ const droppableId = useMemo(() => getTimelineProviderDroppableId({ groupIndex, timelineId }), [
+ groupIndex,
+ timelineId,
+ ]);
+
+ const GroupDataProviders = useMemo(
+ () =>
+ group.map((dataProvider, index) => (
+
+ )),
+ [browserFields, group, groupIndex, timelineId]
+ );
+
+ const DroppableContent = useCallback(
+ (droppableProvided) => (
+
+ {GroupDataProviders}
+ {droppableProvided.placeholder}
+
+ ),
+ [GroupDataProviders, isLastGroup]
+ );
+
+ return (
+ <>
+ {groupIndex !== 0 && }
+
+
+
+
+
+
+
+
+ {'('}
+
+
+
+ {DroppableContent}
+
+
+
+ {')'}
+
+ {isLastGroup && (
+
+ )}
+
+ >
+ );
+ }
+);
+
+DataProvidersGroup.displayName = 'DataProvidersGroup';
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/events.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/events.ts
index f894ac4e73939..8ab3a71604bf1 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/events.ts
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/events.ts
@@ -7,33 +7,8 @@
import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model';
import { ColumnId } from './body/column_id';
import { SortDirection } from './body/sort';
-import { DataProvider, DataProviderType, QueryOperator } from './data_providers/data_provider';
+import { DataProvider, QueryOperator } from './data_providers/data_provider';
-/** Invoked when a user clicks the close button to remove a data provider */
-export type OnDataProviderRemoved = (providerId: string, andProviderId?: string) => void;
-
-/** Invoked when a user temporarily disables or re-enables a data provider */
-export type OnToggleDataProviderEnabled = (toggled: {
- providerId: string;
- enabled: boolean;
- andProviderId?: string;
-}) => void;
-
-/** Invoked when a user toggles negation ("boolean NOT") of a data provider */
-export type OnToggleDataProviderExcluded = (excluded: {
- providerId: string;
- excluded: boolean;
- andProviderId?: string;
-}) => void;
-
-/** Invoked when a user toggles type (can "default" or "template") of a data provider */
-export type OnToggleDataProviderType = (type: {
- providerId: string;
- type: DataProviderType;
- andProviderId?: string;
-}) => void;
-
-/** Invoked when a user edits the properties of a data provider */
export type OnDataProviderEdited = ({
andProviderId,
excluded,
@@ -54,9 +29,6 @@ export type OnDataProviderEdited = ({
type: DataProvider['type'];
}) => void;
-/** Invoked when a user change the kql query of our data provider */
-export type OnChangeDataProviderKqlQuery = (edit: { providerId: string; kqlQuery: string }) => void;
-
/** Invoked when a user selects a new minimap time range */
export type OnRangeSelected = (range: string) => void;
@@ -76,8 +48,6 @@ export type OnChangeItemsPerPage = (itemsPerPage: number) => void;
/** Invoked when a user clicks to load more item */
export type OnChangePage = (nextPage: number) => void;
-export type OnChangeDroppableAndProvider = (providerId: string) => void;
-
/** Invoked when a user pins an event */
export type OnPinEvent = (eventId: string) => void;
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/__snapshots__/index.test.tsx.snap
index f81934f9a1d91..5b14edf818fdc 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/__snapshots__/index.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/__snapshots__/index.test.tsx.snap
@@ -77,7 +77,7 @@ exports[`Footer Timeline Component rendering it renders the default timeline foo
data-test-subj="paging-control"
isLoading={false}
onPageClick={[Function]}
- totalPages={2}
+ totalPages={5}
/>
{
const loadMore = jest.fn();
const onChangeItemsPerPage = jest.fn();
const updatedAt = 1546878704036;
- const totalCount = 15546;
+ const serverSideEventCount = 15546;
const itemsCount = 2;
+ const totalCount = 10;
describe('rendering', () => {
test('it renders the default timeline footer', () => {
@@ -33,8 +34,8 @@ describe('Footer Timeline Component', () => {
itemsPerPageOptions={[1, 5, 10, 20]}
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadMore}
- serverSideEventCount={totalCount}
- totalPages={2}
+ serverSideEventCount={serverSideEventCount}
+ totalCount={totalCount}
/>
);
@@ -55,8 +56,8 @@ describe('Footer Timeline Component', () => {
itemsPerPageOptions={[1, 5, 10, 20]}
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadMore}
- serverSideEventCount={totalCount}
- totalPages={2}
+ serverSideEventCount={serverSideEventCount}
+ totalCount={totalCount}
/>
);
@@ -78,8 +79,8 @@ describe('Footer Timeline Component', () => {
itemsPerPageOptions={[1, 5, 10, 20]}
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadMore}
- serverSideEventCount={totalCount}
- totalPages={2}
+ serverSideEventCount={serverSideEventCount}
+ totalCount={totalCount}
/>
);
@@ -129,8 +130,8 @@ describe('Footer Timeline Component', () => {
itemsPerPageOptions={[1, 5, 10, 20]}
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadMore}
- serverSideEventCount={totalCount}
- totalPages={2}
+ serverSideEventCount={serverSideEventCount}
+ totalCount={totalCount}
/>
);
@@ -152,8 +153,8 @@ describe('Footer Timeline Component', () => {
itemsPerPageOptions={[1, 5, 10, 20]}
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadMore}
- serverSideEventCount={totalCount}
- totalPages={2}
+ serverSideEventCount={serverSideEventCount}
+ totalCount={totalCount}
/>
);
@@ -179,8 +180,8 @@ describe('Footer Timeline Component', () => {
itemsPerPageOptions={[1, 5, 10, 20]}
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadMore}
- serverSideEventCount={totalCount}
- totalPages={2}
+ serverSideEventCount={serverSideEventCount}
+ totalCount={totalCount}
/>
);
@@ -204,8 +205,8 @@ describe('Footer Timeline Component', () => {
itemsPerPageOptions={[1, 5, 10, 20]}
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadMore}
- serverSideEventCount={totalCount}
- totalPages={2}
+ serverSideEventCount={serverSideEventCount}
+ totalCount={totalCount}
/>
);
@@ -231,8 +232,8 @@ describe('Footer Timeline Component', () => {
itemsPerPageOptions={[1, 5, 10, 20]}
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadMore}
- serverSideEventCount={totalCount}
- totalPages={2}
+ serverSideEventCount={serverSideEventCount}
+ totalCount={totalCount}
/>
);
@@ -256,8 +257,8 @@ describe('Footer Timeline Component', () => {
itemsPerPageOptions={[1, 5, 10, 20]}
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadMore}
- serverSideEventCount={totalCount}
- totalPages={2}
+ serverSideEventCount={serverSideEventCount}
+ totalCount={totalCount}
/>
);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx
index 7174e9b2121e5..7c10168da3c62 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx
@@ -174,30 +174,36 @@ export const EventsCount = React.memo(EventsCountComponent);
EventsCount.displayName = 'EventsCount';
-export const PagingControlComponent = ({
- activePage,
- isLoading,
- onPageClick,
- totalPages,
-}: {
+interface PagingControlProps {
activePage: number;
isLoading: boolean;
onPageClick: OnChangePage;
totalPages: number;
-}) => (
- <>
- {isLoading ? (
- `${i18n.LOADING}...`
- ) : (
-
- )}
- >
-);
+}
+
+export const PagingControlComponent: React.FC = ({
+ activePage,
+ isLoading,
+ onPageClick,
+ totalPages,
+}) => {
+ if (isLoading) {
+ return <>{`${i18n.LOADING}...`}>;
+ }
+
+ if (!totalPages) {
+ return null;
+ }
+
+ return (
+
+ );
+};
PagingControlComponent.displayName = 'PagingControlComponent';
@@ -217,7 +223,7 @@ interface FooterProps {
onChangeItemsPerPage: OnChangeItemsPerPage;
onChangePage: OnChangePage;
serverSideEventCount: number;
- totalPages: number;
+ totalCount: number;
}
/** Renders a loading indicator and paging controls */
@@ -234,7 +240,7 @@ export const FooterComponent = ({
onChangeItemsPerPage,
onChangePage,
serverSideEventCount,
- totalPages,
+ totalCount,
}: FooterProps) => {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const [paginationLoading, setPaginationLoading] = useState(false);
@@ -259,6 +265,30 @@ export const FooterComponent = ({
]);
const closePopover = useCallback(() => setIsPopoverOpen(false), [setIsPopoverOpen]);
+ const rowItems = useMemo(
+ () =>
+ itemsPerPageOptions &&
+ itemsPerPageOptions.map((item) => (
+ {
+ closePopover();
+ onChangeItemsPerPage(item);
+ }}
+ >
+ {`${item} ${i18n.ROWS}`}
+
+ )),
+ [closePopover, itemsPerPage, itemsPerPageOptions, onChangeItemsPerPage]
+ );
+
+ const totalPages = useMemo(() => Math.ceil(totalCount / itemsPerPage), [
+ itemsPerPage,
+ totalCount,
+ ]);
+
useEffect(() => {
if (paginationLoading && !isLoading) {
setPaginationLoading(false);
@@ -279,22 +309,6 @@ export const FooterComponent = ({
);
}
- const rowItems =
- itemsPerPageOptions &&
- itemsPerPageOptions.map((item) => (
- {
- closePopover();
- onChangeItemsPerPage(item);
- }}
- >
- {`${item} ${i18n.ROWS}`}
-
- ));
-
return (
= ({
dataProviders,
filterManager,
graphEventId,
- onDataProviderEdited,
- onDataProviderRemoved,
- onToggleDataProviderEnabled,
- onToggleDataProviderExcluded,
- onToggleDataProviderType,
show,
showCallOutUnauthorizedMsg,
status,
@@ -85,11 +68,6 @@ const TimelineHeaderComponent: React.FC = ({
browserFields={browserFields}
timelineId={timelineId}
dataProviders={dataProviders}
- onDataProviderEdited={onDataProviderEdited}
- onDataProviderRemoved={onDataProviderRemoved}
- onToggleDataProviderEnabled={onToggleDataProviderEnabled}
- onToggleDataProviderExcluded={onToggleDataProviderExcluded}
- onToggleDataProviderType={onToggleDataProviderType}
/>
(
kqlMode,
kqlQueryExpression,
onClose,
- onDataProviderEdited,
removeColumn,
- removeProvider,
show,
showCallOutUnauthorizedMsg,
sort,
start,
status,
timelineType,
- updateDataProviderEnabled,
- updateDataProviderExcluded,
- updateDataProviderType,
updateItemsPerPage,
upsertColumn,
usersViewing,
@@ -75,59 +63,6 @@ const StatefulTimelineComponent = React.memo(
selectedPatterns,
} = useSourcererScope(SourcererScopeName.timeline);
- const onDataProviderRemoved: OnDataProviderRemoved = useCallback(
- (providerId: string, andProviderId?: string) =>
- removeProvider!({ id, providerId, andProviderId }),
- [id, removeProvider]
- );
-
- const onToggleDataProviderEnabled: OnToggleDataProviderEnabled = useCallback(
- ({ providerId, enabled, andProviderId }) =>
- updateDataProviderEnabled!({
- id,
- enabled,
- providerId,
- andProviderId,
- }),
- [id, updateDataProviderEnabled]
- );
-
- const onToggleDataProviderExcluded: OnToggleDataProviderExcluded = useCallback(
- ({ providerId, excluded, andProviderId }) =>
- updateDataProviderExcluded!({
- id,
- excluded,
- providerId,
- andProviderId,
- }),
- [id, updateDataProviderExcluded]
- );
-
- const onToggleDataProviderType: OnToggleDataProviderType = useCallback(
- ({ providerId, type, andProviderId }) =>
- updateDataProviderType!({
- id,
- type,
- providerId,
- andProviderId,
- }),
- [id, updateDataProviderType]
- );
-
- const onDataProviderEditedLocal: OnDataProviderEdited = useCallback(
- ({ andProviderId, excluded, field, operator, providerId, value }) =>
- onDataProviderEdited!({
- andProviderId,
- excluded,
- field,
- id,
- operator,
- providerId,
- value,
- }),
- [id, onDataProviderEdited]
- );
-
const onChangeItemsPerPage: OnChangeItemsPerPage = useCallback(
(itemsChangedPerPage) => updateItemsPerPage!({ id, itemsPerPage: itemsChangedPerPage }),
[id, updateItemsPerPage]
@@ -183,11 +118,6 @@ const StatefulTimelineComponent = React.memo(
loadingSourcerer={loading}
onChangeItemsPerPage={onChangeItemsPerPage}
onClose={onClose}
- onDataProviderEdited={onDataProviderEditedLocal}
- onDataProviderRemoved={onDataProviderRemoved}
- onToggleDataProviderEnabled={onToggleDataProviderEnabled}
- onToggleDataProviderExcluded={onToggleDataProviderExcluded}
- onToggleDataProviderType={onToggleDataProviderType}
show={show!}
showCallOutUnauthorizedMsg={showCallOutUnauthorizedMsg}
sort={sort!}
@@ -287,14 +217,8 @@ const makeMapStateToProps = () => {
const mapDispatchToProps = {
addProvider: timelineActions.addProvider,
createTimeline: timelineActions.createTimeline,
- onDataProviderEdited: timelineActions.dataProviderEdited,
removeColumn: timelineActions.removeColumn,
- removeProvider: timelineActions.removeProvider,
updateColumns: timelineActions.updateColumns,
- updateDataProviderEnabled: timelineActions.updateDataProviderEnabled,
- updateDataProviderExcluded: timelineActions.updateDataProviderExcluded,
- updateDataProviderKqlQuery: timelineActions.updateDataProviderKqlQuery,
- updateDataProviderType: timelineActions.updateDataProviderType,
updateHighlightedDropAndProviderId: timelineActions.updateHighlightedDropAndProviderId,
updateItemsPerPage: timelineActions.updateItemsPerPage,
updateItemsPerPageOptions: timelineActions.updateItemsPerPageOptions,
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx
index 43ab8ab203e11..a28f4240d3a2f 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx
@@ -73,23 +73,31 @@ export const StarIcon = React.memo<{
isFavorite: boolean;
timelineId: string;
updateIsFavorite: UpdateIsFavorite;
-}>(({ isFavorite, timelineId: id, updateIsFavorite }) => (
- // TODO: 1 error is: Visible, non-interactive elements with click handlers must have at least one keyboard listener
- // TODO: 2 error is: Elements with the 'button' interactive role must be focusable
- // TODO: Investigate this error
- // eslint-disable-next-line
- updateIsFavorite({ id, isFavorite: !isFavorite })}>
- {isFavorite ? (
-
-
-
- ) : (
-
-
-
- )}
-
-));
+}>(({ isFavorite, timelineId: id, updateIsFavorite }) => {
+ const handleClick = useCallback(() => updateIsFavorite({ id, isFavorite: !isFavorite }), [
+ id,
+ isFavorite,
+ updateIsFavorite,
+ ]);
+
+ return (
+ // TODO: 1 error is: Visible, non-interactive elements with click handlers must have at least one keyboard listener
+ // TODO: 2 error is: Elements with the 'button' interactive role must be focusable
+ // TODO: Investigate this error
+ // eslint-disable-next-line
+
+ {isFavorite ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
+ );
+});
StarIcon.displayName = 'StarIcon';
interface DescriptionProps {
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx
index bde1e7bf5829a..630a71693d182 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx
@@ -19,11 +19,6 @@ import {
import '../../../common/mock/match_media';
import { TestProviders } from '../../../common/mock/test_providers';
-import {
- DELETE_CLASS_NAME,
- ENABLE_CLASS_NAME,
- EXCLUDE_CLASS_NAME,
-} from './data_providers/provider_item_actions';
import { TimelineComponent, Props as TimelineComponentProps } from './timeline';
import { Sort } from './body/sort';
import { mockDataProviders } from './data_providers/mock/mock_data_providers';
@@ -115,11 +110,6 @@ describe('Timeline', () => {
loadingSourcerer: false,
onChangeItemsPerPage: jest.fn(),
onClose: jest.fn(),
- onDataProviderEdited: jest.fn(),
- onDataProviderRemoved: jest.fn(),
- onToggleDataProviderEnabled: jest.fn(),
- onToggleDataProviderExcluded: jest.fn(),
- onToggleDataProviderType: jest.fn(),
show: true,
showCallOutUnauthorizedMsg: false,
sort,
@@ -238,204 +228,4 @@ describe('Timeline', () => {
});
});
});
-
- describe('event wire up', () => {
- describe('onDataProviderRemoved', () => {
- test('it invokes the onDataProviderRemoved callback when the delete button on a provider is clicked', () => {
- const wrapper = mount(
-
-
-
- );
-
- wrapper
- .find('[data-test-subj="providerBadge"] [data-euiicon-type]')
- .first()
- .simulate('click');
-
- expect((props.onDataProviderRemoved as jest.Mock).mock.calls[0][0]).toEqual(
- 'id-Provider 1'
- );
- });
-
- test('it invokes the onDataProviderRemoved callback when you click on the option "Delete" in the provider menu', () => {
- const wrapper = mount(
-
-
-
- );
- wrapper.find('button[data-test-subj="providerBadge"]').first().simulate('click');
-
- wrapper.update();
-
- wrapper
- .find(`[data-test-subj="providerActions"] .${DELETE_CLASS_NAME}`)
- .first()
- .simulate('click');
-
- expect((props.onDataProviderRemoved as jest.Mock).mock.calls[0][0]).toEqual(
- 'id-Provider 1'
- );
- });
- });
-
- describe('onToggleDataProviderEnabled', () => {
- test('it invokes the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the provider menu', () => {
- const wrapper = mount(
-
-
-
- );
-
- wrapper.find('button[data-test-subj="providerBadge"]').first().simulate('click');
-
- wrapper.update();
-
- wrapper
- .find(`[data-test-subj="providerActions"] .${ENABLE_CLASS_NAME}`)
- .first()
- .simulate('click');
-
- expect((props.onToggleDataProviderEnabled as jest.Mock).mock.calls[0][0]).toEqual({
- providerId: 'id-Provider 1',
- enabled: false,
- });
- });
- });
-
- describe('onToggleDataProviderExcluded', () => {
- test('it invokes the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the provider menu', () => {
- const wrapper = mount(
-
-
-
- );
-
- wrapper
- .find('[data-test-subj="providerBadge"]')
- .first()
- .find('button')
- .first()
- .simulate('click');
-
- wrapper.update();
-
- wrapper
- .find(`[data-test-subj="providerActions"] .${EXCLUDE_CLASS_NAME}`)
- .first()
- .simulate('click');
-
- expect((props.onToggleDataProviderExcluded as jest.Mock).mock.calls[0][0]).toEqual({
- providerId: 'id-Provider 1',
- excluded: true,
- });
- });
- });
-
- describe('#ProviderWithAndProvider', () => {
- const dataProviders = mockDataProviders.slice(0, 1);
- dataProviders[0].and = mockDataProviders.slice(1, 3);
-
- test('Rendering And Provider', () => {
- const wrapper = mount(
-
-
-
- );
-
- const andProviderBadges = wrapper.find(
- '[data-test-subj="providerBadge"] .euiBadge__content span.field-value'
- );
-
- const andProviderBadgesText = andProviderBadges.map((node) => node.text()).join(' ');
- expect(andProviderBadges.length).toEqual(3);
- expect(andProviderBadgesText).toEqual(
- 'name: "Provider 1" name: "Provider 2" name: "Provider 3"'
- );
- });
-
- test('it invokes the onDataProviderRemoved callback when you click on the option "Delete" in the accordion menu', () => {
- const wrapper = mount(
-
-
-
- );
-
- wrapper
- .find('[data-test-subj="providerBadge"]')
- .at(3)
- .find('button')
- .first()
- .simulate('click');
-
- wrapper.update();
-
- wrapper
- .find(`[data-test-subj="providerActions"] .${DELETE_CLASS_NAME}`)
- .first()
- .simulate('click');
-
- expect((props.onDataProviderRemoved as jest.Mock).mock.calls[0]).toEqual([
- 'id-Provider 1',
- 'id-Provider 2',
- ]);
- });
-
- test('it invokes the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the accordion menu', () => {
- const wrapper = mount(
-
-
-
- );
-
- wrapper
- .find('[data-test-subj="providerBadge"]')
- .at(3)
- .find('button')
- .first()
- .simulate('click');
-
- wrapper.update();
-
- wrapper
- .find(`[data-test-subj="providerActions"] .${ENABLE_CLASS_NAME}`)
- .first()
- .simulate('click');
-
- expect((props.onToggleDataProviderEnabled as jest.Mock).mock.calls[0][0]).toEqual({
- andProviderId: 'id-Provider 2',
- enabled: false,
- providerId: 'id-Provider 1',
- });
- });
-
- test('it invokes the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the accordion menu', () => {
- const wrapper = mount(
-
-
-
- );
-
- wrapper
- .find('[data-test-subj="providerBadge"]')
- .at(3)
- .find('button')
- .first()
- .simulate('click');
-
- wrapper.update();
-
- wrapper
- .find(`[data-test-subj="providerActions"] .${EXCLUDE_CLASS_NAME}`)
- .first()
- .simulate('click');
-
- expect((props.onToggleDataProviderExcluded as jest.Mock).mock.calls[0][0]).toEqual({
- andProviderId: 'id-Provider 2',
- excluded: true,
- providerId: 'id-Provider 1',
- });
- });
- });
- });
});
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx
index d1a25e6f3e1a4..1097d58b227a8 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx
@@ -19,14 +19,7 @@ import { defaultHeaders } from './body/column_headers/default_headers';
import { Sort } from './body/sort';
import { StatefulBody } from './body/stateful_body';
import { DataProvider } from './data_providers/data_provider';
-import {
- OnChangeItemsPerPage,
- OnDataProviderRemoved,
- OnDataProviderEdited,
- OnToggleDataProviderEnabled,
- OnToggleDataProviderExcluded,
- OnToggleDataProviderType,
-} from './events';
+import { OnChangeItemsPerPage } from './events';
import { TimelineKqlFetch } from './fetch_kql_timeline';
import { Footer, footerHeight } from './footer';
import { TimelineHeader } from './header';
@@ -113,11 +106,6 @@ export interface Props {
loadingSourcerer: boolean;
onChangeItemsPerPage: OnChangeItemsPerPage;
onClose: () => void;
- onDataProviderEdited: OnDataProviderEdited;
- onDataProviderRemoved: OnDataProviderRemoved;
- onToggleDataProviderEnabled: OnToggleDataProviderEnabled;
- onToggleDataProviderExcluded: OnToggleDataProviderExcluded;
- onToggleDataProviderType: OnToggleDataProviderType;
show: boolean;
showCallOutUnauthorizedMsg: boolean;
sort: Sort;
@@ -149,11 +137,6 @@ export const TimelineComponent: React.FC = ({
kqlQueryExpression,
onChangeItemsPerPage,
onClose,
- onDataProviderEdited,
- onDataProviderRemoved,
- onToggleDataProviderEnabled,
- onToggleDataProviderExcluded,
- onToggleDataProviderType,
show,
showCallOutUnauthorizedMsg,
start,
@@ -270,11 +253,6 @@ export const TimelineComponent: React.FC = ({
dataProviders={dataProviders}
filterManager={filterManager}
graphEventId={graphEventId}
- onDataProviderEdited={onDataProviderEdited}
- onDataProviderRemoved={onDataProviderRemoved}
- onToggleDataProviderEnabled={onToggleDataProviderEnabled}
- onToggleDataProviderExcluded={onToggleDataProviderExcluded}
- onToggleDataProviderType={onToggleDataProviderType}
show={show}
showCallOutUnauthorizedMsg={showCallOutUnauthorizedMsg}
timelineId={id}
@@ -324,7 +302,7 @@ export const TimelineComponent: React.FC = ({
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadPage}
serverSideEventCount={totalCount}
- totalPages={pageInfo.totalPages}
+ totalCount={pageInfo.fakeTotalCount}
/>
)
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 54db52b985c31..53944fd29a687 100644
--- a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx
@@ -15,11 +15,13 @@ import { inputsModel } from '../../common/store';
import { useKibana } from '../../common/lib/kibana';
import { createFilter } from '../../common/containers/helpers';
import { DocValueFields } from '../../common/containers/query_template';
+import { generateTablePaginationOptions } from '../../common/components/paginated_table/helpers';
import { timelineActions } from '../../timelines/store/timeline';
import { detectionsTimelineIds, skipQueryForDetectionsPage } from './helpers';
import { getInspectResponse } from '../../helpers';
import {
Direction,
+ PageInfoPaginated,
TimelineEventsQueries,
TimelineEventsAllStrategyResponse,
TimelineEventsAllRequestOptions,
@@ -35,10 +37,7 @@ export interface TimelineArgs {
id: string;
inspect: InspectResponse;
loadPage: LoadPage;
- pageInfo: {
- activePage: number;
- totalPages: number;
- };
+ pageInfo: PageInfoPaginated;
refetch: inputsModel.Refetch;
totalCount: number;
updatedAt: number;
@@ -97,10 +96,7 @@ export const useTimelineEvents = ({
from: startDate,
to: endDate,
},
- pagination: {
- activePage,
- querySize: limit,
- },
+ pagination: generateTablePaginationOptions(activePage, limit),
sort,
defaultIndex: indexNames,
docValueFields: docValueFields ?? [],
@@ -134,7 +130,8 @@ export const useTimelineEvents = ({
totalCount: -1,
pageInfo: {
activePage: 0,
- totalPages: 0,
+ fakeTotalCount: 0,
+ showMorePagesIndicator: false,
},
events: [],
loadPage: wrappedLoadPage,
@@ -218,10 +215,7 @@ export const useTimelineEvents = ({
defaultIndex: indexNames,
docValueFields: docValueFields ?? [],
filterQuery: createFilter(filterQuery),
- pagination: {
- activePage,
- querySize: limit,
- },
+ pagination: generateTablePaginationOptions(activePage, limit),
timerange: {
interval: '12h',
from: startDate,
diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx
index 1f79b26394a69..1992b1f88f064 100644
--- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx
@@ -96,11 +96,6 @@ describe('epicLocalStorage', () => {
loadingSourcerer: false,
onChangeItemsPerPage: jest.fn(),
onClose: jest.fn(),
- onDataProviderEdited: jest.fn(),
- onDataProviderRemoved: jest.fn(),
- onToggleDataProviderEnabled: jest.fn(),
- onToggleDataProviderExcluded: jest.fn(),
- onToggleDataProviderType: jest.fn(),
show: true,
showCallOutUnauthorizedMsg: false,
start: startDate,
diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts
index fc178df86362b..30d0796443ab5 100644
--- a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts
+++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts
@@ -1209,17 +1209,20 @@ export const updateTimelinePerPageOptions = ({
const removeAndProvider = (andProviderId: string, providerId: string, timeline: TimelineModel) => {
const providerIndex = timeline.dataProviders.findIndex((p) => p.id === providerId);
- const providerAndIndex = timeline.dataProviders[providerIndex].and.findIndex(
+ const providerAndIndex = timeline.dataProviders[providerIndex]?.and.findIndex(
(p) => p.id === andProviderId
);
+
return [
...timeline.dataProviders.slice(0, providerIndex),
{
...timeline.dataProviders[providerIndex],
- and: [
- ...timeline.dataProviders[providerIndex].and.slice(0, providerAndIndex),
- ...timeline.dataProviders[providerIndex].and.slice(providerAndIndex + 1),
- ],
+ and: timeline.dataProviders[providerIndex]?.and
+ ? [
+ ...timeline.dataProviders[providerIndex]?.and.slice(0, providerAndIndex),
+ ...timeline.dataProviders[providerIndex]?.and.slice(providerAndIndex + 1),
+ ]
+ : [],
},
...timeline.dataProviders.slice(providerIndex + 1),
];
@@ -1229,7 +1232,7 @@ const removeProvider = (providerId: string, timeline: TimelineModel) => {
const providerIndex = timeline.dataProviders.findIndex((p) => p.id === providerId);
return [
...timeline.dataProviders.slice(0, providerIndex),
- ...(timeline.dataProviders[providerIndex].and.length
+ ...(timeline.dataProviders[providerIndex]?.and.length
? [
{
...timeline.dataProviders[providerIndex].and.slice(0, 1)[0],
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts
index 98c9b79f32d6b..9e9a35ea35318 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts
@@ -240,7 +240,7 @@ describe('when invoking endpoint trusted apps route handlers', () => {
os: 'windows',
entries: [
{
- field: 'process.path.text',
+ field: 'process.executable.text',
type: 'match',
operator: 'included',
value: 'c:/programs files/Anti-Virus',
@@ -293,7 +293,7 @@ describe('when invoking endpoint trusted apps route handlers', () => {
description: 'this one is ok',
entries: [
{
- field: 'process.path.text',
+ field: 'process.executable.text',
operator: 'included',
type: 'match',
value: 'c:/programs files/Anti-Virus',
@@ -320,7 +320,7 @@ describe('when invoking endpoint trusted apps route handlers', () => {
description: 'this one is ok',
entries: [
{
- field: 'process.path.text',
+ field: 'process.executable.text',
operator: 'included',
type: 'match',
value: 'c:/programs files/Anti-Virus',
@@ -357,7 +357,7 @@ describe('when invoking endpoint trusted apps route handlers', () => {
it('should trim condition entry values', async () => {
const newTrustedApp = createNewTrustedAppBody();
newTrustedApp.entries.push({
- field: 'process.path.text',
+ field: 'process.executable.text',
value: '\n some value \r\n ',
operator: 'included',
type: 'match',
@@ -366,13 +366,13 @@ describe('when invoking endpoint trusted apps route handlers', () => {
await routeHandler(context, request, response);
expect(exceptionsListClient.createExceptionListItem.mock.calls[0][0].entries).toEqual([
{
- field: 'process.path.text',
+ field: 'process.executable.text',
operator: 'included',
type: 'match',
value: 'c:/programs files/Anti-Virus',
},
{
- field: 'process.path.text',
+ field: 'process.executable.text',
value: 'some value',
operator: 'included',
type: 'match',
@@ -392,7 +392,7 @@ describe('when invoking endpoint trusted apps route handlers', () => {
await routeHandler(context, request, response);
expect(exceptionsListClient.createExceptionListItem.mock.calls[0][0].entries).toEqual([
{
- field: 'process.path.text',
+ field: 'process.executable.text',
operator: 'included',
type: 'match',
value: 'c:/programs files/Anti-Virus',
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.test.ts
index 2f7dd22c0c78e..75a7de8cd2c44 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.test.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.test.ts
@@ -11,8 +11,15 @@ import {
sampleIdGuid,
sampleDocWithAncestors,
sampleRuleSO,
+ sampleDocNoSortIdNoVersion,
} from './__mocks__/es_results';
-import { buildBulkBody, buildSignalFromSequence, buildSignalFromEvent } from './build_bulk_body';
+import {
+ buildBulkBody,
+ buildSignalFromSequence,
+ buildSignalFromEvent,
+ objectPairIntersection,
+ objectArrayIntersection,
+} from './build_bulk_body';
import { SignalHit } from './types';
import { getListArrayMock } from '../../../../common/detection_engine/schemas/types/lists.mock';
@@ -438,13 +445,20 @@ describe('buildBulkBody', () => {
describe('buildSignalFromSequence', () => {
test('builds a basic signal from a sequence of building blocks', () => {
- const blocks = [sampleDocWithAncestors().hits.hits[0], sampleDocWithAncestors().hits.hits[0]];
+ const block1 = sampleDocWithAncestors().hits.hits[0];
+ block1._source.new_key = 'new_key_value';
+ block1._source.new_key2 = 'new_key2_value';
+ const block2 = sampleDocWithAncestors().hits.hits[0];
+ block2._source.new_key = 'new_key_value';
+ const blocks = [block1, block2];
const ruleSO = sampleRuleSO();
const signal = buildSignalFromSequence(blocks, ruleSO);
// Timestamp will potentially always be different so remove it for the test
// @ts-expect-error
delete signal['@timestamp'];
- const expected: Omit = {
+ const expected: Omit & { someKey: string; new_key: string } = {
+ someKey: 'someValue',
+ new_key: 'new_key_value',
event: {
kind: 'signal',
},
@@ -539,6 +553,96 @@ describe('buildSignalFromSequence', () => {
};
expect(signal).toEqual(expected);
});
+
+ test('builds a basic signal if there is no overlap between source events', () => {
+ const block1 = sampleDocNoSortIdNoVersion();
+ const block2 = sampleDocNoSortIdNoVersion();
+ block2._source['@timestamp'] = '2021-05-20T22:28:46+0000';
+ block2._source.someKey = 'someOtherValue';
+ const ruleSO = sampleRuleSO();
+ const signal = buildSignalFromSequence([block1, block2], ruleSO);
+ // Timestamp will potentially always be different so remove it for the test
+ // @ts-expect-error
+ delete signal['@timestamp'];
+ const expected: Omit = {
+ event: {
+ kind: 'signal',
+ },
+ signal: {
+ parents: [
+ {
+ id: sampleIdGuid,
+ type: 'event',
+ index: 'myFakeSignalIndex',
+ depth: 0,
+ },
+ {
+ id: sampleIdGuid,
+ type: 'event',
+ index: 'myFakeSignalIndex',
+ depth: 0,
+ },
+ ],
+ ancestors: [
+ {
+ id: sampleIdGuid,
+ type: 'event',
+ index: 'myFakeSignalIndex',
+ depth: 0,
+ },
+ {
+ id: sampleIdGuid,
+ type: 'event',
+ index: 'myFakeSignalIndex',
+ depth: 0,
+ },
+ ],
+ status: 'open',
+ rule: {
+ actions: [],
+ author: ['Elastic'],
+ building_block_type: 'default',
+ id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
+ rule_id: 'rule-1',
+ false_positives: [],
+ max_signals: 10000,
+ risk_score: 50,
+ risk_score_mapping: [],
+ output_index: '.siem-signals',
+ description: 'Detecting root and admin users',
+ from: 'now-6m',
+ immutable: false,
+ index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'],
+ interval: '5m',
+ language: 'kuery',
+ license: 'Elastic License',
+ name: 'rule-name',
+ query: 'user.name: root or user.name: admin',
+ references: ['http://google.com'],
+ severity: 'high',
+ severity_mapping: [],
+ tags: ['some fake tag 1', 'some fake tag 2'],
+ threat: [],
+ type: 'query',
+ to: 'now',
+ note: '',
+ enabled: true,
+ created_by: 'sample user',
+ updated_by: 'sample user',
+ version: 1,
+ updated_at: ruleSO.updated_at ?? '',
+ created_at: ruleSO.attributes.createdAt,
+ throttle: 'no_actions',
+ exceptions_list: getListArrayMock(),
+ },
+ depth: 1,
+ group: {
+ id: '269c1f5754bff92fb8040283b687258e99b03e8b2ab1262cc20c82442e5de5ea',
+ },
+ },
+ };
+ expect(signal).toEqual(expected);
+ });
});
describe('buildSignalFromEvent', () => {
@@ -632,3 +736,341 @@ describe('buildSignalFromEvent', () => {
expect(signal).toEqual(expected);
});
});
+
+describe('recursive intersection between objects', () => {
+ test('should treat numbers and strings as unequal', () => {
+ const a = {
+ field1: 1,
+ field2: 1,
+ };
+ const b = {
+ field1: 1,
+ field2: '1',
+ };
+ const intersection = objectPairIntersection(a, b);
+ const expected = {
+ field1: 1,
+ };
+ expect(intersection).toEqual(expected);
+ });
+
+ test('should strip unequal numbers and strings', () => {
+ const a = {
+ field1: 1,
+ field2: 1,
+ field3: 'abcd',
+ field4: 'abcd',
+ };
+ const b = {
+ field1: 1,
+ field2: 100,
+ field3: 'abcd',
+ field4: 'wxyz',
+ };
+ const intersection = objectPairIntersection(a, b);
+ const expected = {
+ field1: 1,
+ field3: 'abcd',
+ };
+ expect(intersection).toEqual(expected);
+ });
+
+ test('should handle null values', () => {
+ const a = {
+ field1: 1,
+ field2: '1',
+ field3: null,
+ };
+ const b = {
+ field1: null,
+ field2: null,
+ field3: null,
+ };
+ const intersection = objectPairIntersection(a, b);
+ const expected = {
+ field3: null,
+ };
+ expect(intersection).toEqual(expected);
+ });
+
+ test('should handle explicit undefined values and return undefined if left with only undefined fields', () => {
+ const a = {
+ field1: 1,
+ field2: '1',
+ field3: undefined,
+ };
+ const b = {
+ field1: undefined,
+ field2: undefined,
+ field3: undefined,
+ };
+ const intersection = objectPairIntersection(a, b);
+ const expected = undefined;
+ expect(intersection).toEqual(expected);
+ });
+
+ test('should strip arrays out regardless of whether they are equal', () => {
+ const a = {
+ array_field1: [1, 2],
+ array_field2: [1, 2],
+ };
+ const b = {
+ array_field1: [1, 2],
+ array_field2: [3, 4],
+ };
+ const intersection = objectPairIntersection(a, b);
+ const expected = undefined;
+ expect(intersection).toEqual(expected);
+ });
+
+ test('should strip fields that are not in both objects', () => {
+ const a = {
+ field1: 1,
+ };
+ const b = {
+ field2: 1,
+ };
+ const intersection = objectPairIntersection(a, b);
+ const expected = undefined;
+ expect(intersection).toEqual(expected);
+ });
+
+ test('should work on objects within objects', () => {
+ const a = {
+ container_field: {
+ field1: 1,
+ field2: 1,
+ field3: 10,
+ field5: 1,
+ field6: null,
+ array_field: [1, 2],
+ nested_container_field: {
+ field1: 1,
+ field2: 1,
+ },
+ nested_container_field2: {
+ field1: undefined,
+ },
+ },
+ container_field_without_intersection: {
+ sub_field1: 1,
+ },
+ };
+ const b = {
+ container_field: {
+ field1: 1,
+ field2: 2,
+ field4: 10,
+ field5: '1',
+ field6: null,
+ array_field: [1, 2],
+ nested_container_field: {
+ field1: 1,
+ field2: 2,
+ },
+ nested_container_field2: {
+ field1: undefined,
+ },
+ },
+ container_field_without_intersection: {
+ sub_field2: 1,
+ },
+ };
+ const intersection = objectPairIntersection(a, b);
+ const expected = {
+ container_field: {
+ field1: 1,
+ field6: null,
+ nested_container_field: {
+ field1: 1,
+ },
+ },
+ };
+ expect(intersection).toEqual(expected);
+ });
+
+ test('should work on objects with a variety of fields', () => {
+ const a = {
+ field1: 1,
+ field2: 1,
+ field3: 10,
+ field5: 1,
+ field6: null,
+ array_field: [1, 2],
+ container_field: {
+ sub_field1: 1,
+ sub_field2: 1,
+ sub_field3: 10,
+ },
+ container_field_without_intersection: {
+ sub_field1: 1,
+ },
+ };
+ const b = {
+ field1: 1,
+ field2: 2,
+ field4: 10,
+ field5: '1',
+ field6: null,
+ array_field: [1, 2],
+ container_field: {
+ sub_field1: 1,
+ sub_field2: 2,
+ sub_field4: 10,
+ },
+ container_field_without_intersection: {
+ sub_field2: 1,
+ },
+ };
+ const intersection = objectPairIntersection(a, b);
+ const expected = {
+ field1: 1,
+ field6: null,
+ container_field: {
+ sub_field1: 1,
+ },
+ };
+ expect(intersection).toEqual(expected);
+ });
+});
+
+describe('objectArrayIntersection', () => {
+ test('should return undefined if the array is empty', () => {
+ const intersection = objectArrayIntersection([]);
+ const expected = undefined;
+ expect(intersection).toEqual(expected);
+ });
+ test('should return the initial object if there is only 1', () => {
+ const a = {
+ field1: 1,
+ field2: 1,
+ field3: 10,
+ field5: 1,
+ field6: null,
+ array_field: [1, 2],
+ container_field: {
+ sub_field1: 1,
+ sub_field2: 1,
+ sub_field3: 10,
+ },
+ container_field_without_intersection: {
+ sub_field1: 1,
+ },
+ };
+ const intersection = objectArrayIntersection([a]);
+ const expected = {
+ field1: 1,
+ field2: 1,
+ field3: 10,
+ field5: 1,
+ field6: null,
+ array_field: [1, 2],
+ container_field: {
+ sub_field1: 1,
+ sub_field2: 1,
+ sub_field3: 10,
+ },
+ container_field_without_intersection: {
+ sub_field1: 1,
+ },
+ };
+ expect(intersection).toEqual(expected);
+ });
+ test('should work with exactly 2 objects', () => {
+ const a = {
+ field1: 1,
+ field2: 1,
+ field3: 10,
+ field5: 1,
+ field6: null,
+ array_field: [1, 2],
+ container_field: {
+ sub_field1: 1,
+ sub_field2: 1,
+ sub_field3: 10,
+ },
+ container_field_without_intersection: {
+ sub_field1: 1,
+ },
+ };
+ const b = {
+ field1: 1,
+ field2: 2,
+ field4: 10,
+ field5: '1',
+ field6: null,
+ array_field: [1, 2],
+ container_field: {
+ sub_field1: 1,
+ sub_field2: 2,
+ sub_field4: 10,
+ },
+ container_field_without_intersection: {
+ sub_field2: 1,
+ },
+ };
+ const intersection = objectArrayIntersection([a, b]);
+ const expected = {
+ field1: 1,
+ field6: null,
+ container_field: {
+ sub_field1: 1,
+ },
+ };
+ expect(intersection).toEqual(expected);
+ });
+
+ test('should work with 3 or more objects', () => {
+ const a = {
+ field1: 1,
+ field2: 1,
+ field3: 10,
+ field5: 1,
+ field6: null,
+ array_field: [1, 2],
+ container_field: {
+ sub_field1: 1,
+ sub_field2: 1,
+ sub_field3: 10,
+ },
+ container_field_without_intersection: {
+ sub_field1: 1,
+ },
+ };
+ const b = {
+ field1: 1,
+ field2: 2,
+ field4: 10,
+ field5: '1',
+ field6: null,
+ array_field: [1, 2],
+ container_field: {
+ sub_field1: 1,
+ sub_field2: 2,
+ sub_field4: 10,
+ },
+ container_field_without_intersection: {
+ sub_field2: 1,
+ },
+ };
+ const c = {
+ field1: 1,
+ field2: 2,
+ field4: 10,
+ field5: '1',
+ array_field: [1, 2],
+ container_field: {
+ sub_field2: 2,
+ sub_field4: 10,
+ },
+ container_field_without_intersection: {
+ sub_field2: 1,
+ },
+ };
+ const intersection = objectArrayIntersection([a, b, c]);
+ const expected = {
+ field1: 1,
+ };
+ expect(intersection).toEqual(expected);
+ });
+});
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts
index f8632a85c77e9..8e9571fe8a445 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts
@@ -129,7 +129,9 @@ export const buildSignalFromSequence = (
): SignalHit => {
const rule = buildRuleWithoutOverrides(ruleSO);
const signal: Signal = buildSignal(events, rule);
+ const mergedEvents = objectArrayIntersection(events.map((event) => event._source));
return {
+ ...mergedEvents,
'@timestamp': new Date().toISOString(),
event: {
kind: 'signal',
@@ -167,3 +169,54 @@ export const buildSignalFromEvent = (
};
return signalHit;
};
+
+export const objectArrayIntersection = (objects: object[]) => {
+ if (objects.length === 0) {
+ return undefined;
+ } else if (objects.length === 1) {
+ return objects[0];
+ } else {
+ return objects
+ .slice(1)
+ .reduce(
+ (acc: object | undefined, obj): object | undefined => objectPairIntersection(acc, obj),
+ objects[0]
+ );
+ }
+};
+
+export const objectPairIntersection = (a: object | undefined, b: object | undefined) => {
+ if (a === undefined || b === undefined) {
+ return undefined;
+ }
+ const intersection: Record = {};
+ Object.entries(a).forEach(([key, aVal]) => {
+ if (key in b) {
+ const bVal = (b as Record)[key];
+ if (
+ typeof aVal === 'object' &&
+ !(aVal instanceof Array) &&
+ aVal !== null &&
+ typeof bVal === 'object' &&
+ !(bVal instanceof Array) &&
+ bVal !== null
+ ) {
+ intersection[key] = objectPairIntersection(aVal, bVal);
+ } else if (aVal === bVal) {
+ intersection[key] = aVal;
+ }
+ }
+ });
+ // Count up the number of entries that are NOT undefined in the intersection
+ // If there are no keys OR all entries are undefined, return undefined
+ if (
+ Object.values(intersection).reduce(
+ (acc: number, value) => (value !== undefined ? acc + 1 : acc),
+ 0
+ ) === 0
+ ) {
+ return undefined;
+ } else {
+ return intersection;
+ }
+};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/index.ts b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/index.ts
index 12729eb88a666..6b28fc2598d41 100644
--- a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/index.ts
+++ b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/index.ts
@@ -42,17 +42,19 @@ export const timelineEventsAll: SecuritySolutionTimelineFactory
+ const edges: TimelineEdges[] = hits.splice(cursorStart, querySize - cursorStart).map((hit) =>
// @ts-expect-error
formatTimelineData(options.fieldRequested, TIMELINE_EVENTS_FIELDS, hit, eventFieldsMap)
);
const inspect = {
dsl: [inspectStringifyObject(buildTimelineEventsAllQuery(queryOptions))],
};
+ const fakeTotalCount = fakePossibleCount <= totalCount ? fakePossibleCount : totalCount;
+ const showMorePagesIndicator = totalCount > fakeTotalCount;
return {
...response,
@@ -61,7 +63,8 @@ export const timelineEventsAll: SecuritySolutionTimelineFactory {
};
return (
-
+
diff --git a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap
index ee1eb7c5e9aba..063a34091c4e7 100644
--- a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap
+++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap
@@ -2,10 +2,8 @@
exports[`EnabledFeatures renders as expected 1`] = `
{
return (
-
-
- hide
-
-
diff --git a/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx
index 0b8085ff1ad16..576881398a63c 100644
--- a/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx
+++ b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx
@@ -4,14 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { EuiLink } from '@elastic/eui';
import React from 'react';
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
import { SectionPanel } from './section_panel';
test('it renders without blowing up', () => {
const wrapper = shallowWithIntl(
-
+
child
);
@@ -19,9 +18,9 @@ test('it renders without blowing up', () => {
expect(wrapper).toMatchSnapshot();
});
-test('it renders children by default', () => {
+test('it renders children', () => {
const wrapper = mountWithIntl(
-
+
child 1
child 2
@@ -30,19 +29,3 @@ test('it renders children by default', () => {
expect(wrapper.find(SectionPanel)).toHaveLength(1);
expect(wrapper.find('.child')).toHaveLength(2);
});
-
-test('it hides children when the "hide" link is clicked', () => {
- const wrapper = mountWithIntl(
-
- child 1
- child 2
-
- );
-
- expect(wrapper.find(SectionPanel)).toHaveLength(1);
- expect(wrapper.find('.child')).toHaveLength(2);
-
- wrapper.find(EuiLink).simulate('click');
-
- expect(wrapper.find('.child')).toHaveLength(0);
-});
diff --git a/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx
index a6d25511acba6..492932aa95741 100644
--- a/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx
+++ b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx
@@ -8,39 +8,20 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
- EuiLink,
EuiPanel,
EuiSpacer,
EuiTitle,
IconType,
} from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
import React, { Component, Fragment, ReactNode } from 'react';
interface Props {
iconType?: IconType;
title: string | ReactNode;
description: string;
- collapsible: boolean;
- initiallyCollapsed?: boolean;
}
-interface State {
- collapsed: boolean;
-}
-
-export class SectionPanel extends Component {
- public state = {
- collapsed: false,
- };
-
- constructor(props: Props) {
- super(props);
- this.state = {
- collapsed: props.initiallyCollapsed || false,
- };
- }
-
+export class SectionPanel extends Component {
public render() {
return (
@@ -51,30 +32,6 @@ export class SectionPanel extends Component {
}
public getTitle = () => {
- const showLinkText = i18n.translate('xpack.spaces.management.collapsiblePanel.showLinkText', {
- defaultMessage: 'show',
- });
-
- const hideLinkText = i18n.translate('xpack.spaces.management.collapsiblePanel.hideLinkText', {
- defaultMessage: 'hide',
- });
-
- const showLinkDescription = i18n.translate(
- 'xpack.spaces.management.collapsiblePanel.showLinkDescription',
- {
- defaultMessage: 'show {title}',
- values: { title: this.props.description },
- }
- );
-
- const hideLinkDescription = i18n.translate(
- 'xpack.spaces.management.collapsiblePanel.hideLinkDescription',
- {
- defaultMessage: 'hide {title}',
- values: { title: this.props.description },
- }
- );
-
return (
@@ -93,26 +50,11 @@ export class SectionPanel extends Component {
- {this.props.collapsible && (
-
-
- {this.state.collapsed ? showLinkText : hideLinkText}
-
-
- )}
);
};
public getForm = () => {
- if (this.state.collapsed) {
- return null;
- }
-
return (
@@ -120,10 +62,4 @@ export class SectionPanel extends Component {
);
};
-
- public toggleCollapsed = () => {
- this.setState({
- collapsed: !this.state.collapsed,
- });
- };
}
diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json
index b08585066f100..bdafbfd8ec967 100644
--- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json
+++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json
@@ -1,5 +1,83 @@
{
"properties": {
+ "actions": {
+ "properties": {
+ "count_total": {
+ "type": "long"
+ },
+ "count_active_total": {
+ "type": "long"
+ },
+ "count_by_type": {
+ "properties": {
+ "DYNAMIC_KEY": {
+ "type": "long"
+ },
+ "__email": {
+ "type": "long"
+ },
+ "__index": {
+ "type": "long"
+ },
+ "__pagerduty": {
+ "type": "long"
+ },
+ "__server-log": {
+ "type": "long"
+ },
+ "__slack": {
+ "type": "long"
+ },
+ "__webhook": {
+ "type": "long"
+ },
+ "__servicenow": {
+ "type": "long"
+ },
+ "__jira": {
+ "type": "long"
+ },
+ "__resilient": {
+ "type": "long"
+ }
+ }
+ },
+ "count_active_by_type": {
+ "properties": {
+ "DYNAMIC_KEY": {
+ "type": "long"
+ },
+ "__email": {
+ "type": "long"
+ },
+ "__index": {
+ "type": "long"
+ },
+ "__pagerduty": {
+ "type": "long"
+ },
+ "__server-log": {
+ "type": "long"
+ },
+ "__slack": {
+ "type": "long"
+ },
+ "__webhook": {
+ "type": "long"
+ },
+ "__servicenow": {
+ "type": "long"
+ },
+ "__jira": {
+ "type": "long"
+ },
+ "__resilient": {
+ "type": "long"
+ }
+ }
+ }
+ }
+ },
"canvas": {
"properties": {
"workpads": {
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index bd9a66b48f633..f144cbd3873f7 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -2962,7 +2962,6 @@
"telemetry.provideUsageStatisticsAriaName": "使用統計を提供",
"telemetry.provideUsageStatisticsTitle": "使用統計を提供",
"telemetry.readOurUsageDataPrivacyStatementLinkText": "プライバシーポリシー",
- "telemetry.seeExampleOfWhatWeCollectLinkText": "収集されるデータの例を見る",
"telemetry.telemetryBannerDescription": "Elastic Stackの改善にご協力ください使用状況データの収集は現在無効です。使用状況データの収集を有効にすると、製品とサービスを管理して改善することができます。詳細については、{privacyStatementLink}をご覧ください。",
"telemetry.telemetryConfigAndLinkDescription": "使用状況データの収集を有効にすると、製品とサービスを管理して改善することができます。詳細については、{privacyStatementLink}をご覧ください。",
"telemetry.telemetryConfigDescription": "基本的な機能の利用状況に関する統計情報を提供して、Elastic Stack の改善にご協力ください。このデータは Elastic 社外と共有されません。",
@@ -14303,8 +14302,6 @@
"xpack.security.management.editRole.elasticSearchPrivileges.manageRoleActionsDescription": "このロールがクラスターに対して実行できる操作を管理します。 ",
"xpack.security.management.editRole.elasticSearchPrivileges.runAsPrivilegesTitle": "権限として実行",
"xpack.security.management.editRole.featureTable.customizeSubFeaturePrivilegesSwitchLabel": "サブ機能権限をカスタマイズする",
- "xpack.security.management.editRole.featureTable.enabledRoleFeaturesEnabledColumnTitle": "権限",
- "xpack.security.management.editRole.featureTable.enabledRoleFeaturesFeatureColumnTitle": "機能",
"xpack.security.management.editRole.featureTable.privilegeCustomizationTooltip": "機能でサブ機能の権限がカスタマイズされています。この行を展開すると詳細が表示されます。",
"xpack.security.management.editRole.indexPrivilegeForm.deleteSpacePrivilegeAriaLabel": "インデックスの権限を削除",
"xpack.security.management.editRole.indexPrivilegeForm.grantedDocumentsQueryFormRowLabel": "提供されたドキュメントのクエリ",
@@ -14346,35 +14343,24 @@
"xpack.security.management.editRole.spaceAwarePrivilegeForm.howToViewAllAvailableSpacesDescription": "利用可能なすべてのスペースを表示する権限がありません。",
"xpack.security.management.editRole.spaceAwarePrivilegeForm.insufficientPrivilegesDescription": "権限が不十分です",
"xpack.security.management.editRole.spaceAwarePrivilegeForm.kibanaAdminTitle": "kibana_admin",
- "xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDetails": "選択されたスペースの全機能への完全アクセスを許可します。",
- "xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDisplay": "すべて",
- "xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDropdownDisplay": "すべて",
"xpack.security.management.editRole.spacePrivilegeForm.cancelButton": "キャンセル",
"xpack.security.management.editRole.spacePrivilegeForm.customizeFeaturePrivilegeDescription": "機能ごとに権限のレベルを上げます。機能によってはスペースごとに非表示になっているか、グローバルスペース権限による影響を受けているものもあります。",
"xpack.security.management.editRole.spacePrivilegeForm.customizeFeaturePrivileges": "機能ごとにカスタマイズ",
- "xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDetails": "選択されたスペースの機能ごとにアクセスをカスタマイズします",
- "xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDisplay": "カスタム",
- "xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDropdownDisplay": "カスタム",
"xpack.security.management.editRole.spacePrivilegeForm.featurePrivilegeSummaryDescription": "機能によってはスペースごとに非表示になっているか、グローバルスペース権限による影響を受けているものもあります。",
"xpack.security.management.editRole.spacePrivilegeForm.globalPrivilegeNotice": "これらの権限はすべての現在および未来のスペースに適用されます。",
"xpack.security.management.editRole.spacePrivilegeForm.globalPrivilegeWarning": "グローバル権限の作成は他のスペース権限に影響を与える可能性があります。",
"xpack.security.management.editRole.spacePrivilegeForm.modalTitle": "スペース権限",
"xpack.security.management.editRole.spacePrivilegeForm.privilegeSelectorFormLabel": "権限",
- "xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDetails": "選択されたスペースの全機能への読み込み専用アクセスを許可します。",
- "xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDisplay": "読み込み",
- "xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDropdownDisplay": "読み込み",
"xpack.security.management.editRole.spacePrivilegeForm.spaceSelectorFormLabel": "スペース",
"xpack.security.management.editRole.spacePrivilegeForm.summaryOfFeaturePrivileges": "機能権限のサマリー",
"xpack.security.management.editRole.spacePrivilegeForm.supersededWarning": "宣言された権限は、構成済みグローバル権限よりも許容度が低くなります。権限サマリーを表示すると有効な権限がわかります。",
"xpack.security.management.editRole.spacePrivilegeForm.supersededWarningTitle": "グローバル権限に置き換え",
"xpack.security.management.editRole.spacePrivilegeMatrix.globalSpaceName": "グローバル",
- "xpack.security.management.editRole.spacePrivilegeMatrix.showAllSpacesLink": "(すべてのスペース)",
"xpack.security.management.editRole.spacePrivilegeMatrix.showNMoreSpacesLink": "他 {count} 件",
"xpack.security.management.editRole.spacePrivilegeSection.addSpacePrivilegeButton": "スペース権限を追加",
"xpack.security.management.editRole.spacePrivilegeSection.noAccessToKibanaTitle": "このロールは Kibana へのアクセスを許可しません",
"xpack.security.management.editRole.spacePrivilegeTable.deletePrivilegesLabel": "次のスペースの権限を削除: {spaceNames}",
"xpack.security.management.editRole.spacePrivilegeTable.editPrivilegesLabel": "次のスペースの権限を編集: {spaceNames}",
- "xpack.security.management.editRole.spacePrivilegeTable.showAllSpacesLink": "スペースを表示",
"xpack.security.management.editRole.spacePrivilegeTable.showLessSpacesLink": "縮小表示",
"xpack.security.management.editRole.spacePrivilegeTable.showNMoreSpacesLink": "他 {count} 件",
"xpack.security.management.editRole.spacePrivilegeTable.supersededPrivilegeWarning": "権限は、構成されたグローバル権限に置き換わります。権限サマリーを表示すると有効な権限がわかります。",
@@ -17361,10 +17347,6 @@
"xpack.spaces.management.advancedSettingsSubtitle.applyingSettingsOnPageToSpaceDescription": "このページの設定は、別途指定されていない限り {spaceName}’スペースに適用されます。’",
"xpack.spaces.management.advancedSettingsTitle.settingsTitle": "設定",
"xpack.spaces.management.breadcrumb": "スペース",
- "xpack.spaces.management.collapsiblePanel.hideLinkDescription": "{title} を非表示",
- "xpack.spaces.management.collapsiblePanel.hideLinkText": "非表示",
- "xpack.spaces.management.collapsiblePanel.showLinkDescription": "{title} を表示",
- "xpack.spaces.management.collapsiblePanel.showLinkText": "表示",
"xpack.spaces.management.confirmAlterActiveSpaceModal.cancelButton": "キャンセル",
"xpack.spaces.management.confirmAlterActiveSpaceModal.reloadWarningMessage": "このスペースで表示される機能を更新しました。保存後にページが更新されます。",
"xpack.spaces.management.confirmAlterActiveSpaceModal.title": "スペースの更新の確認",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 3ce9278234005..c4a599fa35e7f 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -2963,7 +2963,6 @@
"telemetry.provideUsageStatisticsAriaName": "提供使用情况统计",
"telemetry.provideUsageStatisticsTitle": "提供使用情况统计",
"telemetry.readOurUsageDataPrivacyStatementLinkText": "隐私声明",
- "telemetry.seeExampleOfWhatWeCollectLinkText": "查看我们收集的内容示例",
"telemetry.telemetryBannerDescription": "想帮助我们改进 Elastic Stack?数据使用情况收集当前已禁用。启用数据使用情况收集可帮助我们管理并改善产品和服务。有关详情,请参阅我们的{privacyStatementLink}。",
"telemetry.telemetryConfigAndLinkDescription": "启用使用情况数据收集可帮助我们管理并改善产品和服务。有关更多详情,请参阅我们的{privacyStatementLink}。",
"telemetry.telemetryConfigDescription": "通过提供基本功能的使用情况统计信息,来帮助我们改进 Elastic Stack。我们不会在 Elastic 之外共享此数据。",
@@ -14312,8 +14311,6 @@
"xpack.security.management.editRole.elasticSearchPrivileges.manageRoleActionsDescription": "管理此角色可以对您的集群执行的操作。 ",
"xpack.security.management.editRole.elasticSearchPrivileges.runAsPrivilegesTitle": "运行身份权限",
"xpack.security.management.editRole.featureTable.customizeSubFeaturePrivilegesSwitchLabel": "定制子功能权限",
- "xpack.security.management.editRole.featureTable.enabledRoleFeaturesEnabledColumnTitle": "权限",
- "xpack.security.management.editRole.featureTable.enabledRoleFeaturesFeatureColumnTitle": "功能",
"xpack.security.management.editRole.featureTable.privilegeCustomizationTooltip": "功能已定制子功能权限。展开此行以了解更多信息。",
"xpack.security.management.editRole.indexPrivilegeForm.deleteSpacePrivilegeAriaLabel": "删除索引权限",
"xpack.security.management.editRole.indexPrivilegeForm.grantedDocumentsQueryFormRowLabel": "已授权文档查询",
@@ -14355,35 +14352,24 @@
"xpack.security.management.editRole.spaceAwarePrivilegeForm.howToViewAllAvailableSpacesDescription": "您无权查看所有可用工作区。",
"xpack.security.management.editRole.spaceAwarePrivilegeForm.insufficientPrivilegesDescription": "权限不足",
"xpack.security.management.editRole.spaceAwarePrivilegeForm.kibanaAdminTitle": "kibana_admin",
- "xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDetails": "授予对选定工作区所有功能的完全访问权限。",
- "xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDisplay": "全部",
- "xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDropdownDisplay": "全部",
"xpack.security.management.editRole.spacePrivilegeForm.cancelButton": "取消",
"xpack.security.management.editRole.spacePrivilegeForm.customizeFeaturePrivilegeDescription": "按功能提高权限级别。某些功能可能被工作区隐藏或受全局工作区权限影响。",
"xpack.security.management.editRole.spacePrivilegeForm.customizeFeaturePrivileges": "按功能定制",
- "xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDetails": "在选定工作区中按功能定制访问权限。",
- "xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDisplay": "定制",
- "xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDropdownDisplay": "定制",
"xpack.security.management.editRole.spacePrivilegeForm.featurePrivilegeSummaryDescription": "某些功能可能被工作区隐藏或受全局工作区权限影响。",
"xpack.security.management.editRole.spacePrivilegeForm.globalPrivilegeNotice": "这些权限将应用到所有当前和未来工作区。",
"xpack.security.management.editRole.spacePrivilegeForm.globalPrivilegeWarning": "创建全局权限可能会影响您的其他工作区权限。",
"xpack.security.management.editRole.spacePrivilegeForm.modalTitle": "工作区权限",
"xpack.security.management.editRole.spacePrivilegeForm.privilegeSelectorFormLabel": "权限",
- "xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDetails": "授予对选定工作区所有功能的只读访问权限。",
- "xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDisplay": "读取",
- "xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDropdownDisplay": "读取",
"xpack.security.management.editRole.spacePrivilegeForm.spaceSelectorFormLabel": "工作区",
"xpack.security.management.editRole.spacePrivilegeForm.summaryOfFeaturePrivileges": "功能权限的摘要",
"xpack.security.management.editRole.spacePrivilegeForm.supersededWarning": "声明的权限相对配置的全局权限有较小的宽容度。查看权限摘要以查看有效的权限。",
"xpack.security.management.editRole.spacePrivilegeForm.supersededWarningTitle": "已由全局权限取代",
"xpack.security.management.editRole.spacePrivilegeMatrix.globalSpaceName": "全局",
- "xpack.security.management.editRole.spacePrivilegeMatrix.showAllSpacesLink": "(所有工作区)",
"xpack.security.management.editRole.spacePrivilegeMatrix.showNMoreSpacesLink": "另外 {count} 个",
"xpack.security.management.editRole.spacePrivilegeSection.addSpacePrivilegeButton": "添加工作区权限",
"xpack.security.management.editRole.spacePrivilegeSection.noAccessToKibanaTitle": "此角色未授予对 Kibana 的访问权限",
"xpack.security.management.editRole.spacePrivilegeTable.deletePrivilegesLabel": "删除以下工作区的权限:{spaceNames}。",
"xpack.security.management.editRole.spacePrivilegeTable.editPrivilegesLabel": "编辑以下工作区的权限:{spaceNames}。",
- "xpack.security.management.editRole.spacePrivilegeTable.showAllSpacesLink": "显示工作区",
"xpack.security.management.editRole.spacePrivilegeTable.showLessSpacesLink": "显示更少",
"xpack.security.management.editRole.spacePrivilegeTable.showNMoreSpacesLink": "另外 {count} 个",
"xpack.security.management.editRole.spacePrivilegeTable.supersededPrivilegeWarning": "权限已由配置的全局权限取代。查看权限摘要以查看有效的权限。",
@@ -17371,10 +17357,6 @@
"xpack.spaces.management.advancedSettingsSubtitle.applyingSettingsOnPageToSpaceDescription": "除非已指定,否则此页面上的设置适用于 {spaceName} 空间。",
"xpack.spaces.management.advancedSettingsTitle.settingsTitle": "设置",
"xpack.spaces.management.breadcrumb": "工作区",
- "xpack.spaces.management.collapsiblePanel.hideLinkDescription": "隐藏 {title}",
- "xpack.spaces.management.collapsiblePanel.hideLinkText": "隐藏",
- "xpack.spaces.management.collapsiblePanel.showLinkDescription": "显示 {title}",
- "xpack.spaces.management.collapsiblePanel.showLinkText": "显示",
"xpack.spaces.management.confirmAlterActiveSpaceModal.cancelButton": "取消",
"xpack.spaces.management.confirmAlterActiveSpaceModal.reloadWarningMessage": "您已更新此工作区中的可见功能。保存后,您的页面将重新加载。",
"xpack.spaces.management.confirmAlterActiveSpaceModal.title": "确认更新工作区",
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx
index 39800865ed761..32f16760dd461 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { Fragment } from 'react';
-import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSelect } from '@elastic/eui';
+import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSelect, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { ActionParamsProps } from '../../../../types';
import { PagerDutyActionParams } from '.././types';
@@ -143,6 +143,29 @@ const PagerDutyParamsFields: React.FunctionComponent
+
+
+
+
-