mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[SecuritySolution][Timeline] Clean removed runtime fields (#122976)
* remove unexisting fields from timeline * replicate clean logic in security timeline * tests updated due to mocks changes * solve action dipatches race condition * tests fixed * fix async dispatches Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
1e576042df
commit
7f7dbbb3cc
19 changed files with 1315 additions and 122 deletions
|
@ -129,6 +129,35 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
|
|||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
"_id": Object {
|
||||
"aggregatable": false,
|
||||
"category": "base",
|
||||
"description": "Each document has an _id that uniquely identifies it",
|
||||
"example": "Y-6TfmcB0WOhS6qyMv3s",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "_id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"message": Object {
|
||||
"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.",
|
||||
"example": "Hello World",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "message",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"client": Object {
|
||||
|
@ -359,6 +388,46 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
|
|||
},
|
||||
"event": Object {
|
||||
"fields": Object {
|
||||
"event.action": Object {
|
||||
"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.",
|
||||
"example": "user-password-change",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.action",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"event.category": Object {
|
||||
"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.",
|
||||
"example": "authentication",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.category",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"event.end": Object {
|
||||
"aggregatable": true,
|
||||
"category": "event",
|
||||
|
@ -379,6 +448,49 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
|
|||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
"event.severity": Object {
|
||||
"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\`.",
|
||||
"example": 7,
|
||||
"format": "number",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.severity",
|
||||
"searchable": true,
|
||||
"type": "number",
|
||||
},
|
||||
},
|
||||
},
|
||||
"host": Object {
|
||||
"fields": Object {
|
||||
"host.name": Object {
|
||||
"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.",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "host.name",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"nestedField": Object {
|
||||
|
@ -459,6 +571,25 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] =
|
|||
},
|
||||
},
|
||||
},
|
||||
"user": Object {
|
||||
"fields": Object {
|
||||
"user.name": Object {
|
||||
"aggregatable": true,
|
||||
"category": "user",
|
||||
"description": "Short name or login of the user.",
|
||||
"example": "albert",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "user.name",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
|
|
|
@ -24,30 +24,40 @@ exports[`AlertSummaryView Behavior event code renders additional summary rows 1`
|
|||
line-height: 1.7rem;
|
||||
}
|
||||
|
||||
.c2 {
|
||||
.c2,
|
||||
.c2 * {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.c3 {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.c2:focus-within .timelines__hoverActionButton,
|
||||
.c2:focus-within .securitySolution__hoverActionButton {
|
||||
.c3:focus-within .timelines__hoverActionButton,
|
||||
.c3:focus-within .securitySolution__hoverActionButton {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.c2:hover .timelines__hoverActionButton,
|
||||
.c2:hover .securitySolution__hoverActionButton {
|
||||
.c3:hover .timelines__hoverActionButton,
|
||||
.c3:hover .securitySolution__hoverActionButton {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.c2 .timelines__hoverActionButton,
|
||||
.c2 .securitySolution__hoverActionButton {
|
||||
.c3 .timelines__hoverActionButton,
|
||||
.c3 .securitySolution__hoverActionButton {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.c2 .timelines__hoverActionButton:focus,
|
||||
.c2 .securitySolution__hoverActionButton:focus {
|
||||
.c3 .timelines__hoverActionButton:focus,
|
||||
.c3 .securitySolution__hoverActionButton:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
@ -140,20 +150,27 @@ exports[`AlertSummaryView Behavior event code renders additional summary rows 1`
|
|||
data-test-subj="event-field-host.name"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero eventFieldsTable__fieldValue"
|
||||
>
|
||||
<div
|
||||
class="euiText euiText--extraSmall"
|
||||
<button
|
||||
class="euiLink euiLink--primary"
|
||||
data-test-subj="host-details-button"
|
||||
type="button"
|
||||
>
|
||||
windows-native
|
||||
</div>
|
||||
<span
|
||||
class="c2"
|
||||
data-test-subj="draggable-truncatable-content"
|
||||
>
|
||||
windows-native
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-eui="EuiFocusTrap"
|
||||
>
|
||||
<div
|
||||
class="c2"
|
||||
class="c3"
|
||||
>
|
||||
<p
|
||||
class="euiScreenReaderOnly"
|
||||
|
@ -173,7 +190,7 @@ exports[`AlertSummaryView Behavior event code renders additional summary rows 1`
|
|||
<div
|
||||
data-test-subj="more-actions-host.name"
|
||||
field="host.name"
|
||||
items="[object Object]"
|
||||
items="[object Object],[object Object],[object Object]"
|
||||
value="windows-native"
|
||||
>
|
||||
Overflow button
|
||||
|
@ -213,20 +230,20 @@ exports[`AlertSummaryView Behavior event code renders additional summary rows 1`
|
|||
data-test-subj="event-field-user.name"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero eventFieldsTable__fieldValue"
|
||||
>
|
||||
<div
|
||||
class="euiText euiText--extraSmall"
|
||||
<span
|
||||
data-test-subj="formatted-field-user.name"
|
||||
>
|
||||
administrator
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-eui="EuiFocusTrap"
|
||||
>
|
||||
<div
|
||||
class="c2"
|
||||
class="c3"
|
||||
>
|
||||
<p
|
||||
class="euiScreenReaderOnly"
|
||||
|
@ -246,7 +263,7 @@ exports[`AlertSummaryView Behavior event code renders additional summary rows 1`
|
|||
<div
|
||||
data-test-subj="more-actions-user.name"
|
||||
field="user.name"
|
||||
items="[object Object]"
|
||||
items="[object Object],[object Object],[object Object]"
|
||||
value="administrator"
|
||||
>
|
||||
Overflow button
|
||||
|
@ -305,7 +322,7 @@ exports[`AlertSummaryView Behavior event code renders additional summary rows 1`
|
|||
data-eui="EuiFocusTrap"
|
||||
>
|
||||
<div
|
||||
class="c2"
|
||||
class="c3"
|
||||
>
|
||||
<p
|
||||
class="euiScreenReaderOnly"
|
||||
|
@ -365,30 +382,40 @@ exports[`AlertSummaryView Memory event code renders additional summary rows 1`]
|
|||
line-height: 1.7rem;
|
||||
}
|
||||
|
||||
.c2 {
|
||||
.c2,
|
||||
.c2 * {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.c3 {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.c2:focus-within .timelines__hoverActionButton,
|
||||
.c2:focus-within .securitySolution__hoverActionButton {
|
||||
.c3:focus-within .timelines__hoverActionButton,
|
||||
.c3:focus-within .securitySolution__hoverActionButton {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.c2:hover .timelines__hoverActionButton,
|
||||
.c2:hover .securitySolution__hoverActionButton {
|
||||
.c3:hover .timelines__hoverActionButton,
|
||||
.c3:hover .securitySolution__hoverActionButton {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.c2 .timelines__hoverActionButton,
|
||||
.c2 .securitySolution__hoverActionButton {
|
||||
.c3 .timelines__hoverActionButton,
|
||||
.c3 .securitySolution__hoverActionButton {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.c2 .timelines__hoverActionButton:focus,
|
||||
.c2 .securitySolution__hoverActionButton:focus {
|
||||
.c3 .timelines__hoverActionButton:focus,
|
||||
.c3 .securitySolution__hoverActionButton:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
@ -481,20 +508,27 @@ exports[`AlertSummaryView Memory event code renders additional summary rows 1`]
|
|||
data-test-subj="event-field-host.name"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero eventFieldsTable__fieldValue"
|
||||
>
|
||||
<div
|
||||
class="euiText euiText--extraSmall"
|
||||
<button
|
||||
class="euiLink euiLink--primary"
|
||||
data-test-subj="host-details-button"
|
||||
type="button"
|
||||
>
|
||||
windows-native
|
||||
</div>
|
||||
<span
|
||||
class="c2"
|
||||
data-test-subj="draggable-truncatable-content"
|
||||
>
|
||||
windows-native
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-eui="EuiFocusTrap"
|
||||
>
|
||||
<div
|
||||
class="c2"
|
||||
class="c3"
|
||||
>
|
||||
<p
|
||||
class="euiScreenReaderOnly"
|
||||
|
@ -514,7 +548,7 @@ exports[`AlertSummaryView Memory event code renders additional summary rows 1`]
|
|||
<div
|
||||
data-test-subj="more-actions-host.name"
|
||||
field="host.name"
|
||||
items="[object Object]"
|
||||
items="[object Object],[object Object],[object Object]"
|
||||
value="windows-native"
|
||||
>
|
||||
Overflow button
|
||||
|
@ -554,20 +588,20 @@ exports[`AlertSummaryView Memory event code renders additional summary rows 1`]
|
|||
data-test-subj="event-field-user.name"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero eventFieldsTable__fieldValue"
|
||||
>
|
||||
<div
|
||||
class="euiText euiText--extraSmall"
|
||||
<span
|
||||
data-test-subj="formatted-field-user.name"
|
||||
>
|
||||
administrator
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-eui="EuiFocusTrap"
|
||||
>
|
||||
<div
|
||||
class="c2"
|
||||
class="c3"
|
||||
>
|
||||
<p
|
||||
class="euiScreenReaderOnly"
|
||||
|
@ -587,7 +621,7 @@ exports[`AlertSummaryView Memory event code renders additional summary rows 1`]
|
|||
<div
|
||||
data-test-subj="more-actions-user.name"
|
||||
field="user.name"
|
||||
items="[object Object]"
|
||||
items="[object Object],[object Object],[object Object]"
|
||||
value="administrator"
|
||||
>
|
||||
Overflow button
|
||||
|
@ -646,7 +680,7 @@ exports[`AlertSummaryView Memory event code renders additional summary rows 1`]
|
|||
data-eui="EuiFocusTrap"
|
||||
>
|
||||
<div
|
||||
class="c2"
|
||||
class="c3"
|
||||
>
|
||||
<p
|
||||
class="euiScreenReaderOnly"
|
||||
|
|
|
@ -118,23 +118,23 @@ describe('source/index.tsx', () => {
|
|||
await act(async () => {
|
||||
const { rerender, waitForNextUpdate, result } = renderHook<
|
||||
string,
|
||||
{ indexFieldsSearch: (id: string) => void }
|
||||
{ indexFieldsSearch: (id: string) => Promise<void> }
|
||||
>(() => useDataView(), {
|
||||
wrapper: ({ children }) => <Provider store={store}>{children}</Provider>,
|
||||
});
|
||||
await waitForNextUpdate();
|
||||
rerender();
|
||||
act(() => result.current.indexFieldsSearch('neato'));
|
||||
expect(mockDispatch.mock.calls[0][0]).toEqual({
|
||||
type: 'x-pack/security_solution/local/sourcerer/SET_DATA_VIEW_LOADING',
|
||||
payload: { id: 'neato', loading: true },
|
||||
});
|
||||
const { type: sourceType, payload } = mockDispatch.mock.calls[1][0];
|
||||
expect(sourceType).toEqual('x-pack/security_solution/local/sourcerer/SET_DATA_VIEW');
|
||||
expect(payload.id).toEqual('neato');
|
||||
expect(Object.keys(payload.browserFields)).toHaveLength(10);
|
||||
expect(payload.docValueFields).toEqual([{ field: '@timestamp' }]);
|
||||
await result.current.indexFieldsSearch('neato');
|
||||
});
|
||||
expect(mockDispatch.mock.calls[0][0]).toEqual({
|
||||
type: 'x-pack/security_solution/local/sourcerer/SET_DATA_VIEW_LOADING',
|
||||
payload: { id: 'neato', loading: true },
|
||||
});
|
||||
const { type: sourceType, payload } = mockDispatch.mock.calls[1][0];
|
||||
expect(sourceType).toEqual('x-pack/security_solution/local/sourcerer/SET_DATA_VIEW');
|
||||
expect(payload.id).toEqual('neato');
|
||||
expect(Object.keys(payload.browserFields)).toHaveLength(12);
|
||||
expect(payload.docValueFields).toEqual([{ field: '@timestamp' }]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -25,6 +25,28 @@ export const mocksSource = {
|
|||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
{
|
||||
category: 'base',
|
||||
description: 'Each document has an _id that uniquely identifies it',
|
||||
example: 'Y-6TfmcB0WOhS6qyMv3s',
|
||||
name: '_id',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: false,
|
||||
indexes: ['auditbeat', 'filebeat', 'packetbeat'],
|
||||
},
|
||||
{
|
||||
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.',
|
||||
example: 'Hello World',
|
||||
name: 'message',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: false,
|
||||
format: 'string',
|
||||
indexes: ['auditbeat', 'filebeat', 'packetbeat'],
|
||||
},
|
||||
{
|
||||
category: 'agent',
|
||||
description:
|
||||
|
@ -296,6 +318,64 @@ export const mocksSource = {
|
|||
searchable: true,
|
||||
type: 'date',
|
||||
},
|
||||
{
|
||||
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.',
|
||||
example: 'user-password-change',
|
||||
name: 'event.action',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
format: 'string',
|
||||
indexes: DEFAULT_INDEX_PATTERN,
|
||||
},
|
||||
{
|
||||
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.',
|
||||
example: 'authentication',
|
||||
name: 'event.category',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
format: 'string',
|
||||
indexes: DEFAULT_INDEX_PATTERN,
|
||||
},
|
||||
{
|
||||
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`.",
|
||||
example: 7,
|
||||
name: 'event.severity',
|
||||
type: 'number',
|
||||
format: 'number',
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
indexes: DEFAULT_INDEX_PATTERN,
|
||||
},
|
||||
{
|
||||
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.',
|
||||
name: 'host.name',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
format: 'string',
|
||||
indexes: DEFAULT_INDEX_PATTERN,
|
||||
},
|
||||
{
|
||||
category: 'user',
|
||||
description: 'Short name or login of the user.',
|
||||
example: 'albert',
|
||||
name: 'user.name',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
format: 'string',
|
||||
indexes: ['auditbeat', 'filebeat', 'packetbeat'],
|
||||
},
|
||||
{
|
||||
aggregatable: false,
|
||||
category: 'nestedField',
|
||||
|
@ -469,6 +549,28 @@ export const mockBrowserFields: BrowserFields = {
|
|||
type: 'date',
|
||||
readFromDocValues: true,
|
||||
},
|
||||
_id: {
|
||||
category: 'base',
|
||||
description: 'Each document has an _id that uniquely identifies it',
|
||||
example: 'Y-6TfmcB0WOhS6qyMv3s',
|
||||
name: '_id',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: false,
|
||||
indexes: ['auditbeat', 'filebeat', 'packetbeat'],
|
||||
},
|
||||
message: {
|
||||
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.',
|
||||
example: 'Hello World',
|
||||
name: 'message',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: false,
|
||||
format: 'string',
|
||||
indexes: ['auditbeat', 'filebeat', 'packetbeat'],
|
||||
},
|
||||
},
|
||||
},
|
||||
client: {
|
||||
|
@ -659,6 +761,57 @@ export const mockBrowserFields: BrowserFields = {
|
|||
type: 'date',
|
||||
aggregatable: true,
|
||||
},
|
||||
'event.action': {
|
||||
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.',
|
||||
example: 'user-password-change',
|
||||
name: 'event.action',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
format: 'string',
|
||||
indexes: DEFAULT_INDEX_PATTERN,
|
||||
},
|
||||
'event.category': {
|
||||
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.',
|
||||
example: 'authentication',
|
||||
name: 'event.category',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
format: 'string',
|
||||
indexes: DEFAULT_INDEX_PATTERN,
|
||||
},
|
||||
'event.severity': {
|
||||
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`.",
|
||||
example: 7,
|
||||
name: 'event.severity',
|
||||
type: 'number',
|
||||
format: 'number',
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
indexes: DEFAULT_INDEX_PATTERN,
|
||||
},
|
||||
},
|
||||
},
|
||||
host: {
|
||||
fields: {
|
||||
'host.name': {
|
||||
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.',
|
||||
name: 'host.name',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
format: 'string',
|
||||
indexes: DEFAULT_INDEX_PATTERN,
|
||||
},
|
||||
},
|
||||
},
|
||||
source: {
|
||||
|
@ -687,6 +840,21 @@ export const mockBrowserFields: BrowserFields = {
|
|||
},
|
||||
},
|
||||
},
|
||||
user: {
|
||||
fields: {
|
||||
'user.name': {
|
||||
category: 'user',
|
||||
description: 'Short name or login of the user.',
|
||||
example: 'albert',
|
||||
name: 'user.name',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
format: 'string',
|
||||
indexes: ['auditbeat', 'filebeat', 'packetbeat'],
|
||||
},
|
||||
},
|
||||
},
|
||||
nestedField: {
|
||||
fields: {
|
||||
'nestedField.firstAttributes': {
|
||||
|
|
|
@ -44,7 +44,7 @@ export const useDataView = (): {
|
|||
selectedDataViewId: string,
|
||||
scopeId?: SourcererScopeName,
|
||||
needToBeInit?: boolean
|
||||
) => void;
|
||||
) => Promise<void>;
|
||||
} => {
|
||||
const { data } = useKibana().services;
|
||||
const abortCtrl = useRef<Record<string, AbortController>>({});
|
||||
|
@ -82,63 +82,67 @@ export const useDataView = (): {
|
|||
);
|
||||
}
|
||||
|
||||
const subscription = data.search
|
||||
.search<IndexFieldsStrategyRequest<'dataView'>, IndexFieldsStrategyResponse>(
|
||||
{
|
||||
dataViewId: selectedDataViewId,
|
||||
onlyCheckIfIndicesExist: false,
|
||||
},
|
||||
{
|
||||
abortSignal: abortCtrl.current[selectedDataViewId].signal,
|
||||
strategy: 'indexFields',
|
||||
}
|
||||
)
|
||||
.subscribe({
|
||||
next: (response) => {
|
||||
if (isCompleteResponse(response)) {
|
||||
const patternString = response.indicesExist.sort().join();
|
||||
if (needToBeInit && scopeId) {
|
||||
return new Promise<void>((resolve) => {
|
||||
const subscription = data.search
|
||||
.search<IndexFieldsStrategyRequest<'dataView'>, IndexFieldsStrategyResponse>(
|
||||
{
|
||||
dataViewId: selectedDataViewId,
|
||||
onlyCheckIfIndicesExist: false,
|
||||
},
|
||||
{
|
||||
abortSignal: abortCtrl.current[selectedDataViewId].signal,
|
||||
strategy: 'indexFields',
|
||||
}
|
||||
)
|
||||
.subscribe({
|
||||
next: async (response) => {
|
||||
if (isCompleteResponse(response)) {
|
||||
const patternString = response.indicesExist.sort().join();
|
||||
if (needToBeInit && scopeId) {
|
||||
dispatch(
|
||||
sourcererActions.setSelectedDataView({
|
||||
id: scopeId,
|
||||
selectedDataViewId,
|
||||
selectedPatterns: response.indicesExist,
|
||||
})
|
||||
);
|
||||
}
|
||||
dispatch(
|
||||
sourcererActions.setSelectedDataView({
|
||||
id: scopeId,
|
||||
selectedDataViewId,
|
||||
selectedPatterns: response.indicesExist,
|
||||
sourcererActions.setDataView({
|
||||
browserFields: getBrowserFields(patternString, response.indexFields),
|
||||
docValueFields: getDocValueFields(patternString, response.indexFields),
|
||||
id: selectedDataViewId,
|
||||
indexFields: getEsFields(response.indexFields),
|
||||
loading: false,
|
||||
runtimeMappings: response.runtimeMappings,
|
||||
})
|
||||
);
|
||||
searchSubscription$.current[selectedDataViewId]?.unsubscribe();
|
||||
} else if (isErrorResponse(response)) {
|
||||
setLoading({ id: selectedDataViewId, loading: false });
|
||||
addWarning(i18n.ERROR_BEAT_FIELDS);
|
||||
searchSubscription$.current[selectedDataViewId]?.unsubscribe();
|
||||
}
|
||||
resolve();
|
||||
},
|
||||
error: (msg) => {
|
||||
if (msg.message === DELETED_SECURITY_SOLUTION_DATA_VIEW) {
|
||||
// reload app if security solution data view is deleted
|
||||
return location.reload();
|
||||
}
|
||||
dispatch(
|
||||
sourcererActions.setDataView({
|
||||
browserFields: getBrowserFields(patternString, response.indexFields),
|
||||
docValueFields: getDocValueFields(patternString, response.indexFields),
|
||||
id: selectedDataViewId,
|
||||
indexFields: getEsFields(response.indexFields),
|
||||
loading: false,
|
||||
runtimeMappings: response.runtimeMappings,
|
||||
})
|
||||
);
|
||||
searchSubscription$.current[selectedDataViewId]?.unsubscribe();
|
||||
} else if (isErrorResponse(response)) {
|
||||
setLoading({ id: selectedDataViewId, loading: false });
|
||||
addWarning(i18n.ERROR_BEAT_FIELDS);
|
||||
addError(msg, {
|
||||
title: i18n.FAIL_BEAT_FIELDS,
|
||||
});
|
||||
searchSubscription$.current[selectedDataViewId]?.unsubscribe();
|
||||
}
|
||||
},
|
||||
error: (msg) => {
|
||||
if (msg.message === DELETED_SECURITY_SOLUTION_DATA_VIEW) {
|
||||
// reload app if security solution data view is deleted
|
||||
return location.reload();
|
||||
}
|
||||
setLoading({ id: selectedDataViewId, loading: false });
|
||||
addError(msg, {
|
||||
title: i18n.FAIL_BEAT_FIELDS,
|
||||
});
|
||||
searchSubscription$.current[selectedDataViewId]?.unsubscribe();
|
||||
},
|
||||
});
|
||||
searchSubscription$.current = {
|
||||
...searchSubscription$.current,
|
||||
[selectedDataViewId]: subscription,
|
||||
};
|
||||
resolve();
|
||||
},
|
||||
});
|
||||
searchSubscription$.current = {
|
||||
...searchSubscription$.current,
|
||||
[selectedDataViewId]: subscription,
|
||||
};
|
||||
});
|
||||
};
|
||||
if (searchSubscription$.current[selectedDataViewId]) {
|
||||
searchSubscription$.current[selectedDataViewId].unsubscribe();
|
||||
|
@ -146,7 +150,7 @@ export const useDataView = (): {
|
|||
if (abortCtrl.current[selectedDataViewId]) {
|
||||
abortCtrl.current[selectedDataViewId].abort();
|
||||
}
|
||||
asyncSearch();
|
||||
return asyncSearch();
|
||||
},
|
||||
[addError, addWarning, data.search, dispatch, setLoading]
|
||||
);
|
||||
|
|
|
@ -54,11 +54,11 @@ export const CreateFieldButton = React.memo<CreateFieldButtonProps>(
|
|||
if (dataView) {
|
||||
dataViewFieldEditor?.openEditor({
|
||||
ctx: { dataView },
|
||||
onSave: (field: DataViewField) => {
|
||||
onSave: async (field: DataViewField) => {
|
||||
// Fetch the updated list of fields
|
||||
indexFieldsSearch(selectedDataViewId);
|
||||
await indexFieldsSearch(selectedDataViewId);
|
||||
|
||||
// Add the new field to the event table
|
||||
// Add the new field to the event table, after waiting for browserFields to be stored
|
||||
dispatch(
|
||||
upsertColumn({
|
||||
column: {
|
||||
|
|
|
@ -49,7 +49,10 @@ describe('helpers', () => {
|
|||
{ label: 'auditd.data.a2' },
|
||||
],
|
||||
},
|
||||
{ label: 'base', options: [{ label: '@timestamp' }] },
|
||||
{
|
||||
label: 'base',
|
||||
options: [{ label: '@timestamp' }, { label: '_id' }, { label: 'message' }],
|
||||
},
|
||||
{
|
||||
label: 'client',
|
||||
options: [
|
||||
|
@ -81,7 +84,19 @@ describe('helpers', () => {
|
|||
{ label: 'destination.port' },
|
||||
],
|
||||
},
|
||||
{ label: 'event', options: [{ label: 'event.end' }] },
|
||||
{
|
||||
label: 'event',
|
||||
options: [
|
||||
{ label: 'event.end' },
|
||||
{ label: 'event.action' },
|
||||
{ label: 'event.category' },
|
||||
{ label: 'event.severity' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'host',
|
||||
options: [{ label: 'host.name' }],
|
||||
},
|
||||
{
|
||||
label: 'nestedField',
|
||||
options: [
|
||||
|
@ -94,6 +109,10 @@ describe('helpers', () => {
|
|||
],
|
||||
},
|
||||
{ label: 'source', options: [{ label: 'source.ip' }, { label: 'source.port' }] },
|
||||
{
|
||||
label: 'user',
|
||||
options: [{ label: 'user.name' }],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -130,6 +130,35 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
|
|||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
"_id": Object {
|
||||
"aggregatable": false,
|
||||
"category": "base",
|
||||
"description": "Each document has an _id that uniquely identifies it",
|
||||
"example": "Y-6TfmcB0WOhS6qyMv3s",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "_id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"message": Object {
|
||||
"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.",
|
||||
"example": "Hello World",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "message",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"client": Object {
|
||||
|
@ -360,6 +389,46 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
|
|||
},
|
||||
"event": Object {
|
||||
"fields": Object {
|
||||
"event.action": Object {
|
||||
"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.",
|
||||
"example": "user-password-change",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.action",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"event.category": Object {
|
||||
"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.",
|
||||
"example": "authentication",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.category",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"event.end": Object {
|
||||
"aggregatable": true,
|
||||
"category": "event",
|
||||
|
@ -380,6 +449,49 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
|
|||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
"event.severity": Object {
|
||||
"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\`.",
|
||||
"example": 7,
|
||||
"format": "number",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.severity",
|
||||
"searchable": true,
|
||||
"type": "number",
|
||||
},
|
||||
},
|
||||
},
|
||||
"host": Object {
|
||||
"fields": Object {
|
||||
"host.name": Object {
|
||||
"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.",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "host.name",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"nestedField": Object {
|
||||
|
@ -460,6 +572,25 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
|
|||
},
|
||||
},
|
||||
},
|
||||
"user": Object {
|
||||
"fields": Object {
|
||||
"user.name": Object {
|
||||
"aggregatable": true,
|
||||
"category": "user",
|
||||
"description": "Short name or login of the user.",
|
||||
"example": "albert",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "user.name",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
columnHeaders={
|
||||
|
|
|
@ -22,7 +22,7 @@ import { Sort } from './sort';
|
|||
import { getDefaultControlColumn } from './control_columns';
|
||||
import { useMountAppended } from '../../../../common/utils/use_mount_appended';
|
||||
import { timelineActions } from '../../../store/timeline';
|
||||
import { TimelineTabs } from '../../../../../common/types/timeline';
|
||||
import { ColumnHeaderOptions, TimelineTabs } from '../../../../../common/types/timeline';
|
||||
import { defaultRowRenderers } from './renderers';
|
||||
|
||||
jest.mock('../../../../common/lib/kibana/hooks');
|
||||
|
@ -154,6 +154,10 @@ describe('Body', () => {
|
|||
};
|
||||
|
||||
describe('rendering', () => {
|
||||
beforeEach(() => {
|
||||
mockDispatch.mockClear();
|
||||
});
|
||||
|
||||
test('it renders the column headers', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
|
@ -206,6 +210,28 @@ describe('Body', () => {
|
|||
});
|
||||
});
|
||||
}, 20000);
|
||||
|
||||
test('it dispatches the `REMOVE_COLUMN` action when there is a field removed from the custom fields', async () => {
|
||||
const customFieldId = 'my.custom.runtimeField';
|
||||
const extraFieldProps = {
|
||||
...props,
|
||||
columnHeaders: [
|
||||
...defaultHeaders,
|
||||
{ id: customFieldId, category: 'my' } as ColumnHeaderOptions,
|
||||
],
|
||||
};
|
||||
mount(
|
||||
<TestProviders>
|
||||
<BodyComponent {...extraFieldProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(mockDispatch).toBeCalledTimes(1);
|
||||
expect(mockDispatch).toBeCalledWith({
|
||||
payload: { columnId: customFieldId, id: 'timeline-test' },
|
||||
type: 'x-pack/timelines/t-grid/REMOVE_COLUMN',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('action on event', () => {
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { noop } from 'lodash/fp';
|
||||
import { noop, isEmpty } from 'lodash/fp';
|
||||
import memoizeOne from 'memoize-one';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
import { connect, ConnectedProps, useDispatch } from 'react-redux';
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
|
||||
import {
|
||||
|
@ -99,6 +99,7 @@ export const BodyComponent = React.memo<StatefulBodyProps>(
|
|||
leadingControlColumns = [],
|
||||
trailingControlColumns = [],
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const getManageTimeline = useMemo(() => timelineSelectors.getManageTimelineById(), []);
|
||||
const { queryFields, selectAll } = useDeepEqualSelector((state) =>
|
||||
|
@ -143,6 +144,19 @@ export const BodyComponent = React.memo<StatefulBodyProps>(
|
|||
}
|
||||
}, [isSelectAllChecked, onSelectAll, selectAll]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEmpty(browserFields) && !isEmpty(columnHeaders)) {
|
||||
columnHeaders.forEach(({ id: columnId }) => {
|
||||
if (browserFields.base?.fields?.[columnId] == null) {
|
||||
const [category] = columnId.split('.');
|
||||
if (browserFields[category]?.fields?.[columnId] == null) {
|
||||
dispatch(timelineActions.removeColumn({ id, columnId }));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [browserFields, columnHeaders, dispatch, id]);
|
||||
|
||||
const enabledRowRenderers = useMemo(() => {
|
||||
if (
|
||||
excludedRowRendererIds &&
|
||||
|
|
|
@ -131,6 +131,35 @@ exports[`suricata_row_renderer renders correctly against snapshot 1`] = `
|
|||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
"_id": Object {
|
||||
"aggregatable": false,
|
||||
"category": "base",
|
||||
"description": "Each document has an _id that uniquely identifies it",
|
||||
"example": "Y-6TfmcB0WOhS6qyMv3s",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "_id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"message": Object {
|
||||
"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.",
|
||||
"example": "Hello World",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "message",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"client": Object {
|
||||
|
@ -361,6 +390,46 @@ exports[`suricata_row_renderer renders correctly against snapshot 1`] = `
|
|||
},
|
||||
"event": Object {
|
||||
"fields": Object {
|
||||
"event.action": Object {
|
||||
"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.",
|
||||
"example": "user-password-change",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.action",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"event.category": Object {
|
||||
"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.",
|
||||
"example": "authentication",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.category",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"event.end": Object {
|
||||
"aggregatable": true,
|
||||
"category": "event",
|
||||
|
@ -381,6 +450,49 @@ exports[`suricata_row_renderer renders correctly against snapshot 1`] = `
|
|||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
"event.severity": Object {
|
||||
"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\`.",
|
||||
"example": 7,
|
||||
"format": "number",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.severity",
|
||||
"searchable": true,
|
||||
"type": "number",
|
||||
},
|
||||
},
|
||||
},
|
||||
"host": Object {
|
||||
"fields": Object {
|
||||
"host.name": Object {
|
||||
"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.",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "host.name",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"nestedField": Object {
|
||||
|
@ -461,6 +573,25 @@ exports[`suricata_row_renderer renders correctly against snapshot 1`] = `
|
|||
},
|
||||
},
|
||||
},
|
||||
"user": Object {
|
||||
"fields": Object {
|
||||
"user.name": Object {
|
||||
"aggregatable": true,
|
||||
"category": "user",
|
||||
"description": "Short name or login of the user.",
|
||||
"example": "albert",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "user.name",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
data={
|
||||
|
|
|
@ -129,6 +129,35 @@ exports[`ZeekDetails rendering it renders the default ZeekDetails 1`] = `
|
|||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
"_id": Object {
|
||||
"aggregatable": false,
|
||||
"category": "base",
|
||||
"description": "Each document has an _id that uniquely identifies it",
|
||||
"example": "Y-6TfmcB0WOhS6qyMv3s",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "_id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"message": Object {
|
||||
"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.",
|
||||
"example": "Hello World",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "message",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"client": Object {
|
||||
|
@ -359,6 +388,46 @@ exports[`ZeekDetails rendering it renders the default ZeekDetails 1`] = `
|
|||
},
|
||||
"event": Object {
|
||||
"fields": Object {
|
||||
"event.action": Object {
|
||||
"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.",
|
||||
"example": "user-password-change",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.action",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"event.category": Object {
|
||||
"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.",
|
||||
"example": "authentication",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.category",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"event.end": Object {
|
||||
"aggregatable": true,
|
||||
"category": "event",
|
||||
|
@ -379,6 +448,49 @@ exports[`ZeekDetails rendering it renders the default ZeekDetails 1`] = `
|
|||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
"event.severity": Object {
|
||||
"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\`.",
|
||||
"example": 7,
|
||||
"format": "number",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.severity",
|
||||
"searchable": true,
|
||||
"type": "number",
|
||||
},
|
||||
},
|
||||
},
|
||||
"host": Object {
|
||||
"fields": Object {
|
||||
"host.name": Object {
|
||||
"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.",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "host.name",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"nestedField": Object {
|
||||
|
@ -459,6 +571,25 @@ exports[`ZeekDetails rendering it renders the default ZeekDetails 1`] = `
|
|||
},
|
||||
},
|
||||
},
|
||||
"user": Object {
|
||||
"fields": Object {
|
||||
"user.name": Object {
|
||||
"aggregatable": true,
|
||||
"category": "user",
|
||||
"description": "Short name or login of the user.",
|
||||
"example": "albert",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "user.name",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
data={
|
||||
|
|
|
@ -131,6 +131,35 @@ exports[`zeek_row_renderer renders correctly against snapshot 1`] = `
|
|||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
"_id": Object {
|
||||
"aggregatable": false,
|
||||
"category": "base",
|
||||
"description": "Each document has an _id that uniquely identifies it",
|
||||
"example": "Y-6TfmcB0WOhS6qyMv3s",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "_id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"message": Object {
|
||||
"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.",
|
||||
"example": "Hello World",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "message",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"client": Object {
|
||||
|
@ -361,6 +390,46 @@ exports[`zeek_row_renderer renders correctly against snapshot 1`] = `
|
|||
},
|
||||
"event": Object {
|
||||
"fields": Object {
|
||||
"event.action": Object {
|
||||
"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.",
|
||||
"example": "user-password-change",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.action",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"event.category": Object {
|
||||
"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.",
|
||||
"example": "authentication",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.category",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"event.end": Object {
|
||||
"aggregatable": true,
|
||||
"category": "event",
|
||||
|
@ -381,6 +450,49 @@ exports[`zeek_row_renderer renders correctly against snapshot 1`] = `
|
|||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
"event.severity": Object {
|
||||
"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\`.",
|
||||
"example": 7,
|
||||
"format": "number",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.severity",
|
||||
"searchable": true,
|
||||
"type": "number",
|
||||
},
|
||||
},
|
||||
},
|
||||
"host": Object {
|
||||
"fields": Object {
|
||||
"host.name": Object {
|
||||
"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.",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "host.name",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"nestedField": Object {
|
||||
|
@ -461,6 +573,25 @@ exports[`zeek_row_renderer renders correctly against snapshot 1`] = `
|
|||
},
|
||||
},
|
||||
},
|
||||
"user": Object {
|
||||
"fields": Object {
|
||||
"user.name": Object {
|
||||
"aggregatable": true,
|
||||
"category": "user",
|
||||
"description": "Short name or login of the user.",
|
||||
"example": "albert",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "user.name",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
data={
|
||||
|
|
|
@ -129,6 +129,35 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
|
|||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
"_id": Object {
|
||||
"aggregatable": false,
|
||||
"category": "base",
|
||||
"description": "Each document has an _id that uniquely identifies it",
|
||||
"example": "Y-6TfmcB0WOhS6qyMv3s",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "_id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"message": Object {
|
||||
"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.",
|
||||
"example": "Hello World",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "message",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"client": Object {
|
||||
|
@ -359,6 +388,46 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
|
|||
},
|
||||
"event": Object {
|
||||
"fields": Object {
|
||||
"event.action": Object {
|
||||
"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.",
|
||||
"example": "user-password-change",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.action",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"event.category": Object {
|
||||
"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.",
|
||||
"example": "authentication",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.category",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"event.end": Object {
|
||||
"aggregatable": true,
|
||||
"category": "event",
|
||||
|
@ -379,6 +448,49 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
|
|||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
"event.severity": Object {
|
||||
"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\`.",
|
||||
"example": 7,
|
||||
"format": "number",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.severity",
|
||||
"searchable": true,
|
||||
"type": "number",
|
||||
},
|
||||
},
|
||||
},
|
||||
"host": Object {
|
||||
"fields": Object {
|
||||
"host.name": Object {
|
||||
"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.",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "host.name",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"nestedField": Object {
|
||||
|
@ -459,6 +571,25 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = `
|
|||
},
|
||||
},
|
||||
},
|
||||
"user": Object {
|
||||
"fields": Object {
|
||||
"user.name": Object {
|
||||
"aggregatable": true,
|
||||
"category": "user",
|
||||
"description": "Short name or login of the user.",
|
||||
"example": "albert",
|
||||
"format": "string",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "user.name",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
columnHeaders={
|
||||
|
|
|
@ -14,7 +14,7 @@ import { REMOVE_COLUMN } from './column_headers/translations';
|
|||
import { Direction } from '../../../../common/search_strategy';
|
||||
import { useMountAppended } from '../../utils/use_mount_appended';
|
||||
import { defaultHeaders, mockBrowserFields, mockTimelineData, TestProviders } from '../../../mock';
|
||||
import { TimelineTabs } from '../../../../common/types/timeline';
|
||||
import { ColumnHeaderOptions, TimelineTabs } from '../../../../common/types/timeline';
|
||||
import { TestCellRenderer } from '../../../mock/cell_renderer';
|
||||
import { mockGlobalState } from '../../../mock/global_state';
|
||||
import { EuiDataGridColumn } from '@elastic/eui';
|
||||
|
@ -95,6 +95,10 @@ describe('Body', () => {
|
|||
indexNames: [''],
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockDispatch.mockReset();
|
||||
});
|
||||
|
||||
describe('rendering', () => {
|
||||
test('it renders the body data grid', () => {
|
||||
const wrapper = mount(
|
||||
|
@ -330,4 +334,26 @@ describe('Body', () => {
|
|||
type: 'x-pack/timelines/t-grid/UPDATE_COLUMN_WIDTH',
|
||||
});
|
||||
});
|
||||
|
||||
test('it dispatches the `REMOVE_COLUMN` action when there is a field removed from the custom fields', async () => {
|
||||
const customFieldId = 'my.custom.runtimeField';
|
||||
const extraFieldProps = {
|
||||
...props,
|
||||
columnHeaders: [
|
||||
...defaultHeaders,
|
||||
{ id: customFieldId, category: 'my' } as ColumnHeaderOptions,
|
||||
],
|
||||
};
|
||||
render(
|
||||
<TestProviders>
|
||||
<BodyComponent {...extraFieldProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(mockDispatch).toBeCalledTimes(1);
|
||||
expect(mockDispatch).toBeCalledWith({
|
||||
payload: { columnId: customFieldId, id: 'timeline-test' },
|
||||
type: 'x-pack/timelines/t-grid/REMOVE_COLUMN',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
EuiFlexItem,
|
||||
EuiProgress,
|
||||
} from '@elastic/eui';
|
||||
import { getOr } from 'lodash/fp';
|
||||
import { getOr, isEmpty } from 'lodash/fp';
|
||||
import memoizeOne from 'memoize-one';
|
||||
import React, {
|
||||
ComponentType,
|
||||
|
@ -393,6 +393,20 @@ export const BodyComponent = React.memo<StatefulBodyProps>(
|
|||
}
|
||||
}, [isSelectAllChecked, onSelectPage, selectAll]);
|
||||
|
||||
// Clean any removed custom field that may still be present in stored columnHeaders
|
||||
useEffect(() => {
|
||||
if (!isEmpty(browserFields) && !isEmpty(columnHeaders)) {
|
||||
columnHeaders.forEach(({ id: columnId }) => {
|
||||
if (browserFields.base?.fields?.[columnId] == null) {
|
||||
const [category] = columnId.split('.');
|
||||
if (browserFields[category]?.fields?.[columnId] == null) {
|
||||
dispatch(tGridActions.removeColumn({ id, columnId }));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [browserFields, columnHeaders, dispatch, id]);
|
||||
|
||||
const onAlertStatusActionSuccess = useMemo(() => {
|
||||
if (bulkActions && bulkActions !== true) {
|
||||
return bulkActions.onAlertStatusActionSuccess;
|
||||
|
|
|
@ -232,6 +232,20 @@ describe('helpers', () => {
|
|||
},
|
||||
},
|
||||
},
|
||||
base: {
|
||||
fields: {
|
||||
_id: {
|
||||
category: 'base',
|
||||
description: 'Each document has an _id that uniquely identifies it',
|
||||
example: 'Y-6TfmcB0WOhS6qyMv3s',
|
||||
name: '_id',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: false,
|
||||
indexes: ['auditbeat', 'filebeat', 'packetbeat'],
|
||||
},
|
||||
},
|
||||
},
|
||||
cloud: {
|
||||
fields: {
|
||||
'cloud.account.id': {
|
||||
|
|
|
@ -121,7 +121,7 @@ describe('Search', () => {
|
|||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="categories-count"]').first().text()).toEqual(
|
||||
'10 categories'
|
||||
'12 categories'
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -154,6 +154,6 @@ describe('Search', () => {
|
|||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="fields-count"]').first().text()).toEqual('27 fields');
|
||||
expect(wrapper.find('[data-test-subj="fields-count"]').first().text()).toEqual('34 fields');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -471,6 +471,28 @@ export const mockBrowserFields: BrowserFields = {
|
|||
searchable: true,
|
||||
type: 'date',
|
||||
},
|
||||
_id: {
|
||||
category: 'base',
|
||||
description: 'Each document has an _id that uniquely identifies it',
|
||||
example: 'Y-6TfmcB0WOhS6qyMv3s',
|
||||
name: '_id',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: false,
|
||||
indexes: ['auditbeat', 'filebeat', 'packetbeat'],
|
||||
},
|
||||
message: {
|
||||
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.',
|
||||
example: 'Hello World',
|
||||
name: 'message',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: false,
|
||||
format: 'string',
|
||||
indexes: ['auditbeat', 'filebeat', 'packetbeat'],
|
||||
},
|
||||
},
|
||||
},
|
||||
client: {
|
||||
|
@ -661,6 +683,57 @@ export const mockBrowserFields: BrowserFields = {
|
|||
type: 'date',
|
||||
aggregatable: true,
|
||||
},
|
||||
'event.action': {
|
||||
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.',
|
||||
example: 'user-password-change',
|
||||
name: 'event.action',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
format: 'string',
|
||||
indexes: DEFAULT_INDEX_PATTERN,
|
||||
},
|
||||
'event.category': {
|
||||
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.',
|
||||
example: 'authentication',
|
||||
name: 'event.category',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
format: 'string',
|
||||
indexes: DEFAULT_INDEX_PATTERN,
|
||||
},
|
||||
'event.severity': {
|
||||
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`.",
|
||||
example: 7,
|
||||
name: 'event.severity',
|
||||
type: 'number',
|
||||
format: 'number',
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
indexes: DEFAULT_INDEX_PATTERN,
|
||||
},
|
||||
},
|
||||
},
|
||||
host: {
|
||||
fields: {
|
||||
'host.name': {
|
||||
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.',
|
||||
name: 'host.name',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
format: 'string',
|
||||
indexes: DEFAULT_INDEX_PATTERN,
|
||||
},
|
||||
},
|
||||
},
|
||||
source: {
|
||||
|
@ -689,6 +762,21 @@ export const mockBrowserFields: BrowserFields = {
|
|||
},
|
||||
},
|
||||
},
|
||||
user: {
|
||||
fields: {
|
||||
'user.name': {
|
||||
category: 'user',
|
||||
description: 'Short name or login of the user.',
|
||||
example: 'albert',
|
||||
name: 'user.name',
|
||||
type: 'string',
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
format: 'string',
|
||||
indexes: ['auditbeat', 'filebeat', 'packetbeat'],
|
||||
},
|
||||
},
|
||||
},
|
||||
nestedField: {
|
||||
fields: {
|
||||
'nestedField.firstAttributes': {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue