From e5d53163d8de232a409aa52585509f0f4e80d72d Mon Sep 17 00:00:00 2001 From: Jatin Kathuria Date: Wed, 10 Aug 2022 15:27:28 +0200 Subject: [PATCH 1/2] fix for adding empty value to the timeline --- .../investigate_in_timeline.spec.ts | 34 +++++++++++++++++-- .../cypress/screens/timeline.ts | 15 ++++++++ .../security_solution/cypress/tasks/alerts.ts | 10 +++++- .../cypress/tasks/timeline.ts | 5 +++ .../lib/cell_actions/add_to_timeline.tsx | 34 ++++++++++++++----- 5 files changed, 87 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/investigate_in_timeline.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/investigate_in_timeline.spec.ts index 5f52041e75d17..5d8099c84d5a6 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/investigate_in_timeline.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/investigate_in_timeline.spec.ts @@ -6,13 +6,23 @@ */ import { getNewRule } from '../../objects/rule'; -import { PROVIDER_BADGE } from '../../screens/timeline'; +import { + ALERT_TABLE_FILE_NAME_HEADER, + ALERT_TABLE_FILE_NAME_VALUES, + ALERT_TABLE_SEVERITY_VALUES, + PROVIDER_BADGE, +} from '../../screens/timeline'; -import { investigateFirstAlertInTimeline } from '../../tasks/alerts'; +import { + addAlertPropertyToTimeline, + investigateFirstAlertInTimeline, + scrollAlertTableColumnIntoView, +} from '../../tasks/alerts'; import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; import { login, visit } from '../../tasks/login'; +import { openActiveTimeline } from '../../tasks/timeline'; import { ALERTS_URL } from '../../urls/navigation'; @@ -37,4 +47,24 @@ describe('Alerts timeline', () => { cy.get(PROVIDER_BADGE).filter(':visible').should('have.text', eventId); }); }); + + it('Add a non-empty property to default timeline', () => { + cy.get(ALERT_TABLE_SEVERITY_VALUES) + .first() + .invoke('text') + .then((severityVal) => { + addAlertPropertyToTimeline(ALERT_TABLE_SEVERITY_VALUES, 0); + openActiveTimeline(); + cy.get(PROVIDER_BADGE) + .first() + .should('have.text', `kibana.alert.severity: "${severityVal}"`); + }); + }); + + it('Add an empty property to default timeline', () => { + scrollAlertTableColumnIntoView(ALERT_TABLE_FILE_NAME_HEADER); + addAlertPropertyToTimeline(ALERT_TABLE_FILE_NAME_VALUES, 0); + openActiveTimeline(); + cy.get(PROVIDER_BADGE).first().should('have.text', 'NOT file.name exists'); + }); }); diff --git a/x-pack/plugins/security_solution/cypress/screens/timeline.ts b/x-pack/plugins/security_solution/cypress/screens/timeline.ts index 231c24b3127f3..1c6e41da02c1d 100644 --- a/x-pack/plugins/security_solution/cypress/screens/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/screens/timeline.ts @@ -267,3 +267,18 @@ export const USER_KPI = '[data-test-subj="siem-timeline-user-kpi"]'; export const EDIT_TIMELINE_BTN = '[data-test-subj="save-timeline-button-icon"]'; export const EDIT_TIMELINE_TOOLTIP = '[data-test-subj="save-timeline-btn-tooltip"]'; + +export const ALERT_TABLE_SEVERITY_VALUES = + '[data-test-subj="formatted-field-kibana.alert.severity"]'; + +export const ALERT_TABLE_FILE_NAME_HEADER = '[data-gridcell-column-id="file.name"]'; + +export const ALERT_TABLE_FILE_NAME_VALUES = + '[data-gridcell-column-id="file.name"][data-test-subj="dataGridRowCell"]'; // empty column for the test data + +export const ALERT_TABLE_CELL_ACTIONS_ADD_TO_TIMELINE = '[data-test-subj="add-to-timeline"]'; + +export const ACTIVE_TIMELINE_BOTTOM_BAR = + '[data-test-subj="flyoutBottomBar"] .active-timeline-button'; + +export const DATA_GRID_BODY = '[data-test-subj=body-data-grid] .euiDataGrid__virtualized'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts.ts index 94a4529549a6d..16f8568bf7a82 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts.ts @@ -26,7 +26,10 @@ import { TIMELINE_CONTEXT_MENU_BTN, } from '../screens/alerts'; import { REFRESH_BUTTON } from '../screens/security_header'; -import { TIMELINE_COLUMN_SPINNER } from '../screens/timeline'; +import { + ALERT_TABLE_CELL_ACTIONS_ADD_TO_TIMELINE, + TIMELINE_COLUMN_SPINNER, +} from '../screens/timeline'; import { UPDATE_ENRICHMENT_RANGE_BUTTON, ENRICHMENT_QUERY_END_INPUT, @@ -152,6 +155,11 @@ export const investigateFirstAlertInTimeline = () => { cy.get(SEND_ALERT_TO_TIMELINE_BTN).first().click({ force: true }); }; +export const addAlertPropertyToTimeline = (propertySelector: string, rowIndex: number) => { + cy.get(propertySelector).eq(rowIndex).trigger('mouseover'); + cy.get(ALERT_TABLE_CELL_ACTIONS_ADD_TO_TIMELINE).first().click({ force: true }); +}; + export const waitForAlerts = () => { cy.get(REFRESH_BUTTON).should('not.have.attr', 'aria-label', 'Needs updating'); }; diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts index 5403c4f95bf54..01d10c627af85 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts @@ -69,6 +69,7 @@ import { TIMELINE_TAB_CONTENT_EQL, TIMESTAMP_HOVER_ACTION_OVERFLOW_BTN, TIMELINE_DATA_PROVIDER_FIELD_INPUT, + ACTIVE_TIMELINE_BOTTOM_BAR, } from '../screens/timeline'; import { REFRESH_BUTTON, TIMELINE } from '../screens/timelines'; @@ -296,6 +297,10 @@ export const openTimelineById = (timelineId: string): Cypress.Chainable { + cy.get(ACTIVE_TIMELINE_BOTTOM_BAR).click({ force: true }); +}; + export const pinFirstEvent = (): Cypress.Chainable> => { return cy.get(PIN_EVENT).first().click({ force: true }); }; diff --git a/x-pack/plugins/security_solution/public/common/lib/cell_actions/add_to_timeline.tsx b/x-pack/plugins/security_solution/public/common/lib/cell_actions/add_to_timeline.tsx index 4e944072fefef..b882c16722d69 100644 --- a/x-pack/plugins/security_solution/public/common/lib/cell_actions/add_to_timeline.tsx +++ b/x-pack/plugins/security_solution/public/common/lib/cell_actions/add_to_timeline.tsx @@ -12,7 +12,10 @@ import type { TimelineNonEcsData } from '@kbn/timelines-plugin/common/search_str import type { DataProvider } from '@kbn/timelines-plugin/common/types'; import { getPageRowIndex } from '@kbn/timelines-plugin/public'; import { useGetMappedNonEcsValue } from '../../../timelines/components/timeline/body/data_driven_columns'; -import { IS_OPERATOR } from '../../../timelines/components/timeline/data_providers/data_provider'; +import { + EXISTS_OPERATOR, + IS_OPERATOR, +} from '../../../timelines/components/timeline/data_providers/data_provider'; import { escapeDataProviderId } from '../../components/drag_and_drop/helpers'; import { EmptyComponent, useKibanaServices } from './helpers'; @@ -41,23 +44,38 @@ export const getAddToTimelineCellAction = ({ [timelines] ); - const dataProvider: DataProvider[] = useMemo( - () => + const dataProvider: DataProvider[] = useMemo(() => { + const queryIdPrefix = `${escapeDataProviderId(columnId)}-row-${rowIndex}-col-${columnId}`; + return ( value?.map((x) => ({ and: [], enabled: true, - id: `${escapeDataProviderId(columnId)}-row-${rowIndex}-col-${columnId}-val-${x}`, - name: x, excluded: false, kqlQuery: '', + id: `${queryIdPrefix}-val-${x}`, + name: x, queryMatch: { field: columnId, value: x, operator: IS_OPERATOR, }, - })) ?? [], - [columnId, rowIndex, value] - ); + })) ?? [ + { + and: [], + enabled: true, + kqlQuery: '', + id: `${queryIdPrefix}`, + name: '', + excluded: true, + queryMatch: { + field: columnId, + value: '', + operator: EXISTS_OPERATOR, + }, + }, + ] + ); + }, [columnId, rowIndex, value]); const addToTimelineProps = useMemo(() => { return { Component, From 92f0094993f1953ec5dd60d607b75ffd87d997c1 Mon Sep 17 00:00:00 2001 From: Jatin Kathuria Date: Wed, 10 Aug 2022 16:29:33 +0200 Subject: [PATCH 2/2] more readable code --- .../lib/cell_actions/add_to_timeline.tsx | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/lib/cell_actions/add_to_timeline.tsx b/x-pack/plugins/security_solution/public/common/lib/cell_actions/add_to_timeline.tsx index b882c16722d69..54956f6304576 100644 --- a/x-pack/plugins/security_solution/public/common/lib/cell_actions/add_to_timeline.tsx +++ b/x-pack/plugins/security_solution/public/common/lib/cell_actions/add_to_timeline.tsx @@ -46,20 +46,8 @@ export const getAddToTimelineCellAction = ({ const dataProvider: DataProvider[] = useMemo(() => { const queryIdPrefix = `${escapeDataProviderId(columnId)}-row-${rowIndex}-col-${columnId}`; - return ( - value?.map((x) => ({ - and: [], - enabled: true, - excluded: false, - kqlQuery: '', - id: `${queryIdPrefix}-val-${x}`, - name: x, - queryMatch: { - field: columnId, - value: x, - operator: IS_OPERATOR, - }, - })) ?? [ + if (!value) { + return [ { and: [], enabled: true, @@ -73,8 +61,21 @@ export const getAddToTimelineCellAction = ({ operator: EXISTS_OPERATOR, }, }, - ] - ); + ]; + } + return value.map((x) => ({ + and: [], + enabled: true, + excluded: false, + kqlQuery: '', + id: `${queryIdPrefix}-val-${x}`, + name: x, + queryMatch: { + field: columnId, + value: x, + operator: IS_OPERATOR, + }, + })); }, [columnId, rowIndex, value]); const addToTimelineProps = useMemo(() => { return {