From 691c8956a20721a1e4f57a7f925baf12be00e327 Mon Sep 17 00:00:00 2001
From: Andrew Goldstein
Date: Mon, 18 Apr 2022 16:36:02 -0600
Subject: [PATCH] This PR fixes the following issues related to sorting
unmapped fields in timelines and the events / alerts tables:
-
-
-
The `unmapped_type` property [addition](https://github.com/elastic/kibana/pull/87241/files#diff-52fd5870dcd5f783f9fc8ac3a18a8674d83ac6136e09fe0e0bcae30427d61c3fR55) to the `sort` parameter of requests was using the `type` field metadata from `BrowserFields`, but the `type` metadata (for some fields) contains the value `string`, which is not a [valid field data type](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html).
The fix for the issues above:
- Populates the `sort` property of requests with values from the `esTypes` `BrowserFields` metadata (instead of `type`)
- The `esTypes` metadata may specify more than one field value type. When `esTypes` contains more than one type, and `keyword` is one of the types, the `sort` property of the request will prefer `keyword` over other the other types
- When the field metadata has an empty `esTypes` collection, the `sort` property of the request will default to using `"unmapped_type": "keyword"`
- The field type displayed in tooltips when hovering over columns in a timeline now displays values from `esTypes` instead of `type`
To reproduce issue and to verify the fix:
1) Open Kibana `Dev tools`
2) Execute the following query to delete any exiting `logs-ti_test` index:
```
DELETE logs-ti_test
```
3) Execute the following query to create an index named `logs-ti_test`, which has the following properities:
- Dynamic mappings are disabled via `"dynamic": false`
- It does NOT contain a mapping for `event.action` (we will sort by this field in later steps)
- It contains a mapping for the non-ECS `testing` field
```
PUT logs-ti_test
{
"mappings": {
"dynamic": false,
"properties": {
"@timestamp": {
"type": "date"
},
"event": {
"properties": {
"category": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"dataset": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"kind": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"type": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
"host": {
"properties": {
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
"testing": {
"type": "keyword",
"ignore_above": 1024
},
"threat": {
"properties": {
"indicator": {
"properties": {
"file": {
"properties": {
"hash": {
"properties": {
"md5": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
}
}
}
}
}
}
}
```
4) Execute the following query to add a new document to the `logs-ti_test` index, and note that:
- It does NOT contain a `event.action` field
- It contains a value for the non-ECS `testing` field
```
POST logs-ti_test/_doc/
{
"@timestamp": "2022-05-12T00:00:14.725Z",
"host": {
"name": "foozle"
},
"threat": {
"indicator": {
"file": {
"hash": {
"md5": "a4f87cbcd2a4241da77b6bf0c5d9e8553fec991f"
}
}
}
},
"event": {
"kind": "enrichment",
"type": "indicator",
"dataset": "ti_*",
"category": "threat"
},
"testing": "simulated threat intel data"
}
```
5) Navigate to the Security > Hosts page
6) Select `Last 1 year` from the date picker
7) Click the `Events` tab
8) Enter the following KQL query in the search bar at the top of the page:
```
host.name: foozle
```
9) Hover over the `foozle` entry in the `host.name` column in the Events table, and click the `Add to timeline investigation` cell action
10) Open the timeline
11) Hover over the `event.action` field
**Expected result**
- The tooltip displays type `keyword` for the `event.action` field
**Actual result**
- The tooltip displays type `string` for the `event.action` field
12) Click the `event.action` column to add a secondary sort
**Expected result**
- The table is sorted by `@timestamp` and `event.action`
- The table contents are (still) visible
**Actual result**
- The table is sorted by `@timestamp` and `event.action`
- The contents of the table are now empty
13) Click the timeline's `Inspect` button
14) In the `Inspect Timeline` dialog, click the `Request` tab
15) Scroll down to the `sort` property of the request
**Expected result**
- The `event.action` field contains a `"unmapped_type": "keyword"` property, per the example below:
```json
"sort": [
{
"@timestamp": {
"order": "desc",
"unmapped_type": "number"
}
},
{
"event.action": {
"order": "desc",
"unmapped_type": "keyword"
}
}
],
```
**Actual result**
- The request's `event.action` field contains a `"unmapped_type": "string"` property, per the example below:
```json
"sort": [
{
"@timestamp": {
"order": "desc",
"unmapped_type": "number"
}
},
{
"event.action": {
"order": "desc",
"unmapped_type": "string"
}
}
],
```
16) In the `Inspect Timeline` dialog, click the `Response` tab
**Expected result**
- The response contains `0` `failed` shards / no failures
**Actual result**
- The response contains failures for the `logs-ti_test` index, with the following reason:
```
"reason": "No mapper found for type [string]"
```
per the example below:
```json
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 4,
"successful": 3,
"skipped": 0,
"failed": 1,
"failures": [
{
"shard": 0,
"index": "logs-ti_test",
"node": "NCRcGeDqSlKQiuPWVFvMEg",
"reason": {
"type": "illegal_argument_exception",
"reason": "No mapper found for type [string]"
}
}
]
},
```
---
.../common/search_strategy/timeline/index.ts | 1 +
.../common/types/timeline/store.ts | 1 +
.../drag_drop_context_wrapper.test.tsx.snap | 98 +++++++++++++++-
.../events_viewer/default_headers.tsx | 2 +
.../source/__snapshots__/index.test.tsx.snap | 98 +++++++++++++++-
.../public/common/containers/source/mock.ts | 72 +++++++++++-
.../public/common/mock/global_state.ts | 9 +-
.../public/common/mock/header.ts | 27 +++--
.../public/common/mock/timeline_results.ts | 20 +++-
.../components/alerts_table/actions.test.tsx | 3 +-
.../alerts_table/default_config.tsx | 3 +-
.../components/open_timeline/helpers.test.ts | 3 +-
.../timeline/body/actions/header_actions.tsx | 17 ++-
.../__snapshots__/index.test.tsx.snap | 108 +++++++++++++++++-
.../body/column_headers/column_header.tsx | 8 +-
.../body/column_headers/default_headers.ts | 3 +-
.../header/__snapshots__/index.test.tsx.snap | 25 +++-
.../body/column_headers/header/index.test.tsx | 10 +-
.../body/column_headers/header/index.tsx | 5 +-
.../__snapshots__/index.test.tsx.snap | 7 +-
.../header_tooltip_content/index.test.tsx | 19 ++-
.../header_tooltip_content/index.tsx | 9 +-
.../body/column_headers/helpers.test.ts | 3 +
.../body/column_headers/index.test.tsx | 50 ++++++--
.../components/timeline/body/index.test.tsx | 3 +-
.../plain_column_renderer.test.tsx.snap | 2 +-
.../__snapshots__/index.test.tsx.snap | 47 ++++++--
.../__snapshots__/index.test.tsx.snap | 52 +++++++--
.../pinned_tab_content/index.test.tsx | 3 +-
.../timeline/pinned_tab_content/index.tsx | 3 +-
.../__snapshots__/index.test.tsx.snap | 52 +++++++--
.../timeline/query_tab_content/index.test.tsx | 3 +-
.../timeline/query_tab_content/index.tsx | 3 +-
.../public/timelines/containers/index.tsx | 5 +-
.../timelines/store/timeline/defaults.ts | 3 +-
.../timelines/store/timeline/epic.test.ts | 12 +-
.../timeline/epic_local_storage.test.tsx | 4 +-
.../timelines/store/timeline/reducer.test.ts | 11 +-
.../search_strategy/index_fields/index.ts | 1 +
.../common/search_strategy/timeline/index.ts | 1 +
.../common/types/timeline/columns/index.tsx | 1 +
.../timelines/common/types/timeline/store.ts | 1 +
.../body/column_headers/default_headers.ts | 3 +-
.../body/column_headers/helpers.test.tsx | 1 +
.../components/t_grid/body/helpers.test.tsx | 34 +++++-
.../public/components/t_grid/body/helpers.tsx | 17 ++-
.../components/t_grid/body/index.test.tsx | 3 +-
.../components/t_grid/integrated/index.tsx | 3 +-
.../components/t_grid/standalone/index.tsx | 3 +-
.../timelines/public/container/index.tsx | 5 +-
.../timelines/public/mock/global_state.ts | 9 +-
.../plugins/timelines/public/mock/header.ts | 27 +++--
.../public/mock/mock_timeline_data.ts | 3 +-
.../plugins/timelines/public/mock/t_grid.tsx | 1 +
.../timelines/public/store/t_grid/defaults.ts | 4 +-
.../public/store/t_grid/helpers.test.tsx | 2 +-
.../timeline/eql/helpers.test.ts | 20 ++--
.../factory/events/all/helpers.test.ts | 42 +++++++
.../timeline/factory/events/all/helpers.ts | 21 ++++
.../events/all/query.events_all.dsl.ts | 3 +-
60 files changed, 871 insertions(+), 138 deletions(-)
create mode 100644 x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/helpers.test.ts
create mode 100644 x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/helpers.ts
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 b293ad9660988..c25845ab7036d 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
@@ -47,6 +47,7 @@ export interface TimelineRequestBasicOptions extends IEsSearchRequest {
export interface TimelineRequestSortField extends SortField {
type: string;
+ esTypes: string[];
}
export interface TimelineRequestOptionsPaginated
diff --git a/x-pack/plugins/security_solution/common/types/timeline/store.ts b/x-pack/plugins/security_solution/common/types/timeline/store.ts
index 250ff061e81dd..f9399e4eeb6f4 100644
--- a/x-pack/plugins/security_solution/common/types/timeline/store.ts
+++ b/x-pack/plugins/security_solution/common/types/timeline/store.ts
@@ -33,6 +33,7 @@ export type SortDirection = 'none' | 'asc' | 'desc' | Direction;
export interface SortColumnTimeline {
columnId: string;
columnType: string;
+ esTypes?: string[];
sortDirection: SortDirection;
}
diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/__snapshots__/drag_drop_context_wrapper.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/__snapshots__/drag_drop_context_wrapper.test.tsx.snap
index 46add75debdd6..a0ef37259aaa2 100644
--- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/__snapshots__/drag_drop_context_wrapper.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/__snapshots__/drag_drop_context_wrapper.test.tsx.snap
@@ -10,6 +10,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "agent",
"description": "Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but \`agent.id\` does not.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "8a4f500f",
"format": "",
"indexes": Array [
@@ -25,6 +28,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "agent",
"description": null,
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -40,6 +46,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "agent",
"description": "Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "8a4f500d",
"format": "",
"indexes": Array [
@@ -55,6 +64,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "agent",
"description": "Name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "foo",
"format": "",
"indexes": Array [
@@ -74,6 +86,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "auditd",
"description": null,
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -87,6 +102,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "auditd",
"description": null,
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -100,6 +118,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "auditd",
"description": null,
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -117,6 +138,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "base",
"description": "Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.",
+ "esTypes": Array [
+ "date",
+ ],
"example": "2016-05-23T08:05:34.853Z",
"format": "",
"indexes": Array [
@@ -133,6 +157,7 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": false,
"category": "base",
"description": "Each document has an _id that uniquely identifies it",
+ "esTypes": Array [],
"example": "Y-6TfmcB0WOhS6qyMv3s",
"indexes": Array [
"auditbeat",
@@ -147,6 +172,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": false,
"category": "base",
"description": "For log events the message field contains the log message, optimized for viewing in a log viewer. For structured logs without an original message field, other fields can be concatenated to form a human-readable summary of the event. If multiple messages exist, they can be combined into one message.",
+ "esTypes": Array [
+ "text",
+ ],
"example": "Hello World",
"format": "string",
"indexes": Array [
@@ -166,6 +194,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "client",
"description": "Some event client addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the \`.address\` field. Then it should be duplicated to \`.ip\` or \`.domain\`, depending on which one it is.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -181,6 +212,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "client",
"description": "Bytes sent from the client to the server.",
+ "esTypes": Array [
+ "long",
+ ],
"example": "184",
"format": "",
"indexes": Array [
@@ -196,6 +230,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "client",
"description": "Client domain.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -211,6 +248,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "client",
"description": "Country ISO code.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "CA",
"format": "",
"indexes": Array [
@@ -230,6 +270,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "cloud",
"description": "The cloud account or organization id used to identify different entities in a multi-tenant environment. Examples: AWS account id, Google Cloud ORG Id, or other unique identifier.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "666777888999",
"format": "",
"indexes": Array [
@@ -245,6 +288,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "cloud",
"description": "Availability zone in which this host is running.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "us-east-1c",
"format": "",
"indexes": Array [
@@ -264,6 +310,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "container",
"description": "Unique container id.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -279,6 +328,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "container",
"description": "Name of the image the container was built on.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -294,6 +346,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "container",
"description": "Container image tag.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -313,6 +368,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "destination",
"description": "Some event destination addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the \`.address\` field. Then it should be duplicated to \`.ip\` or \`.domain\`, depending on which one it is.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -328,6 +386,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "destination",
"description": "Bytes sent from the destination to the source.",
+ "esTypes": Array [
+ "long",
+ ],
"example": "184",
"format": "",
"indexes": Array [
@@ -343,6 +404,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "destination",
"description": "Destination domain.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -358,6 +422,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "destination",
"description": "IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.",
+ "esTypes": Array [
+ "ip",
+ ],
"example": "",
"format": "",
"indexes": Array [
@@ -373,6 +440,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "destination",
"description": "Port of the destination.",
+ "esTypes": Array [
+ "long",
+ ],
"example": "",
"format": "",
"indexes": Array [
@@ -382,7 +452,7 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
],
"name": "destination.port",
"searchable": true,
- "type": "long",
+ "type": "number",
},
},
},
@@ -392,6 +462,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "event",
"description": "The action captured by the event. This describes the information in the event. It is more specific than \`event.category\`. Examples are \`group-add\`, \`process-started\`, \`file-created\`. The value is normally defined by the implementer.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "user-password-change",
"format": "string",
"indexes": Array [
@@ -413,6 +486,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "event",
"description": "This is one of four ECS Categorization Fields, and indicates the second level in the ECS category hierarchy. \`event.category\` represents the \\"big buckets\\" of ECS categories. For example, filtering on \`event.category:process\` yields all events relating to process activity. This field is closely related to \`event.type\`, which is used as a subcategory. This field is an array. This will allow proper categorization of some events that fall in multiple categories.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "authentication",
"format": "string",
"indexes": Array [
@@ -434,6 +510,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "event",
"description": "event.end contains the date when the event ended or when the activity was last observed.",
+ "esTypes": Array [
+ "date",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -455,6 +534,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "event",
"description": "The numeric severity of the event according to your event source. What the different severity values mean can be different between sources and use cases. It's up to the implementer to make sure severities are consistent across events from the same source. The Syslog severity belongs in \`log.syslog.severity.code\`. \`event.severity\` is meant to represent the severity according to the event source (e.g. firewall, IDS). If the event source does not publish its own severity, you may optionally copy the \`log.syslog.severity.code\` to \`event.severity\`.",
+ "esTypes": Array [
+ "long",
+ ],
"example": 7,
"format": "number",
"indexes": Array [
@@ -480,6 +562,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "host",
"description": "Name of the host. It can contain what \`hostname\` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.",
+ "esTypes": Array [
+ "keyword",
+ ],
"format": "string",
"indexes": Array [
"apm-*-transaction*",
@@ -548,6 +633,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "source",
"description": "IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.",
+ "esTypes": Array [
+ "ip",
+ ],
"example": "",
"format": "",
"indexes": Array [
@@ -563,6 +651,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "source",
"description": "Port of the source.",
+ "esTypes": Array [
+ "long",
+ ],
"example": "",
"format": "",
"indexes": Array [
@@ -572,7 +663,7 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
],
"name": "source.port",
"searchable": true,
- "type": "long",
+ "type": "number",
},
},
},
@@ -582,6 +673,9 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
"aggregatable": true,
"category": "user",
"description": "Short name or login of the user.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "albert",
"format": "string",
"indexes": Array [
diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/default_headers.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/default_headers.tsx
index 1578c77f283fb..b7de934cfaa0a 100644
--- a/x-pack/plugins/security_solution/public/common/components/events_viewer/default_headers.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/default_headers.tsx
@@ -14,6 +14,8 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
columnHeaderType: defaultColumnHeaderType,
id: '@timestamp',
initialWidth: DEFAULT_DATE_COLUMN_MIN_WIDTH,
+ esTypes: ['date'],
+ type: 'date',
},
{
columnHeaderType: defaultColumnHeaderType,
diff --git a/x-pack/plugins/security_solution/public/common/containers/source/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/containers/source/__snapshots__/index.test.tsx.snap
index 43329875275ca..37e6a9b6ba0a6 100644
--- a/x-pack/plugins/security_solution/public/common/containers/source/__snapshots__/index.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/common/containers/source/__snapshots__/index.test.tsx.snap
@@ -6,6 +6,9 @@ Array [
"aggregatable": true,
"category": "agent",
"description": "Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but \`agent.id\` does not.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "8a4f500f",
"format": "",
"indexes": Array [
@@ -21,6 +24,9 @@ Array [
"aggregatable": true,
"category": "agent",
"description": null,
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -36,6 +42,9 @@ Array [
"aggregatable": true,
"category": "agent",
"description": "Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "8a4f500d",
"format": "",
"indexes": Array [
@@ -51,6 +60,9 @@ Array [
"aggregatable": true,
"category": "agent",
"description": "Name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "foo",
"format": "",
"indexes": Array [
@@ -66,6 +78,9 @@ Array [
"aggregatable": true,
"category": "auditd",
"description": null,
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -79,6 +94,9 @@ Array [
"aggregatable": true,
"category": "auditd",
"description": null,
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -92,6 +110,9 @@ Array [
"aggregatable": true,
"category": "auditd",
"description": null,
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -105,6 +126,9 @@ Array [
"aggregatable": true,
"category": "base",
"description": "Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.",
+ "esTypes": Array [
+ "date",
+ ],
"example": "2016-05-23T08:05:34.853Z",
"format": "",
"indexes": Array [
@@ -121,6 +145,7 @@ Array [
"aggregatable": false,
"category": "base",
"description": "Each document has an _id that uniquely identifies it",
+ "esTypes": Array [],
"example": "Y-6TfmcB0WOhS6qyMv3s",
"indexes": Array [
"auditbeat",
@@ -135,6 +160,9 @@ Array [
"aggregatable": false,
"category": "base",
"description": "For log events the message field contains the log message, optimized for viewing in a log viewer. For structured logs without an original message field, other fields can be concatenated to form a human-readable summary of the event. If multiple messages exist, they can be combined into one message.",
+ "esTypes": Array [
+ "text",
+ ],
"example": "Hello World",
"format": "string",
"indexes": Array [
@@ -150,6 +178,9 @@ Array [
"aggregatable": true,
"category": "client",
"description": "Some event client addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the \`.address\` field. Then it should be duplicated to \`.ip\` or \`.domain\`, depending on which one it is.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -165,6 +196,9 @@ Array [
"aggregatable": true,
"category": "client",
"description": "Bytes sent from the client to the server.",
+ "esTypes": Array [
+ "long",
+ ],
"example": "184",
"format": "",
"indexes": Array [
@@ -180,6 +214,9 @@ Array [
"aggregatable": true,
"category": "client",
"description": "Client domain.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -195,6 +232,9 @@ Array [
"aggregatable": true,
"category": "client",
"description": "Country ISO code.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "CA",
"format": "",
"indexes": Array [
@@ -210,6 +250,9 @@ Array [
"aggregatable": true,
"category": "cloud",
"description": "The cloud account or organization id used to identify different entities in a multi-tenant environment. Examples: AWS account id, Google Cloud ORG Id, or other unique identifier.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "666777888999",
"format": "",
"indexes": Array [
@@ -225,6 +268,9 @@ Array [
"aggregatable": true,
"category": "cloud",
"description": "Availability zone in which this host is running.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "us-east-1c",
"format": "",
"indexes": Array [
@@ -240,6 +286,9 @@ Array [
"aggregatable": true,
"category": "container",
"description": "Unique container id.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -255,6 +304,9 @@ Array [
"aggregatable": true,
"category": "container",
"description": "Name of the image the container was built on.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -270,6 +322,9 @@ Array [
"aggregatable": true,
"category": "container",
"description": "Container image tag.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -285,6 +340,9 @@ Array [
"aggregatable": true,
"category": "destination",
"description": "Some event destination addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the \`.address\` field. Then it should be duplicated to \`.ip\` or \`.domain\`, depending on which one it is.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -300,6 +358,9 @@ Array [
"aggregatable": true,
"category": "destination",
"description": "Bytes sent from the destination to the source.",
+ "esTypes": Array [
+ "long",
+ ],
"example": "184",
"format": "",
"indexes": Array [
@@ -315,6 +376,9 @@ Array [
"aggregatable": true,
"category": "destination",
"description": "Destination domain.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -330,6 +394,9 @@ Array [
"aggregatable": true,
"category": "destination",
"description": "IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.",
+ "esTypes": Array [
+ "ip",
+ ],
"example": "",
"format": "",
"indexes": Array [
@@ -345,6 +412,9 @@ Array [
"aggregatable": true,
"category": "destination",
"description": "Port of the destination.",
+ "esTypes": Array [
+ "long",
+ ],
"example": "",
"format": "",
"indexes": Array [
@@ -354,12 +424,15 @@ Array [
],
"name": "destination.port",
"searchable": true,
- "type": "long",
+ "type": "number",
},
Object {
"aggregatable": true,
"category": "event",
"description": "event.end contains the date when the event ended or when the activity was last observed.",
+ "esTypes": Array [
+ "date",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -381,6 +454,9 @@ Array [
"aggregatable": true,
"category": "event",
"description": "The action captured by the event. This describes the information in the event. It is more specific than \`event.category\`. Examples are \`group-add\`, \`process-started\`, \`file-created\`. The value is normally defined by the implementer.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "user-password-change",
"format": "string",
"indexes": Array [
@@ -402,6 +478,9 @@ Array [
"aggregatable": true,
"category": "event",
"description": "This is one of four ECS Categorization Fields, and indicates the second level in the ECS category hierarchy. \`event.category\` represents the \\"big buckets\\" of ECS categories. For example, filtering on \`event.category:process\` yields all events relating to process activity. This field is closely related to \`event.type\`, which is used as a subcategory. This field is an array. This will allow proper categorization of some events that fall in multiple categories.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "authentication",
"format": "string",
"indexes": Array [
@@ -423,6 +502,9 @@ Array [
"aggregatable": true,
"category": "event",
"description": "The numeric severity of the event according to your event source. What the different severity values mean can be different between sources and use cases. It's up to the implementer to make sure severities are consistent across events from the same source. The Syslog severity belongs in \`log.syslog.severity.code\`. \`event.severity\` is meant to represent the severity according to the event source (e.g. firewall, IDS). If the event source does not publish its own severity, you may optionally copy the \`log.syslog.severity.code\` to \`event.severity\`.",
+ "esTypes": Array [
+ "long",
+ ],
"example": 7,
"format": "number",
"indexes": Array [
@@ -444,6 +526,9 @@ Array [
"aggregatable": true,
"category": "host",
"description": "Name of the host. It can contain what \`hostname\` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.",
+ "esTypes": Array [
+ "keyword",
+ ],
"format": "string",
"indexes": Array [
"apm-*-transaction*",
@@ -464,6 +549,9 @@ Array [
"aggregatable": true,
"category": "source",
"description": "IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.",
+ "esTypes": Array [
+ "ip",
+ ],
"example": "",
"format": "",
"indexes": Array [
@@ -479,6 +567,9 @@ Array [
"aggregatable": true,
"category": "source",
"description": "Port of the source.",
+ "esTypes": Array [
+ "long",
+ ],
"example": "",
"format": "",
"indexes": Array [
@@ -488,12 +579,15 @@ Array [
],
"name": "source.port",
"searchable": true,
- "type": "long",
+ "type": "number",
},
Object {
"aggregatable": true,
"category": "user",
"description": "Short name or login of the user.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "albert",
"format": "string",
"indexes": Array [
diff --git a/x-pack/plugins/security_solution/public/common/containers/source/mock.ts b/x-pack/plugins/security_solution/public/common/containers/source/mock.ts
index a1b17484001d0..17f216baed3b7 100644
--- a/x-pack/plugins/security_solution/public/common/containers/source/mock.ts
+++ b/x-pack/plugins/security_solution/public/common/containers/source/mock.ts
@@ -22,6 +22,7 @@ export const mocksSource = {
name: '@timestamp',
searchable: true,
type: 'date',
+ esTypes: ['date'],
aggregatable: true,
readFromDocValues: true,
},
@@ -31,6 +32,7 @@ export const mocksSource = {
example: 'Y-6TfmcB0WOhS6qyMv3s',
name: '_id',
type: 'string',
+ esTypes: [],
searchable: true,
aggregatable: false,
indexes: ['auditbeat', 'filebeat', 'packetbeat'],
@@ -42,6 +44,7 @@ export const mocksSource = {
example: 'Hello World',
name: 'message',
type: 'string',
+ esTypes: ['text'],
searchable: true,
aggregatable: false,
format: 'string',
@@ -57,6 +60,7 @@ export const mocksSource = {
name: 'agent.ephemeral_id',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
},
{
@@ -68,6 +72,7 @@ export const mocksSource = {
name: 'agent.hostname',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
},
{
@@ -80,6 +85,7 @@ export const mocksSource = {
name: 'agent.id',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
},
{
@@ -92,6 +98,7 @@ export const mocksSource = {
name: 'agent.name',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
},
{
@@ -103,6 +110,7 @@ export const mocksSource = {
name: 'auditd.data.a0',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
},
{
@@ -114,6 +122,7 @@ export const mocksSource = {
name: 'auditd.data.a1',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
},
{
@@ -125,6 +134,7 @@ export const mocksSource = {
name: 'auditd.data.a2',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
},
{
@@ -137,6 +147,7 @@ export const mocksSource = {
name: 'client.address',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
},
{
@@ -148,6 +159,7 @@ export const mocksSource = {
name: 'client.bytes',
searchable: true,
type: 'number',
+ esTypes: ['long'],
aggregatable: true,
},
{
@@ -159,6 +171,7 @@ export const mocksSource = {
name: 'client.domain',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
},
{
@@ -170,6 +183,7 @@ export const mocksSource = {
name: 'client.geo.country_iso_code',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
},
{
@@ -182,6 +196,7 @@ export const mocksSource = {
name: 'cloud.account.id',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
},
{
@@ -193,6 +208,7 @@ export const mocksSource = {
name: 'cloud.availability_zone',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
},
{
@@ -204,6 +220,7 @@ export const mocksSource = {
name: 'container.id',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
},
{
@@ -215,6 +232,7 @@ export const mocksSource = {
name: 'container.image.name',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
},
{
@@ -226,6 +244,7 @@ export const mocksSource = {
name: 'container.image.tag',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
},
{
@@ -238,6 +257,7 @@ export const mocksSource = {
name: 'destination.address',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
},
{
@@ -249,6 +269,7 @@ export const mocksSource = {
name: 'destination.bytes',
searchable: true,
type: 'number',
+ esTypes: ['long'],
aggregatable: true,
},
{
@@ -260,6 +281,7 @@ export const mocksSource = {
name: 'destination.domain',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
},
{
@@ -272,6 +294,7 @@ export const mocksSource = {
name: 'destination.ip',
searchable: true,
type: 'ip',
+ esTypes: ['ip'],
},
{
aggregatable: true,
@@ -282,7 +305,8 @@ export const mocksSource = {
indexes: ['auditbeat', 'filebeat', 'packetbeat'],
name: 'destination.port',
searchable: true,
- type: 'long',
+ type: 'number',
+ esTypes: ['long'],
},
{
aggregatable: true,
@@ -294,6 +318,7 @@ export const mocksSource = {
name: 'source.ip',
searchable: true,
type: 'ip',
+ esTypes: ['ip'],
},
{
aggregatable: true,
@@ -304,7 +329,8 @@ export const mocksSource = {
indexes: ['auditbeat', 'filebeat', 'packetbeat'],
name: 'source.port',
searchable: true,
- type: 'long',
+ type: 'number',
+ esTypes: ['long'],
},
{
aggregatable: true,
@@ -317,6 +343,7 @@ export const mocksSource = {
name: 'event.end',
searchable: true,
type: 'date',
+ esTypes: ['date'],
},
{
category: 'event',
@@ -325,6 +352,7 @@ export const mocksSource = {
example: 'user-password-change',
name: 'event.action',
type: 'string',
+ esTypes: ['keyword'],
searchable: true,
aggregatable: true,
format: 'string',
@@ -337,6 +365,7 @@ export const mocksSource = {
example: 'authentication',
name: 'event.category',
type: 'string',
+ esTypes: ['keyword'],
searchable: true,
aggregatable: true,
format: 'string',
@@ -349,6 +378,7 @@ export const mocksSource = {
example: 7,
name: 'event.severity',
type: 'number',
+ esTypes: ['long'],
format: 'number',
searchable: true,
aggregatable: true,
@@ -360,6 +390,7 @@ export const mocksSource = {
'Name of the host. It can contain what `hostname` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.',
name: 'host.name',
type: 'string',
+ esTypes: ['keyword'],
searchable: true,
aggregatable: true,
format: 'string',
@@ -371,6 +402,7 @@ export const mocksSource = {
example: 'albert',
name: 'user.name',
type: 'string',
+ esTypes: ['keyword'],
searchable: true,
aggregatable: true,
format: 'string',
@@ -459,6 +491,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'agent.ephemeral_id',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
},
'agent.hostname': {
aggregatable: true,
@@ -470,6 +503,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'agent.hostname',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
},
'agent.id': {
aggregatable: true,
@@ -482,6 +516,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'agent.id',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
},
'agent.name': {
aggregatable: true,
@@ -494,6 +529,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'agent.name',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
},
},
},
@@ -509,6 +545,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'auditd.data.a0',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
},
'auditd.data.a1': {
aggregatable: true,
@@ -520,6 +557,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'auditd.data.a1',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
},
'auditd.data.a2': {
aggregatable: true,
@@ -531,6 +569,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'auditd.data.a2',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
},
},
},
@@ -547,6 +586,7 @@ export const mockBrowserFields: BrowserFields = {
name: '@timestamp',
searchable: true,
type: 'date',
+ esTypes: ['date'],
readFromDocValues: true,
},
_id: {
@@ -555,6 +595,7 @@ export const mockBrowserFields: BrowserFields = {
example: 'Y-6TfmcB0WOhS6qyMv3s',
name: '_id',
type: 'string',
+ esTypes: [],
searchable: true,
aggregatable: false,
indexes: ['auditbeat', 'filebeat', 'packetbeat'],
@@ -566,6 +607,7 @@ export const mockBrowserFields: BrowserFields = {
example: 'Hello World',
name: 'message',
type: 'string',
+ esTypes: ['text'],
searchable: true,
aggregatable: false,
format: 'string',
@@ -586,6 +628,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'client.address',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
},
'client.bytes': {
aggregatable: true,
@@ -597,6 +640,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'client.bytes',
searchable: true,
type: 'number',
+ esTypes: ['long'],
},
'client.domain': {
aggregatable: true,
@@ -608,6 +652,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'client.domain',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
},
'client.geo.country_iso_code': {
aggregatable: true,
@@ -619,6 +664,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'client.geo.country_iso_code',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
},
},
},
@@ -635,6 +681,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'cloud.account.id',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
},
'cloud.availability_zone': {
aggregatable: true,
@@ -646,6 +693,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'cloud.availability_zone',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
},
},
},
@@ -661,6 +709,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'container.id',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
},
'container.image.name': {
aggregatable: true,
@@ -672,6 +721,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'container.image.name',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
},
'container.image.tag': {
aggregatable: true,
@@ -683,6 +733,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'container.image.tag',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
},
},
},
@@ -699,6 +750,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'destination.address',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
},
'destination.bytes': {
aggregatable: true,
@@ -710,6 +762,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'destination.bytes',
searchable: true,
type: 'number',
+ esTypes: ['long'],
},
'destination.domain': {
aggregatable: true,
@@ -721,6 +774,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'destination.domain',
searchable: true,
type: 'string',
+ esTypes: ['keyword'],
},
'destination.ip': {
aggregatable: true,
@@ -733,6 +787,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'destination.ip',
searchable: true,
type: 'ip',
+ esTypes: ['ip'],
},
'destination.port': {
aggregatable: true,
@@ -743,7 +798,8 @@ export const mockBrowserFields: BrowserFields = {
indexes: ['auditbeat', 'filebeat', 'packetbeat'],
name: 'destination.port',
searchable: true,
- type: 'long',
+ type: 'number',
+ esTypes: ['long'],
},
},
},
@@ -759,6 +815,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'event.end',
searchable: true,
type: 'date',
+ esTypes: ['date'],
aggregatable: true,
},
'event.action': {
@@ -768,6 +825,7 @@ export const mockBrowserFields: BrowserFields = {
example: 'user-password-change',
name: 'event.action',
type: 'string',
+ esTypes: ['keyword'],
searchable: true,
aggregatable: true,
format: 'string',
@@ -780,6 +838,7 @@ export const mockBrowserFields: BrowserFields = {
example: 'authentication',
name: 'event.category',
type: 'string',
+ esTypes: ['keyword'],
searchable: true,
aggregatable: true,
format: 'string',
@@ -792,6 +851,7 @@ export const mockBrowserFields: BrowserFields = {
example: 7,
name: 'event.severity',
type: 'number',
+ esTypes: ['long'],
format: 'number',
searchable: true,
aggregatable: true,
@@ -807,6 +867,7 @@ export const mockBrowserFields: BrowserFields = {
'Name of the host. It can contain what `hostname` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.',
name: 'host.name',
type: 'string',
+ esTypes: ['keyword'],
searchable: true,
aggregatable: true,
format: 'string',
@@ -826,6 +887,7 @@ export const mockBrowserFields: BrowserFields = {
name: 'source.ip',
searchable: true,
type: 'ip',
+ esTypes: ['ip'],
},
'source.port': {
aggregatable: true,
@@ -836,7 +898,8 @@ export const mockBrowserFields: BrowserFields = {
indexes: ['auditbeat', 'filebeat', 'packetbeat'],
name: 'source.port',
searchable: true,
- type: 'long',
+ type: 'number',
+ esTypes: ['long'],
},
},
},
@@ -848,6 +911,7 @@ export const mockBrowserFields: BrowserFields = {
example: 'albert',
name: 'user.name',
type: 'string',
+ esTypes: ['keyword'],
searchable: true,
aggregatable: true,
format: 'string',
diff --git a/x-pack/plugins/security_solution/public/common/mock/global_state.ts b/x-pack/plugins/security_solution/public/common/mock/global_state.ts
index 3c3cd748fecee..d252553586a8b 100644
--- a/x-pack/plugins/security_solution/public/common/mock/global_state.ts
+++ b/x-pack/plugins/security_solution/public/common/mock/global_state.ts
@@ -327,7 +327,14 @@ export const mockGlobalState: State = {
pinnedEventIds: {},
pinnedEventsSaveObject: {},
itemsPerPageOptions: [5, 10, 20],
- sort: [{ columnId: '@timestamp', columnType: 'number', sortDirection: Direction.desc }],
+ sort: [
+ {
+ columnId: '@timestamp',
+ columnType: 'date',
+ esTypes: ['date'],
+ sortDirection: Direction.desc,
+ },
+ ],
isSaving: false,
version: null,
status: TimelineStatus.active,
diff --git a/x-pack/plugins/security_solution/public/common/mock/header.ts b/x-pack/plugins/security_solution/public/common/mock/header.ts
index 66bfda1a02619..c9e56d1c6b032 100644
--- a/x-pack/plugins/security_solution/public/common/mock/header.ts
+++ b/x-pack/plugins/security_solution/public/common/mock/header.ts
@@ -21,6 +21,7 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
example: '2016-05-23T08:05:34.853Z',
id: '@timestamp',
type: 'date',
+ esTypes: ['date'],
aggregatable: true,
initialWidth: DEFAULT_DATE_COLUMN_MIN_WIDTH,
},
@@ -31,7 +32,8 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
"Severity describes the severity of the event. What the different severity values mean can very different between use cases. It's up to the implementer to make sure severities are consistent across events.",
example: '7',
id: 'event.severity',
- type: 'long',
+ type: 'number',
+ esTypes: ['long'],
aggregatable: true,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
@@ -42,7 +44,8 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
'Event category.\nThis contains high-level information about the contents of the event. It is more generic than `event.action`, in the sense that typically a category contains multiple actions. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.',
example: 'user-management',
id: 'event.category',
- type: 'keyword',
+ type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
@@ -53,7 +56,8 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
'The action captured by the event.\nThis describes the information in the event. It is more specific than `event.category`. Examples are `group-add`, `process-started`, `file-created`. The value is normally defined by the implementer.',
example: 'user-password-change',
id: 'event.action',
- type: 'keyword',
+ type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
@@ -64,7 +68,8 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
'Name of the host.\nIt can contain what `hostname` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.',
example: '',
id: 'host.name',
- type: 'keyword',
+ type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
@@ -75,6 +80,7 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
example: '',
id: 'source.ip',
type: 'ip',
+ esTypes: ['ip'],
aggregatable: true,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
@@ -85,6 +91,7 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
example: '',
id: 'destination.ip',
type: 'ip',
+ esTypes: ['ip'],
aggregatable: true,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
@@ -97,6 +104,7 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
format: 'bytes',
id: 'destination.bytes',
type: 'number',
+ esTypes: ['long'],
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
{
@@ -105,7 +113,8 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
description: 'Short name or login of the user.',
example: 'albert',
id: 'user.name',
- type: 'keyword',
+ type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
@@ -115,8 +124,9 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
description: 'Each document has an _id that uniquely identifies it',
example: 'Y-6TfmcB0WOhS6qyMv3s',
id: '_id',
- type: 'keyword',
- aggregatable: true,
+ type: 'string',
+ esTypes: [], // empty for _id
+ aggregatable: false,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
{
@@ -126,7 +136,8 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
'For log events the message field contains the log message.\nIn other use cases the message field can be used to concatenate different values which are then freely searchable. If multiple messages exist, they can be combined into one message.',
example: 'Hello World',
id: 'message',
- type: 'text',
+ type: 'string',
+ esTypes: ['text'],
aggregatable: false,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
diff --git a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts
index e450446e9e116..ec539d1f1fd0b 100644
--- a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts
+++ b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts
@@ -2017,7 +2017,8 @@ export const mockTimelineModel: TimelineModel = {
sort: [
{
columnId: '@timestamp',
- columnType: 'number',
+ columnType: 'date',
+ esTypes: ['date'],
sortDirection: Direction.desc,
},
],
@@ -2066,7 +2067,13 @@ export const mockTimelineResult = {
};
const defaultTimelineColumns: CreateTimelineProps['timeline']['columns'] = [
- { columnHeaderType: 'not-filtered', id: '@timestamp', type: 'number', initialWidth: 190 },
+ {
+ columnHeaderType: 'not-filtered',
+ id: '@timestamp',
+ type: 'date',
+ esTypes: ['date'],
+ initialWidth: 190,
+ },
{ columnHeaderType: 'not-filtered', id: 'message', initialWidth: 180 },
{ columnHeaderType: 'not-filtered', id: 'event.category', initialWidth: 180 },
{ columnHeaderType: 'not-filtered', id: 'event.action', initialWidth: 180 },
@@ -2136,7 +2143,14 @@ export const defaultTimelineProps: CreateTimelineProps = {
sessionViewConfig: null,
show: false,
showCheckboxes: false,
- sort: [{ columnId: '@timestamp', columnType: 'number', sortDirection: Direction.desc }],
+ sort: [
+ {
+ columnId: '@timestamp',
+ columnType: 'date',
+ esTypes: ['date'],
+ sortDirection: Direction.desc,
+ },
+ ],
status: TimelineStatus.draft,
title: '',
timelineType: TimelineType.default,
diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx
index be4668282cad3..d8bf2cff59aaf 100644
--- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx
@@ -207,7 +207,8 @@ describe('alert actions', () => {
{
columnHeaderType: 'not-filtered',
id: '@timestamp',
- type: 'number',
+ type: 'date',
+ esTypes: ['date'],
initialWidth: 190,
},
{
diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/default_config.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/default_config.tsx
index 8ae7e358f280d..34e189884c191 100644
--- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/default_config.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/default_config.tsx
@@ -166,7 +166,8 @@ export const alertsPreviewDefaultModel: SubsetTimelineModel = {
sort: [
{
columnId: 'kibana.alert.original_time',
- columnType: 'number',
+ columnType: 'date',
+ esTypes: ['date'],
sortDirection: 'desc',
},
],
diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts
index 6a7738de7b318..a0ab9a0e5fc42 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts
+++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts
@@ -77,7 +77,8 @@ const columns = [
{
columnHeaderType: 'not-filtered',
id: '@timestamp',
- type: 'number',
+ type: 'date',
+ esTypes: ['date'],
initialWidth: 190,
},
{
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/header_actions.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/header_actions.tsx
index 2c9a538a96076..c45059c909761 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/header_actions.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/header_actions.tsx
@@ -122,11 +122,18 @@ const HeaderActionsComponent: React.FC = ({
dispatch(
timelineActions.updateSort({
id: timelineId,
- sort: cols.map(({ id, direction }) => ({
- columnId: id,
- columnType: columnHeaders.find((ch) => ch.id === id)?.type ?? 'text',
- sortDirection: direction as SortDirection,
- })),
+ sort: cols.map(({ id, direction }) => {
+ const columnHeader = columnHeaders.find((ch) => ch.id === id);
+ const columnType = columnHeader?.type ?? '';
+ const esTypes = columnHeader?.esTypes ?? [];
+
+ return {
+ columnId: id,
+ columnType,
+ esTypes,
+ sortDirection: direction as SortDirection,
+ };
+ }),
})
),
[columnHeaders, dispatch, timelineId]
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/__snapshots__/index.test.tsx.snap
index affd5c894075a..e16d45bf1f3a9 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/__snapshots__/index.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/__snapshots__/index.test.tsx.snap
@@ -11,6 +11,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "agent",
"description": "Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but \`agent.id\` does not.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "8a4f500f",
"format": "",
"indexes": Array [
@@ -26,6 +29,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "agent",
"description": null,
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -41,6 +47,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "agent",
"description": "Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "8a4f500d",
"format": "",
"indexes": Array [
@@ -56,6 +65,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "agent",
"description": "Name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "foo",
"format": "",
"indexes": Array [
@@ -75,6 +87,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "auditd",
"description": null,
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -88,6 +103,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "auditd",
"description": null,
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -101,6 +119,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "auditd",
"description": null,
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -118,6 +139,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "base",
"description": "Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.",
+ "esTypes": Array [
+ "date",
+ ],
"example": "2016-05-23T08:05:34.853Z",
"format": "",
"indexes": Array [
@@ -134,6 +158,7 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": false,
"category": "base",
"description": "Each document has an _id that uniquely identifies it",
+ "esTypes": Array [],
"example": "Y-6TfmcB0WOhS6qyMv3s",
"indexes": Array [
"auditbeat",
@@ -148,6 +173,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": false,
"category": "base",
"description": "For log events the message field contains the log message, optimized for viewing in a log viewer. For structured logs without an original message field, other fields can be concatenated to form a human-readable summary of the event. If multiple messages exist, they can be combined into one message.",
+ "esTypes": Array [
+ "text",
+ ],
"example": "Hello World",
"format": "string",
"indexes": Array [
@@ -167,6 +195,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "client",
"description": "Some event client addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the \`.address\` field. Then it should be duplicated to \`.ip\` or \`.domain\`, depending on which one it is.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -182,6 +213,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "client",
"description": "Bytes sent from the client to the server.",
+ "esTypes": Array [
+ "long",
+ ],
"example": "184",
"format": "",
"indexes": Array [
@@ -197,6 +231,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "client",
"description": "Client domain.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -212,6 +249,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "client",
"description": "Country ISO code.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "CA",
"format": "",
"indexes": Array [
@@ -231,6 +271,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "cloud",
"description": "The cloud account or organization id used to identify different entities in a multi-tenant environment. Examples: AWS account id, Google Cloud ORG Id, or other unique identifier.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "666777888999",
"format": "",
"indexes": Array [
@@ -246,6 +289,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "cloud",
"description": "Availability zone in which this host is running.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "us-east-1c",
"format": "",
"indexes": Array [
@@ -265,6 +311,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "container",
"description": "Unique container id.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -280,6 +329,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "container",
"description": "Name of the image the container was built on.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -295,6 +347,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "container",
"description": "Container image tag.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -314,6 +369,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "destination",
"description": "Some event destination addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the \`.address\` field. Then it should be duplicated to \`.ip\` or \`.domain\`, depending on which one it is.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -329,6 +387,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "destination",
"description": "Bytes sent from the destination to the source.",
+ "esTypes": Array [
+ "long",
+ ],
"example": "184",
"format": "",
"indexes": Array [
@@ -344,6 +405,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "destination",
"description": "Destination domain.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -359,6 +423,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "destination",
"description": "IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.",
+ "esTypes": Array [
+ "ip",
+ ],
"example": "",
"format": "",
"indexes": Array [
@@ -374,6 +441,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "destination",
"description": "Port of the destination.",
+ "esTypes": Array [
+ "long",
+ ],
"example": "",
"format": "",
"indexes": Array [
@@ -383,7 +453,7 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
],
"name": "destination.port",
"searchable": true,
- "type": "long",
+ "type": "number",
},
},
},
@@ -393,6 +463,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "event",
"description": "The action captured by the event. This describes the information in the event. It is more specific than \`event.category\`. Examples are \`group-add\`, \`process-started\`, \`file-created\`. The value is normally defined by the implementer.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "user-password-change",
"format": "string",
"indexes": Array [
@@ -414,6 +487,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "event",
"description": "This is one of four ECS Categorization Fields, and indicates the second level in the ECS category hierarchy. \`event.category\` represents the \\"big buckets\\" of ECS categories. For example, filtering on \`event.category:process\` yields all events relating to process activity. This field is closely related to \`event.type\`, which is used as a subcategory. This field is an array. This will allow proper categorization of some events that fall in multiple categories.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "authentication",
"format": "string",
"indexes": Array [
@@ -435,6 +511,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "event",
"description": "event.end contains the date when the event ended or when the activity was last observed.",
+ "esTypes": Array [
+ "date",
+ ],
"example": null,
"format": "",
"indexes": Array [
@@ -456,6 +535,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "event",
"description": "The numeric severity of the event according to your event source. What the different severity values mean can be different between sources and use cases. It's up to the implementer to make sure severities are consistent across events from the same source. The Syslog severity belongs in \`log.syslog.severity.code\`. \`event.severity\` is meant to represent the severity according to the event source (e.g. firewall, IDS). If the event source does not publish its own severity, you may optionally copy the \`log.syslog.severity.code\` to \`event.severity\`.",
+ "esTypes": Array [
+ "long",
+ ],
"example": 7,
"format": "number",
"indexes": Array [
@@ -481,6 +563,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "host",
"description": "Name of the host. It can contain what \`hostname\` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.",
+ "esTypes": Array [
+ "keyword",
+ ],
"format": "string",
"indexes": Array [
"apm-*-transaction*",
@@ -549,6 +634,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "source",
"description": "IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.",
+ "esTypes": Array [
+ "ip",
+ ],
"example": "",
"format": "",
"indexes": Array [
@@ -564,6 +652,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "source",
"description": "Port of the source.",
+ "esTypes": Array [
+ "long",
+ ],
"example": "",
"format": "",
"indexes": Array [
@@ -573,7 +664,7 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
],
"name": "source.port",
"searchable": true,
- "type": "long",
+ "type": "number",
},
},
},
@@ -583,6 +674,9 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
"aggregatable": true,
"category": "user",
"description": "Short name or login of the user.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "albert",
"format": "string",
"indexes": Array [
@@ -602,9 +696,12 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
Array [
Object {
"columnHeaderType": "not-filtered",
+ "esTypes": Array [
+ "date",
+ ],
"id": "@timestamp",
"initialWidth": 190,
- "type": "number",
+ "type": "date",
},
Object {
"columnHeaderType": "not-filtered",
@@ -670,7 +767,10 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
Array [
Object {
"columnId": "@timestamp",
- "columnType": "number",
+ "columnType": "date",
+ "esTypes": Array [
+ "date",
+ ],
"sortDirection": "desc",
},
]
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 67fcff7de1332..82ca99d077bf5 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
@@ -117,6 +117,8 @@ const ColumnHeaderComponent: React.FC = ({
const onColumnSort = useCallback(
(sortDirection: Direction) => {
const columnId = header.id;
+ const columnType = header.type ?? '';
+ const esTypes = header.esTypes ?? [];
const headerIndex = sort.findIndex((col) => col.columnId === columnId);
const newSort =
headerIndex === -1
@@ -124,7 +126,8 @@ const ColumnHeaderComponent: React.FC = ({
...sort,
{
columnId,
- columnType: `${header.type}`,
+ columnType,
+ esTypes,
sortDirection,
},
]
@@ -132,7 +135,8 @@ const ColumnHeaderComponent: React.FC = ({
...sort.slice(0, headerIndex),
{
columnId,
- columnType: `${header.type}`,
+ columnType,
+ esTypes,
sortDirection,
},
...sort.slice(headerIndex + 1),
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/default_headers.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/default_headers.ts
index 58e8d1869233f..7bad6fc73afb9 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/default_headers.ts
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/default_headers.ts
@@ -15,8 +15,9 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
{
columnHeaderType: defaultColumnHeaderType,
id: '@timestamp',
- type: 'number',
initialWidth: DEFAULT_DATE_COLUMN_MIN_WIDTH,
+ esTypes: ['date'],
+ type: 'date',
},
{
columnHeaderType: defaultColumnHeaderType,
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/__snapshots__/index.test.tsx.snap
index 50da19c3d48f3..0a621f0218337 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/__snapshots__/index.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/__snapshots__/index.test.tsx.snap
@@ -6,9 +6,12 @@ exports[`Header renders correctly against snapshot 1`] = `
header={
Object {
"columnHeaderType": "not-filtered",
+ "esTypes": Array [
+ "date",
+ ],
"id": "@timestamp",
"initialWidth": 190,
- "type": "number",
+ "type": "date",
}
}
isLoading={false}
@@ -19,7 +22,10 @@ exports[`Header renders correctly against snapshot 1`] = `
Array [
Object {
"columnId": "@timestamp",
- "columnType": "number",
+ "columnType": "date",
+ "esTypes": Array [
+ "date",
+ ],
"sortDirection": "desc",
},
]
@@ -29,9 +35,12 @@ exports[`Header renders correctly against snapshot 1`] = `
header={
Object {
"columnHeaderType": "not-filtered",
+ "esTypes": Array [
+ "date",
+ ],
"id": "@timestamp",
"initialWidth": 190,
- "type": "number",
+ "type": "date",
}
}
isLoading={false}
@@ -40,7 +49,10 @@ exports[`Header renders correctly against snapshot 1`] = `
Array [
Object {
"columnId": "@timestamp",
- "columnType": "number",
+ "columnType": "date",
+ "esTypes": Array [
+ "date",
+ ],
"sortDirection": "desc",
},
]
@@ -51,9 +63,12 @@ exports[`Header renders correctly against snapshot 1`] = `
header={
Object {
"columnHeaderType": "not-filtered",
+ "esTypes": Array [
+ "date",
+ ],
"id": "@timestamp",
"initialWidth": 190,
- "type": "number",
+ "type": "date",
}
}
onFilterChange={[Function]}
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.test.tsx
index 4fa72fa5da424..b0c21ac3698df 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.test.tsx
@@ -43,7 +43,8 @@ describe('Header', () => {
const sort: Sort[] = [
{
columnId: columnHeader.id,
- columnType: columnHeader.type ?? 'number',
+ columnType: columnHeader.type ?? '',
+ esTypes: columnHeader.esTypes ?? [],
sortDirection: Direction.desc,
},
];
@@ -183,6 +184,7 @@ describe('Header', () => {
{
columnId: columnHeader.id,
columnType: columnHeader.type ?? 'number',
+ esTypes: columnHeader.esTypes ?? [],
sortDirection: Direction.asc, // (because the previous state was Direction.desc)
},
],
@@ -251,6 +253,7 @@ describe('Header', () => {
{
columnId: 'differentSocks',
columnType: columnHeader.type ?? 'number',
+ esTypes: columnHeader.esTypes ?? [],
sortDirection: Direction.desc,
},
];
@@ -264,6 +267,7 @@ describe('Header', () => {
const sortDescending: Sort = {
columnId: columnHeader.id,
columnType: columnHeader.type ?? 'number',
+ esTypes: columnHeader.esTypes ?? [],
sortDirection: Direction.desc,
};
@@ -274,6 +278,7 @@ describe('Header', () => {
const sortAscending: Sort = {
columnId: columnHeader.id,
columnType: columnHeader.type ?? 'number',
+ esTypes: columnHeader.esTypes ?? [],
sortDirection: Direction.asc,
};
@@ -284,6 +289,7 @@ describe('Header', () => {
const sortNone: Sort = {
columnId: columnHeader.id,
columnType: columnHeader.type ?? 'number',
+ esTypes: columnHeader.esTypes ?? [],
sortDirection: 'none',
};
@@ -297,6 +303,7 @@ describe('Header', () => {
{
columnId: columnHeader.id,
columnType: columnHeader.type ?? 'number',
+ esTypes: columnHeader.esTypes ?? [],
sortDirection: Direction.desc,
},
];
@@ -314,6 +321,7 @@ describe('Header', () => {
{
columnId: 'someOtherColumn',
columnType: columnHeader.type ?? 'number',
+ esTypes: columnHeader.esTypes ?? [],
sortDirection: 'none',
},
];
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.tsx
index 166a4c2da871c..44e461224a180 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.tsx
@@ -43,7 +43,8 @@ export const HeaderComponent: React.FC = ({
const onColumnSort = useCallback(() => {
const columnId = header.id;
- const columnType = header.type ?? 'text';
+ const columnType = header.type ?? '';
+ const esTypes = header.esTypes ?? [];
const sortDirection = getNewSortDirectionOnClick({
clickedHeader: header,
currentSort: sort,
@@ -56,6 +57,7 @@ export const HeaderComponent: React.FC = ({
{
columnId,
columnType,
+ esTypes,
sortDirection,
},
];
@@ -65,6 +67,7 @@ export const HeaderComponent: React.FC = ({
{
columnId,
columnType,
+ esTypes,
sortDirection,
},
...sort.slice(headerIndex + 1),
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/__snapshots__/index.test.tsx.snap
index 945a9a7aee698..750f3956786a5 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/__snapshots__/index.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/__snapshots__/index.test.tsx.snap
@@ -40,11 +40,12 @@ exports[`HeaderToolTipContent it renders the expected table content 1`] = `
data-test-subj="type-icon"
type="clock"
/>
-
date
-
+
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.test.tsx
index 8f64b4e7e6db3..532937c3e8b99 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.test.tsx
@@ -42,7 +42,24 @@ describe('HeaderToolTipContent', () => {
test('it renders the type of the field', () => {
const wrapper = mount();
- expect(wrapper.find('[data-test-subj="type-value"]').first().text()).toEqual(header.type);
+ expect(
+ wrapper
+ .find(`[data-test-subj="type-value-${header.esTypes?.at(0)}"]`)
+ .first()
+ .text()
+ ).toEqual(header.esTypes?.at(0));
+ });
+
+ test('it renders multiple `esTypes`', () => {
+ const hasMultipleTypes = { ...header, esTypes: ['long', 'date'] };
+
+ const wrapper = mount();
+
+ hasMultipleTypes.esTypes.forEach((esType) => {
+ expect(wrapper.find(`[data-test-subj="type-value-${esType}"]`).first().text()).toEqual(
+ esType
+ );
+ });
});
test('it renders the description of the field', () => {
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.tsx
index 4be37de54b365..8cadcad9ef79d 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { EuiIcon } from '@elastic/eui';
+import { EuiIcon, EuiBadge } from '@elastic/eui';
import { isEmpty } from 'lodash/fp';
import React from 'react';
import styled from 'styled-components';
@@ -29,6 +29,7 @@ P.displayName = 'P';
const ToolTipTableMetadata = styled.span`
margin-right: 5px;
display: block;
+ font-weight: bold;
`;
ToolTipTableMetadata.displayName = 'ToolTipTableMetadata';
@@ -62,7 +63,11 @@ export const HeaderToolTipContent = React.memo<{ header: ColumnHeaderOptions }>(
- {header.type}
+ {header.esTypes?.map((esType) => (
+
+ {esType}
+
+ ))}
{!isEmpty(header.description) && (
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/helpers.test.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/helpers.test.ts
index 74cd56bd4e389..84cc6e60d928c 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/helpers.test.ts
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/helpers.test.ts
@@ -40,6 +40,7 @@ describe('helpers', () => {
readFromDocValues: true,
searchable: true,
type: 'date',
+ esTypes: ['date'],
initialWidth: 190,
},
{
@@ -54,6 +55,7 @@ describe('helpers', () => {
name: 'source.ip',
searchable: true,
type: 'ip',
+ esTypes: ['ip'],
initialWidth: 180,
},
{
@@ -69,6 +71,7 @@ describe('helpers', () => {
name: 'destination.ip',
searchable: true,
type: 'ip',
+ esTypes: ['ip'],
initialWidth: 180,
},
];
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.test.tsx
index e1a193e2a414b..5c895f3829bff 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.test.tsx
@@ -55,7 +55,8 @@ describe('ColumnHeaders', () => {
const sort: Sort[] = [
{
columnId: '@timestamp',
- columnType: 'number',
+ columnType: 'date',
+ esTypes: ['date'],
sortDirection: Direction.desc,
},
];
@@ -112,12 +113,14 @@ describe('ColumnHeaders', () => {
let mockSort: Sort[] = [
{
columnId: '@timestamp',
- columnType: 'number',
+ columnType: 'date',
+ esTypes: ['date'],
sortDirection: Direction.desc,
},
{
columnId: 'host.name',
- columnType: 'text',
+ columnType: 'string',
+ esTypes: [],
sortDirection: Direction.asc,
},
];
@@ -132,12 +135,14 @@ describe('ColumnHeaders', () => {
mockSort = [
{
columnId: '@timestamp',
- columnType: 'number',
+ columnType: 'date',
+ esTypes: ['date'],
sortDirection: Direction.desc,
},
{
columnId: 'host.name',
- columnType: 'text',
+ columnType: 'string',
+ esTypes: [],
sortDirection: Direction.asc,
},
];
@@ -156,21 +161,29 @@ describe('ColumnHeaders', () => {
.find('[data-test-subj="header-event.category"] [data-test-subj="header-sort-button"]')
.first()
.simulate('click');
+
expect(mockDispatch).toHaveBeenCalledWith(
timelineActions.updateSort({
id: timelineId,
sort: [
{
columnId: '@timestamp',
- columnType: 'number',
+ columnType: 'date',
+ esTypes: ['date'],
sortDirection: Direction.desc,
},
{
columnId: 'host.name',
- columnType: 'text',
+ columnType: 'string',
+ esTypes: [],
sortDirection: Direction.asc,
},
- { columnId: 'event.category', columnType: 'text', sortDirection: Direction.desc },
+ {
+ columnId: 'event.category',
+ columnType: '',
+ esTypes: [],
+ sortDirection: Direction.desc,
+ },
],
})
);
@@ -195,10 +208,16 @@ describe('ColumnHeaders', () => {
sort: [
{
columnId: '@timestamp',
- columnType: 'number',
+ columnType: 'date',
+ esTypes: ['date'],
+ sortDirection: Direction.asc,
+ },
+ {
+ columnId: 'host.name',
+ columnType: 'string',
+ esTypes: [],
sortDirection: Direction.asc,
},
- { columnId: 'host.name', columnType: 'text', sortDirection: Direction.asc },
],
})
);
@@ -221,16 +240,23 @@ describe('ColumnHeaders', () => {
.find('[data-test-subj="header-host.name"] [data-test-subj="header-sort-button"]')
.first()
.simulate('click');
+
expect(mockDispatch).toHaveBeenCalledWith(
timelineActions.updateSort({
id: timelineId,
sort: [
{
columnId: '@timestamp',
- columnType: 'number',
+ columnType: 'date',
+ esTypes: ['date'],
+ sortDirection: Direction.desc,
+ },
+ {
+ columnId: 'host.name',
+ columnType: '',
+ esTypes: [],
sortDirection: Direction.desc,
},
- { columnId: 'host.name', columnType: 'text', sortDirection: Direction.desc },
],
})
);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx
index 01953cc1272d6..35ea0dd517e95 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx
@@ -80,7 +80,8 @@ jest.mock('../../../../common/lib/kibana', () => {
const mockSort: Sort[] = [
{
columnId: '@timestamp',
- columnType: 'number',
+ columnType: 'date',
+ esTypes: ['date'],
sortDirection: Direction.desc,
},
];
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/__snapshots__/plain_column_renderer.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/__snapshots__/plain_column_renderer.test.tsx.snap
index 6a09567fbf41a..ef61e08a8a883 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/__snapshots__/plain_column_renderer.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/__snapshots__/plain_column_renderer.test.tsx.snap
@@ -7,7 +7,7 @@ exports[`plain_column_renderer rendering renders correctly against snapshot 1`]
eventId="1"
fieldFormat=""
fieldName="event.category"
- fieldType="keyword"
+ fieldType="string"
isAggregatable={true}
isDraggable={true}
key="plain-column-renderer-formatted-field-value-test-event.category-1-event.category-Access-0"
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/__snapshots__/index.test.tsx.snap
index 7c0f0a1ac7f8e..d5135b55e6ebd 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/__snapshots__/index.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/__snapshots__/index.test.tsx.snap
@@ -12,6 +12,9 @@ exports[`Timeline rendering renders correctly against snapshot 1`] = `
"description": "Date/time when the event originated.
For log events this is the date/time when the event was generated, and not when it was read.
Required field for all events.",
+ "esTypes": Array [
+ "date",
+ ],
"example": "2016-05-23T08:05:34.853Z",
"id": "@timestamp",
"initialWidth": 190,
@@ -22,10 +25,13 @@ Required field for all events.",
"category": "event",
"columnHeaderType": "not-filtered",
"description": "Severity describes the severity of the event. What the different severity values mean can very different between use cases. It's up to the implementer to make sure severities are consistent across events.",
+ "esTypes": Array [
+ "long",
+ ],
"example": "7",
"id": "event.severity",
"initialWidth": 180,
- "type": "long",
+ "type": "number",
},
Object {
"aggregatable": true,
@@ -33,10 +39,13 @@ Required field for all events.",
"columnHeaderType": "not-filtered",
"description": "Event category.
This contains high-level information about the contents of the event. It is more generic than \`event.action\`, in the sense that typically a category contains multiple actions. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "user-management",
"id": "event.category",
"initialWidth": 180,
- "type": "keyword",
+ "type": "string",
},
Object {
"aggregatable": true,
@@ -44,10 +53,13 @@ This contains high-level information about the contents of the event. It is more
"columnHeaderType": "not-filtered",
"description": "The action captured by the event.
This describes the information in the event. It is more specific than \`event.category\`. Examples are \`group-add\`, \`process-started\`, \`file-created\`. The value is normally defined by the implementer.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "user-password-change",
"id": "event.action",
"initialWidth": 180,
- "type": "keyword",
+ "type": "string",
},
Object {
"aggregatable": true,
@@ -55,10 +67,13 @@ This describes the information in the event. It is more specific than \`event.ca
"columnHeaderType": "not-filtered",
"description": "Name of the host.
It can contain what \`hostname\` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "",
"id": "host.name",
"initialWidth": 180,
- "type": "keyword",
+ "type": "string",
},
Object {
"aggregatable": true,
@@ -66,6 +81,9 @@ It can contain what \`hostname\` returns on Unix systems, the fully qualified do
"columnHeaderType": "not-filtered",
"description": "IP address of the source.
Can be one or multiple IPv4 or IPv6 addresses.",
+ "esTypes": Array [
+ "ip",
+ ],
"example": "",
"id": "source.ip",
"initialWidth": 180,
@@ -77,6 +95,9 @@ Can be one or multiple IPv4 or IPv6 addresses.",
"columnHeaderType": "not-filtered",
"description": "IP address of the destination.
Can be one or multiple IPv4 or IPv6 addresses.",
+ "esTypes": Array [
+ "ip",
+ ],
"example": "",
"id": "destination.ip",
"initialWidth": 180,
@@ -87,6 +108,9 @@ Can be one or multiple IPv4 or IPv6 addresses.",
"category": "destination",
"columnHeaderType": "not-filtered",
"description": "Bytes sent from the source to the destination",
+ "esTypes": Array [
+ "long",
+ ],
"example": "123",
"format": "bytes",
"id": "destination.bytes",
@@ -98,20 +122,24 @@ Can be one or multiple IPv4 or IPv6 addresses.",
"category": "user",
"columnHeaderType": "not-filtered",
"description": "Short name or login of the user.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "albert",
"id": "user.name",
"initialWidth": 180,
- "type": "keyword",
+ "type": "string",
},
Object {
- "aggregatable": true,
+ "aggregatable": false,
"category": "base",
"columnHeaderType": "not-filtered",
"description": "Each document has an _id that uniquely identifies it",
+ "esTypes": Array [],
"example": "Y-6TfmcB0WOhS6qyMv3s",
"id": "_id",
"initialWidth": 180,
- "type": "keyword",
+ "type": "string",
},
Object {
"aggregatable": false,
@@ -119,10 +147,13 @@ Can be one or multiple IPv4 or IPv6 addresses.",
"columnHeaderType": "not-filtered",
"description": "For log events the message field contains the log message.
In other use cases the message field can be used to concatenate different values which are then freely searchable. If multiple messages exist, they can be combined into one message.",
+ "esTypes": Array [
+ "text",
+ ],
"example": "Hello World",
"id": "message",
"initialWidth": 180,
- "type": "text",
+ "type": "string",
},
]
}
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/__snapshots__/index.test.tsx.snap
index 2ccf562c9ca6f..85d1956920f50 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/__snapshots__/index.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/__snapshots__/index.test.tsx.snap
@@ -11,6 +11,9 @@ exports[`PinnedTabContent rendering renders correctly against snapshot 1`] = `
"description": "Date/time when the event originated.
For log events this is the date/time when the event was generated, and not when it was read.
Required field for all events.",
+ "esTypes": Array [
+ "date",
+ ],
"example": "2016-05-23T08:05:34.853Z",
"id": "@timestamp",
"initialWidth": 190,
@@ -21,10 +24,13 @@ Required field for all events.",
"category": "event",
"columnHeaderType": "not-filtered",
"description": "Severity describes the severity of the event. What the different severity values mean can very different between use cases. It's up to the implementer to make sure severities are consistent across events.",
+ "esTypes": Array [
+ "long",
+ ],
"example": "7",
"id": "event.severity",
"initialWidth": 180,
- "type": "long",
+ "type": "number",
},
Object {
"aggregatable": true,
@@ -32,10 +38,13 @@ Required field for all events.",
"columnHeaderType": "not-filtered",
"description": "Event category.
This contains high-level information about the contents of the event. It is more generic than \`event.action\`, in the sense that typically a category contains multiple actions. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "user-management",
"id": "event.category",
"initialWidth": 180,
- "type": "keyword",
+ "type": "string",
},
Object {
"aggregatable": true,
@@ -43,10 +52,13 @@ This contains high-level information about the contents of the event. It is more
"columnHeaderType": "not-filtered",
"description": "The action captured by the event.
This describes the information in the event. It is more specific than \`event.category\`. Examples are \`group-add\`, \`process-started\`, \`file-created\`. The value is normally defined by the implementer.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "user-password-change",
"id": "event.action",
"initialWidth": 180,
- "type": "keyword",
+ "type": "string",
},
Object {
"aggregatable": true,
@@ -54,10 +66,13 @@ This describes the information in the event. It is more specific than \`event.ca
"columnHeaderType": "not-filtered",
"description": "Name of the host.
It can contain what \`hostname\` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "",
"id": "host.name",
"initialWidth": 180,
- "type": "keyword",
+ "type": "string",
},
Object {
"aggregatable": true,
@@ -65,6 +80,9 @@ It can contain what \`hostname\` returns on Unix systems, the fully qualified do
"columnHeaderType": "not-filtered",
"description": "IP address of the source.
Can be one or multiple IPv4 or IPv6 addresses.",
+ "esTypes": Array [
+ "ip",
+ ],
"example": "",
"id": "source.ip",
"initialWidth": 180,
@@ -76,6 +94,9 @@ Can be one or multiple IPv4 or IPv6 addresses.",
"columnHeaderType": "not-filtered",
"description": "IP address of the destination.
Can be one or multiple IPv4 or IPv6 addresses.",
+ "esTypes": Array [
+ "ip",
+ ],
"example": "",
"id": "destination.ip",
"initialWidth": 180,
@@ -86,6 +107,9 @@ Can be one or multiple IPv4 or IPv6 addresses.",
"category": "destination",
"columnHeaderType": "not-filtered",
"description": "Bytes sent from the source to the destination",
+ "esTypes": Array [
+ "long",
+ ],
"example": "123",
"format": "bytes",
"id": "destination.bytes",
@@ -97,20 +121,24 @@ Can be one or multiple IPv4 or IPv6 addresses.",
"category": "user",
"columnHeaderType": "not-filtered",
"description": "Short name or login of the user.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "albert",
"id": "user.name",
"initialWidth": 180,
- "type": "keyword",
+ "type": "string",
},
Object {
- "aggregatable": true,
+ "aggregatable": false,
"category": "base",
"columnHeaderType": "not-filtered",
"description": "Each document has an _id that uniquely identifies it",
+ "esTypes": Array [],
"example": "Y-6TfmcB0WOhS6qyMv3s",
"id": "_id",
"initialWidth": 180,
- "type": "keyword",
+ "type": "string",
},
Object {
"aggregatable": false,
@@ -118,10 +146,13 @@ Can be one or multiple IPv4 or IPv6 addresses.",
"columnHeaderType": "not-filtered",
"description": "For log events the message field contains the log message.
In other use cases the message field can be used to concatenate different values which are then freely searchable. If multiple messages exist, they can be combined into one message.",
+ "esTypes": Array [
+ "text",
+ ],
"example": "Hello World",
"id": "message",
"initialWidth": 180,
- "type": "text",
+ "type": "string",
},
]
}
@@ -1125,7 +1156,10 @@ In other use cases the message field can be used to concatenate different values
Array [
Object {
"columnId": "@timestamp",
- "columnType": "number",
+ "columnType": "date",
+ "esTypes": Array [
+ "date",
+ ],
"sortDirection": "desc",
},
]
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.test.tsx
index 8a7fd244c6f20..3e5fda4264e5f 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.test.tsx
@@ -82,7 +82,8 @@ describe('PinnedTabContent', () => {
const sort: Sort[] = [
{
columnId: '@timestamp',
- columnType: 'number',
+ columnType: 'date',
+ esTypes: ['date'],
sortDirection: Direction.desc,
},
];
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx
index 9259039ae2535..eacb6ca4282ab 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx
@@ -174,10 +174,11 @@ export const PinnedTabContentComponent: React.FC = ({
const timelineQuerySortField = useMemo(
() =>
- sort.map(({ columnId, columnType, sortDirection }) => ({
+ sort.map(({ columnId, columnType, esTypes, sortDirection }) => ({
field: columnId,
type: columnType,
direction: sortDirection as Direction,
+ esTypes: esTypes ?? [],
})),
[sort]
);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/__snapshots__/index.test.tsx.snap
index cc6feb69561df..5b0757ee775f7 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/__snapshots__/index.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/__snapshots__/index.test.tsx.snap
@@ -12,6 +12,9 @@ exports[`Timeline rendering renders correctly against snapshot 1`] = `
"description": "Date/time when the event originated.
For log events this is the date/time when the event was generated, and not when it was read.
Required field for all events.",
+ "esTypes": Array [
+ "date",
+ ],
"example": "2016-05-23T08:05:34.853Z",
"id": "@timestamp",
"initialWidth": 190,
@@ -22,10 +25,13 @@ Required field for all events.",
"category": "event",
"columnHeaderType": "not-filtered",
"description": "Severity describes the severity of the event. What the different severity values mean can very different between use cases. It's up to the implementer to make sure severities are consistent across events.",
+ "esTypes": Array [
+ "long",
+ ],
"example": "7",
"id": "event.severity",
"initialWidth": 180,
- "type": "long",
+ "type": "number",
},
Object {
"aggregatable": true,
@@ -33,10 +39,13 @@ Required field for all events.",
"columnHeaderType": "not-filtered",
"description": "Event category.
This contains high-level information about the contents of the event. It is more generic than \`event.action\`, in the sense that typically a category contains multiple actions. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "user-management",
"id": "event.category",
"initialWidth": 180,
- "type": "keyword",
+ "type": "string",
},
Object {
"aggregatable": true,
@@ -44,10 +53,13 @@ This contains high-level information about the contents of the event. It is more
"columnHeaderType": "not-filtered",
"description": "The action captured by the event.
This describes the information in the event. It is more specific than \`event.category\`. Examples are \`group-add\`, \`process-started\`, \`file-created\`. The value is normally defined by the implementer.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "user-password-change",
"id": "event.action",
"initialWidth": 180,
- "type": "keyword",
+ "type": "string",
},
Object {
"aggregatable": true,
@@ -55,10 +67,13 @@ This describes the information in the event. It is more specific than \`event.ca
"columnHeaderType": "not-filtered",
"description": "Name of the host.
It can contain what \`hostname\` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "",
"id": "host.name",
"initialWidth": 180,
- "type": "keyword",
+ "type": "string",
},
Object {
"aggregatable": true,
@@ -66,6 +81,9 @@ It can contain what \`hostname\` returns on Unix systems, the fully qualified do
"columnHeaderType": "not-filtered",
"description": "IP address of the source.
Can be one or multiple IPv4 or IPv6 addresses.",
+ "esTypes": Array [
+ "ip",
+ ],
"example": "",
"id": "source.ip",
"initialWidth": 180,
@@ -77,6 +95,9 @@ Can be one or multiple IPv4 or IPv6 addresses.",
"columnHeaderType": "not-filtered",
"description": "IP address of the destination.
Can be one or multiple IPv4 or IPv6 addresses.",
+ "esTypes": Array [
+ "ip",
+ ],
"example": "",
"id": "destination.ip",
"initialWidth": 180,
@@ -87,6 +108,9 @@ Can be one or multiple IPv4 or IPv6 addresses.",
"category": "destination",
"columnHeaderType": "not-filtered",
"description": "Bytes sent from the source to the destination",
+ "esTypes": Array [
+ "long",
+ ],
"example": "123",
"format": "bytes",
"id": "destination.bytes",
@@ -98,20 +122,24 @@ Can be one or multiple IPv4 or IPv6 addresses.",
"category": "user",
"columnHeaderType": "not-filtered",
"description": "Short name or login of the user.",
+ "esTypes": Array [
+ "keyword",
+ ],
"example": "albert",
"id": "user.name",
"initialWidth": 180,
- "type": "keyword",
+ "type": "string",
},
Object {
- "aggregatable": true,
+ "aggregatable": false,
"category": "base",
"columnHeaderType": "not-filtered",
"description": "Each document has an _id that uniquely identifies it",
+ "esTypes": Array [],
"example": "Y-6TfmcB0WOhS6qyMv3s",
"id": "_id",
"initialWidth": 180,
- "type": "keyword",
+ "type": "string",
},
Object {
"aggregatable": false,
@@ -119,10 +147,13 @@ Can be one or multiple IPv4 or IPv6 addresses.",
"columnHeaderType": "not-filtered",
"description": "For log events the message field contains the log message.
In other use cases the message field can be used to concatenate different values which are then freely searchable. If multiple messages exist, they can be combined into one message.",
+ "esTypes": Array [
+ "text",
+ ],
"example": "Hello World",
"id": "message",
"initialWidth": 180,
- "type": "text",
+ "type": "string",
},
]
}
@@ -1267,7 +1298,10 @@ In other use cases the message field can be used to concatenate different values
Array [
Object {
"columnId": "@timestamp",
- "columnType": "number",
+ "columnType": "date",
+ "esTypes": Array [
+ "date",
+ ],
"sortDirection": "desc",
},
]
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx
index 300d793460c09..90ebe5cde9566 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx
@@ -95,7 +95,8 @@ describe('Timeline', () => {
const sort: Sort[] = [
{
columnId: '@timestamp',
- columnType: 'number',
+ columnType: 'date',
+ esTypes: ['date'],
sortDirection: Direction.desc,
},
];
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx
index b206260850c4a..25b7f4a8b636a 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx
@@ -266,9 +266,10 @@ export const QueryTabContentComponent: React.FC = ({
return [...columnFields, ...requiredFieldsForActions];
};
- const timelineQuerySortField = sort.map(({ columnId, columnType, sortDirection }) => ({
+ const timelineQuerySortField = sort.map(({ columnId, columnType, esTypes, sortDirection }) => ({
field: columnId,
direction: sortDirection as Direction,
+ esTypes: esTypes ?? [],
type: columnType,
}));
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 fb76d8f2994c6..362e20d140843 100644
--- a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx
@@ -95,11 +95,12 @@ const getTimelineEvents = (timelineEdges: TimelineEdges[]): TimelineItem[] =>
timelineEdges.map((e: TimelineEdges) => e.node);
const ID = 'timelineEventsQuery';
-export const initSortDefault = [
+export const initSortDefault: TimelineRequestSortField[] = [
{
field: '@timestamp',
direction: Direction.asc,
- type: 'number',
+ type: 'date',
+ esTypes: ['date'],
},
];
diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts
index c1d37d21c2b1d..1213953c431fb 100644
--- a/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts
+++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts
@@ -71,7 +71,8 @@ export const timelineDefaults: SubsetTimelineModel &
sort: [
{
columnId: '@timestamp',
- columnType: 'number',
+ columnType: 'date',
+ esTypes: ['date'],
sortDirection: 'desc',
},
],
diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts
index f79ecdb33f185..d3f204d58d884 100644
--- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts
+++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts
@@ -162,7 +162,14 @@ describe('Epic Timeline', () => {
sessionViewConfig: null,
show: true,
showCheckboxes: false,
- sort: [{ columnId: '@timestamp', columnType: 'number', sortDirection: Direction.desc }],
+ sort: [
+ {
+ columnId: '@timestamp',
+ columnType: 'date',
+ esTypes: ['date'],
+ sortDirection: Direction.desc,
+ },
+ ],
status: TimelineStatus.active,
version: 'WzM4LDFd',
id: '11169110-fc22-11e9-8ca9-072f15ce2685',
@@ -304,7 +311,8 @@ describe('Epic Timeline', () => {
sort: [
{
columnId: '@timestamp',
- columnType: 'number',
+ columnType: 'date',
+ esTypes: ['date'],
sortDirection: 'desc',
},
],
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 af27d2a11ad4b..e35a5b6480426 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
@@ -58,7 +58,8 @@ describe('epicLocalStorage', () => {
const sort: Sort[] = [
{
columnId: '@timestamp',
- columnType: 'number',
+ columnType: 'date',
+ esTypes: ['date'],
sortDirection: Direction.desc,
},
];
@@ -161,6 +162,7 @@ describe('epicLocalStorage', () => {
{
columnId: 'event.severity',
columnType: 'number',
+ esTypes: ['long'],
sortDirection: Direction.desc,
},
],
diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts
index b1a92743d13f0..916376a0b9a38 100644
--- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts
+++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts
@@ -129,7 +129,8 @@ const basicTimeline: TimelineModel = {
sort: [
{
columnId: '@timestamp',
- columnType: 'number',
+ columnType: 'date',
+ esTypes: ['date'],
sortDirection: Direction.desc,
},
],
@@ -958,6 +959,7 @@ describe('Timeline', () => {
{
columnId: 'some column',
columnType: 'text',
+ esTypes: ['keyword'],
sortDirection: Direction.desc,
},
],
@@ -970,7 +972,12 @@ describe('Timeline', () => {
test('should update the sort attribute', () => {
expect(update.foo.sort).toEqual([
- { columnId: 'some column', columnType: 'text', sortDirection: Direction.desc },
+ {
+ columnId: 'some column',
+ columnType: 'text',
+ esTypes: ['keyword'],
+ sortDirection: Direction.desc,
+ },
]);
});
});
diff --git a/x-pack/plugins/timelines/common/search_strategy/index_fields/index.ts b/x-pack/plugins/timelines/common/search_strategy/index_fields/index.ts
index 5e74e01fbe843..e6391e5860aff 100644
--- a/x-pack/plugins/timelines/common/search_strategy/index_fields/index.ts
+++ b/x-pack/plugins/timelines/common/search_strategy/index_fields/index.ts
@@ -70,6 +70,7 @@ export interface BrowserField {
name: string;
searchable: boolean;
type: string;
+ esTypes?: string[];
subType?: IFieldSubType;
readFromDocValues: boolean;
runtimeField?: RuntimeField;
diff --git a/x-pack/plugins/timelines/common/search_strategy/timeline/index.ts b/x-pack/plugins/timelines/common/search_strategy/timeline/index.ts
index 3d575ead5463a..7e37d1d092edf 100644
--- a/x-pack/plugins/timelines/common/search_strategy/timeline/index.ts
+++ b/x-pack/plugins/timelines/common/search_strategy/timeline/index.ts
@@ -48,6 +48,7 @@ export interface TimelineRequestBasicOptions extends IEsSearchRequest {
}
export interface TimelineRequestSortField extends SortField {
+ esTypes: string[];
type: string;
}
diff --git a/x-pack/plugins/timelines/common/types/timeline/columns/index.tsx b/x-pack/plugins/timelines/common/types/timeline/columns/index.tsx
index 9aeda73aeef67..caad36941687d 100644
--- a/x-pack/plugins/timelines/common/types/timeline/columns/index.tsx
+++ b/x-pack/plugins/timelines/common/types/timeline/columns/index.tsx
@@ -79,6 +79,7 @@ export type ColumnHeaderOptions = Pick<
category?: string;
columnHeaderType: ColumnHeaderType;
description?: string | null;
+ esTypes?: string[];
example?: string | number | null;
format?: string;
linkField?: string;
diff --git a/x-pack/plugins/timelines/common/types/timeline/store.ts b/x-pack/plugins/timelines/common/types/timeline/store.ts
index db2f09e4f74c7..74f58ce3622ad 100644
--- a/x-pack/plugins/timelines/common/types/timeline/store.ts
+++ b/x-pack/plugins/timelines/common/types/timeline/store.ts
@@ -34,6 +34,7 @@ export type SortDirection = 'none' | 'asc' | 'desc' | Direction;
export interface SortColumnTimeline {
columnId: string;
columnType: string;
+ esTypes?: string[];
sortDirection: SortDirection;
}
diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/default_headers.ts b/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/default_headers.ts
index a5fb5f4bacd43..9312ac17bf691 100644
--- a/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/default_headers.ts
+++ b/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/default_headers.ts
@@ -14,8 +14,9 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
{
columnHeaderType: defaultColumnHeaderType,
id: '@timestamp',
- type: 'number',
initialWidth: DEFAULT_DATE_COLUMN_MIN_WIDTH,
+ esTypes: ['date'],
+ type: 'date',
},
{
columnHeaderType: defaultColumnHeaderType,
diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/helpers.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/helpers.test.tsx
index 4aba02607ec2e..c91189b509a37 100644
--- a/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/helpers.test.tsx
+++ b/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/helpers.test.tsx
@@ -209,6 +209,7 @@ describe('helpers', () => {
defaultSortDirection,
description:
'Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.',
+ esTypes: ['date'],
example: '2016-05-23T08:05:34.853Z',
format: '',
id: '@timestamp',
diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/helpers.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/body/helpers.test.tsx
index 2e2cf23a87df8..444ba878d6709 100644
--- a/x-pack/plugins/timelines/public/components/t_grid/body/helpers.test.tsx
+++ b/x-pack/plugins/timelines/public/components/t_grid/body/helpers.test.tsx
@@ -91,6 +91,7 @@ describe('helpers', () => {
initialWidth: 176,
category: 'kibana',
type: 'date',
+ esTypes: ['date'],
aggregatable: true,
actions: {
showSortAsc: {
@@ -122,12 +123,22 @@ describe('helpers', () => {
test('it returns the expected results when each column has a corresponding entry in `columnHeaders`', () => {
expect(mapSortingColumns({ columns, columnHeaders })).toEqual([
- { columnId: 'kibana.rac.alert.status', columnType: 'string', sortDirection: 'asc' },
- { columnId: 'kibana.rac.alert.start', columnType: 'date', sortDirection: 'desc' },
+ {
+ columnId: 'kibana.rac.alert.status',
+ columnType: 'string',
+ esTypes: [],
+ sortDirection: 'asc',
+ },
+ {
+ columnId: 'kibana.rac.alert.start',
+ columnType: 'date',
+ esTypes: ['date'],
+ sortDirection: 'desc',
+ },
]);
});
- test('it defaults to a `columnType` of `text` when a column does NOT has a corresponding entry in `columnHeaders`', () => {
+ test('it defaults to a `columnType` of empty string when a column does NOT has a corresponding entry in `columnHeaders`', () => {
const withUnknownColumn: Array<{
id: string;
direction: 'asc' | 'desc';
@@ -147,11 +158,22 @@ describe('helpers', () => {
];
expect(mapSortingColumns({ columns: withUnknownColumn, columnHeaders })).toEqual([
- { columnId: 'kibana.rac.alert.status', columnType: 'string', sortDirection: 'asc' },
- { columnId: 'kibana.rac.alert.start', columnType: 'date', sortDirection: 'desc' },
+ {
+ columnId: 'kibana.rac.alert.status',
+ columnType: 'string',
+ esTypes: [],
+ sortDirection: 'asc',
+ },
+ {
+ columnId: 'kibana.rac.alert.start',
+ columnType: 'date',
+ esTypes: ['date'],
+ sortDirection: 'desc',
+ },
{
columnId: 'unknown',
- columnType: 'text', // <-- mapped to the default
+ columnType: '', // <-- mapped to the default
+ esTypes: [], // <-- mapped to the default
sortDirection: 'asc',
},
]);
diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/helpers.tsx b/x-pack/plugins/timelines/public/components/t_grid/body/helpers.tsx
index dabaecfdeea2b..958ea3595491e 100644
--- a/x-pack/plugins/timelines/public/components/t_grid/body/helpers.tsx
+++ b/x-pack/plugins/timelines/public/components/t_grid/body/helpers.tsx
@@ -94,11 +94,18 @@ export const mapSortingColumns = ({
direction: 'asc' | 'desc';
}>;
}): SortColumnTimeline[] =>
- columns.map(({ id, direction }) => ({
- columnId: id,
- columnType: columnHeaders.find((ch) => ch.id === id)?.type ?? 'text',
- sortDirection: direction,
- }));
+ columns.map(({ id, direction }) => {
+ const columnHeader = columnHeaders.find((ch) => ch.id === id);
+ const columnType = columnHeader?.type ?? '';
+ const esTypes = columnHeader?.esTypes ?? [];
+
+ return {
+ columnId: id,
+ columnType,
+ esTypes,
+ sortDirection: direction,
+ };
+ });
export const allowSorting = ({
browserField,
diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/index.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/body/index.test.tsx
index 7ee56478392fe..4ac9aec83a5cb 100644
--- a/x-pack/plugins/timelines/public/components/t_grid/body/index.test.tsx
+++ b/x-pack/plugins/timelines/public/components/t_grid/body/index.test.tsx
@@ -23,7 +23,8 @@ import { defaultColumnHeaderType } from '../../../store/t_grid/defaults';
const mockSort: Sort[] = [
{
columnId: '@timestamp',
- columnType: 'number',
+ columnType: 'date',
+ esTypes: ['date'],
sortDirection: Direction.desc,
},
];
diff --git a/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx
index 59b2f9c1541a1..e212abc5711df 100644
--- a/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx
+++ b/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx
@@ -226,10 +226,11 @@ const TGridIntegratedComponent: React.FC = ({
const sortField = useMemo(
() =>
- sort.map(({ columnId, columnType, sortDirection }) => ({
+ sort.map(({ columnId, columnType, esTypes, sortDirection }) => ({
field: columnId,
type: columnType,
direction: sortDirection as Direction,
+ esTypes: esTypes ?? [],
})),
[sort]
);
diff --git a/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx
index dedfd18312e9a..7a11389bd5039 100644
--- a/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx
+++ b/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx
@@ -203,10 +203,11 @@ const TGridStandaloneComponent: React.FC = ({
const sortField = useMemo(
() =>
- sortStore.map(({ columnId, columnType, sortDirection }) => ({
+ sortStore.map(({ columnId, columnType, esTypes, sortDirection }) => ({
field: columnId,
type: columnType,
direction: sortDirection as Direction,
+ esTypes: esTypes ?? [],
})),
[sortStore]
);
diff --git a/x-pack/plugins/timelines/public/container/index.tsx b/x-pack/plugins/timelines/public/container/index.tsx
index 322cd2bfdc54e..4ab6856c6df49 100644
--- a/x-pack/plugins/timelines/public/container/index.tsx
+++ b/x-pack/plugins/timelines/public/container/index.tsx
@@ -110,9 +110,10 @@ const getInspectResponse = (
const ID = 'timelineEventsQuery';
export const initSortDefault = [
{
+ direction: Direction.desc,
+ esTypes: ['date'],
field: '@timestamp',
- direction: Direction.asc,
- type: 'number',
+ type: 'date',
},
];
diff --git a/x-pack/plugins/timelines/public/mock/global_state.ts b/x-pack/plugins/timelines/public/mock/global_state.ts
index 955a612f89c1d..e4493964f7f54 100644
--- a/x-pack/plugins/timelines/public/mock/global_state.ts
+++ b/x-pack/plugins/timelines/public/mock/global_state.ts
@@ -40,7 +40,14 @@ export const mockGlobalState: TimelineState = {
itemsPerPageOptions: [5, 10, 20],
loadingEventIds: [],
showCheckboxes: false,
- sort: [{ columnId: '@timestamp', columnType: 'number', sortDirection: Direction.desc }],
+ sort: [
+ {
+ columnId: '@timestamp',
+ columnType: 'date',
+ esTypes: ['date'],
+ sortDirection: Direction.desc,
+ },
+ ],
selectedEventIds: {},
savedObjectId: null,
version: null,
diff --git a/x-pack/plugins/timelines/public/mock/header.ts b/x-pack/plugins/timelines/public/mock/header.ts
index a0a9f0fe15293..b52807d8aa958 100644
--- a/x-pack/plugins/timelines/public/mock/header.ts
+++ b/x-pack/plugins/timelines/public/mock/header.ts
@@ -21,6 +21,7 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
example: '2016-05-23T08:05:34.853Z',
id: '@timestamp',
type: 'date',
+ esTypes: ['date'],
aggregatable: true,
initialWidth: DEFAULT_DATE_COLUMN_MIN_WIDTH,
},
@@ -31,7 +32,8 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
"Severity describes the severity of the event. What the different severity values mean can very different between use cases. It's up to the implementer to make sure severities are consistent across events.",
example: '7',
id: 'event.severity',
- type: 'long',
+ type: 'number',
+ esTypes: ['long'],
aggregatable: true,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
@@ -42,7 +44,8 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
'Event category.\nThis contains high-level information about the contents of the event. It is more generic than `event.action`, in the sense that typically a category contains multiple actions. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.',
example: 'user-management',
id: 'event.category',
- type: 'keyword',
+ type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
@@ -53,7 +56,8 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
'The action captured by the event.\nThis describes the information in the event. It is more specific than `event.category`. Examples are `group-add`, `process-started`, `file-created`. The value is normally defined by the implementer.',
example: 'user-password-change',
id: 'event.action',
- type: 'keyword',
+ type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
@@ -64,7 +68,8 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
'Name of the host.\nIt can contain what `hostname` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.',
example: '',
id: 'host.name',
- type: 'keyword',
+ type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
@@ -75,6 +80,7 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
example: '',
id: 'source.ip',
type: 'ip',
+ esTypes: ['ip'],
aggregatable: true,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
@@ -85,6 +91,7 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
example: '',
id: 'destination.ip',
type: 'ip',
+ esTypes: ['ip'],
aggregatable: true,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
@@ -97,6 +104,7 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
format: 'bytes',
id: 'destination.bytes',
type: 'number',
+ esTypes: ['long'],
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
{
@@ -105,7 +113,8 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
description: 'Short name or login of the user.',
example: 'albert',
id: 'user.name',
- type: 'keyword',
+ type: 'string',
+ esTypes: ['keyword'],
aggregatable: true,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
@@ -115,8 +124,9 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
description: 'Each document has an _id that uniquely identifies it',
example: 'Y-6TfmcB0WOhS6qyMv3s',
id: '_id',
- type: 'keyword',
- aggregatable: true,
+ type: 'string',
+ esTypes: [], // empty for _id
+ aggregatable: false,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
{
@@ -126,7 +136,8 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
'For log events the message field contains the log message.\nIn other use cases the message field can be used to concatenate different values which are then freely searchable. If multiple messages exist, they can be combined into one message.',
example: 'Hello World',
id: 'message',
- type: 'text',
+ type: 'string',
+ esTypes: ['text'],
aggregatable: false,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
diff --git a/x-pack/plugins/timelines/public/mock/mock_timeline_data.ts b/x-pack/plugins/timelines/public/mock/mock_timeline_data.ts
index 55ec6862309aa..e4df9efb62d69 100644
--- a/x-pack/plugins/timelines/public/mock/mock_timeline_data.ts
+++ b/x-pack/plugins/timelines/public/mock/mock_timeline_data.ts
@@ -1580,7 +1580,8 @@ export const mockTgridModel: TGridModel = {
sort: [
{
columnId: '@timestamp',
- columnType: 'number',
+ columnType: 'date',
+ esTypes: ['date'],
sortDirection: Direction.desc,
},
],
diff --git a/x-pack/plugins/timelines/public/mock/t_grid.tsx b/x-pack/plugins/timelines/public/mock/t_grid.tsx
index d856cd43cadaf..b4f0110136886 100644
--- a/x-pack/plugins/timelines/public/mock/t_grid.tsx
+++ b/x-pack/plugins/timelines/public/mock/t_grid.tsx
@@ -123,6 +123,7 @@ export const tGridIntegratedProps: TGridIntegratedProps = {
{
columnId: '@timestamp',
columnType: 'date',
+ esTypes: ['date'],
sortDirection: 'desc',
},
],
diff --git a/x-pack/plugins/timelines/public/store/t_grid/defaults.ts b/x-pack/plugins/timelines/public/store/t_grid/defaults.ts
index 8e09f43a8ad4a..dfed73c16bf17 100644
--- a/x-pack/plugins/timelines/public/store/t_grid/defaults.ts
+++ b/x-pack/plugins/timelines/public/store/t_grid/defaults.ts
@@ -20,8 +20,9 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
{
columnHeaderType: defaultColumnHeaderType,
id: '@timestamp',
- type: 'number',
initialWidth: DEFAULT_DATE_COLUMN_MIN_WIDTH,
+ esTypes: ['date'],
+ type: 'date',
},
{
columnHeaderType: defaultColumnHeaderType,
@@ -84,6 +85,7 @@ export const tGridDefaults: SubsetTGridModel = {
{
columnId: '@timestamp',
columnType: 'date',
+ esTypes: ['date'],
sortDirection: Direction.desc,
},
],
diff --git a/x-pack/plugins/timelines/public/store/t_grid/helpers.test.tsx b/x-pack/plugins/timelines/public/store/t_grid/helpers.test.tsx
index a29a117946392..af0efd0834f9d 100644
--- a/x-pack/plugins/timelines/public/store/t_grid/helpers.test.tsx
+++ b/x-pack/plugins/timelines/public/store/t_grid/helpers.test.tsx
@@ -24,7 +24,7 @@ const defaultTimelineById = {
describe('setInitializeTgridSettings', () => {
test('it returns the expected sort when tGridSettingsProps has an override', () => {
const sort: SortColumnTimeline[] = [
- { columnId: 'foozle', columnType: 'date', sortDirection: 'asc' },
+ { columnId: 'foozle', columnType: 'date', esTypes: ['date'], sortDirection: 'asc' },
];
const tGridSettingsProps: Partial = {
diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/eql/helpers.test.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/eql/helpers.test.ts
index 1b58e63ec8752..5c6a0ac0bd416 100644
--- a/x-pack/plugins/timelines/server/search_strategy/timeline/eql/helpers.test.ts
+++ b/x-pack/plugins/timelines/server/search_strategy/timeline/eql/helpers.test.ts
@@ -35,9 +35,10 @@ describe('Search Strategy EQL helper', () => {
pagination: { activePage: 0, querySize: 25 },
sort: [
{
- field: '@timestamp',
direction: Direction.desc,
- type: 'number',
+ esTypes: ['date'],
+ field: '@timestamp',
+ type: 'date',
},
],
timerange: {
@@ -85,9 +86,10 @@ describe('Search Strategy EQL helper', () => {
pagination: { activePage: 1, querySize: 2 },
sort: [
{
- field: '@timestamp',
direction: Direction.desc,
- type: 'number',
+ esTypes: ['date'],
+ field: '@timestamp',
+ type: 'date',
},
],
timerange: {
@@ -141,9 +143,10 @@ describe('Search Strategy EQL helper', () => {
pagination: { activePage: 0, querySize: 2 },
sort: [
{
- field: '@timestamp',
direction: Direction.desc,
- type: 'number',
+ field: '@timestamp',
+ esTypes: ['date'],
+ type: 'date',
},
],
timerange: {
@@ -418,9 +421,10 @@ describe('Search Strategy EQL helper', () => {
pagination: { activePage: 3, querySize: 2 },
sort: [
{
- field: '@timestamp',
direction: Direction.desc,
- type: 'number',
+ esTypes: ['date'],
+ field: '@timestamp',
+ type: 'date',
},
],
timerange: {
diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/helpers.test.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/helpers.test.ts
new file mode 100644
index 0000000000000..aa6e9c8262c36
--- /dev/null
+++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/helpers.test.ts
@@ -0,0 +1,42 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { getPreferredEsType } from './helpers';
+
+describe('helpers', () => {
+ describe('getPreferredEsType', () => {
+ it('prefers `keyword` over other types when `esTypes` contains a `keyword` entry', () => {
+ const esTypes = ['long', 'keyword'];
+
+ expect(getPreferredEsType(esTypes)).toEqual('keyword');
+ });
+
+ it('returns the first entry when esTypes has multiple entries, but no `keyword` entry', () => {
+ const esTypes = ['long', 'date'];
+
+ expect(getPreferredEsType(esTypes)).toEqual('long');
+ });
+
+ it('returns the first entry when esTypes has only one (non-`keyword`) entry', () => {
+ const esTypes = ['date'];
+
+ expect(getPreferredEsType(esTypes)).toEqual('date');
+ });
+
+ it('returns `keyword` when esTypes only contains a `keyword` entry', () => {
+ const esTypes: string[] = ['keyword'];
+
+ expect(getPreferredEsType(esTypes)).toEqual('keyword');
+ });
+
+ it('returns `keyword` when esTypes is empty', () => {
+ const esTypes: string[] = [];
+
+ expect(getPreferredEsType(esTypes)).toEqual('keyword');
+ });
+ });
+});
diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/helpers.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/helpers.ts
new file mode 100644
index 0000000000000..f33e019cba2ce
--- /dev/null
+++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/helpers.ts
@@ -0,0 +1,21 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+/**
+ * When `esTypes` types array contains more than one value, and one of those
+ * (multiple) values is `keyword`, the `keyword` entry is returned. The
+ * `keyword` entry is preferred over other values when it exists in the array.
+ *
+ * The `keyword` value is also returned when the `esTypes` array is empty.
+ */
+export const getPreferredEsType = (esTypes: string[]): string => {
+ if (esTypes.length === 1 || (esTypes.length > 1 && !esTypes.includes('keyword'))) {
+ return esTypes[0]; // no preference
+ } else {
+ return 'keyword'; // esTypes includes `keyword`, or it's empty
+ }
+};
diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/query.events_all.dsl.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/query.events_all.dsl.ts
index 1ac0ff00c7db1..53009e797e82f 100644
--- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/query.events_all.dsl.ts
+++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/query.events_all.dsl.ts
@@ -15,6 +15,7 @@ import {
TimelineRequestSortField,
} from '../../../../../../common/search_strategy';
import { createQueryFilterClauses } from '../../../../../utils/build_query';
+import { getPreferredEsType } from './helpers';
export const buildTimelineEventsAllQuery = ({
authFilter,
@@ -58,7 +59,7 @@ export const buildTimelineEventsAllQuery = ({
return {
[field]: {
order: item.direction,
- unmapped_type: item.type,
+ unmapped_type: getPreferredEsType(item.esTypes),
},
};
});