mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Security Solutions] Refactor search bar to use global query string (#135210)
* Refactor search bar to use global query string * Fix ml redirect bug where query string was overwritten * Fix cypress test * Fix pinned filters removed on security solution
This commit is contained in:
parent
17a2bcc828
commit
c3c41c9d18
31 changed files with 798 additions and 362 deletions
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { login, visitWithoutDateRange } from '../../tasks/login';
|
||||
|
||||
import {
|
||||
GLOBAL_SEARCH_BAR_FILTER_ITEM,
|
||||
GLOBAL_SEARCH_BAR_PINNED_FILTER,
|
||||
} from '../../screens/search_bar';
|
||||
import { DISCOVER_WITH_FILTER_URL, DISCOVER_WITH_PINNED_FILTER_URL } from '../../urls/navigation';
|
||||
import {
|
||||
navigateFromKibanaCollapsibleTo,
|
||||
openKibanaNavigation,
|
||||
} from '../../tasks/kibana_navigation';
|
||||
import { ALERTS_PAGE } from '../../screens/kibana_navigation';
|
||||
|
||||
describe('pinned filters', () => {
|
||||
before(() => {
|
||||
login();
|
||||
});
|
||||
|
||||
it('show pinned filters on security', () => {
|
||||
visitWithoutDateRange(DISCOVER_WITH_PINNED_FILTER_URL);
|
||||
|
||||
cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).find(GLOBAL_SEARCH_BAR_PINNED_FILTER).should('exist');
|
||||
openKibanaNavigation();
|
||||
navigateFromKibanaCollapsibleTo(ALERTS_PAGE);
|
||||
|
||||
cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).should('have.text', 'host.name: test-host');
|
||||
});
|
||||
|
||||
it('does not show discover filters on security', () => {
|
||||
visitWithoutDateRange(DISCOVER_WITH_FILTER_URL);
|
||||
cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).should('exist');
|
||||
|
||||
openKibanaNavigation();
|
||||
navigateFromKibanaCollapsibleTo(ALERTS_PAGE);
|
||||
|
||||
cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).should('not.exist');
|
||||
});
|
||||
});
|
|
@ -105,7 +105,7 @@ describe('ml conditional links', () => {
|
|||
visitWithoutDateRange(mlNetworkSingleIpKqlQuery);
|
||||
cy.url().should(
|
||||
'include',
|
||||
'/app/security/network/ip/127.0.0.1/source?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))'
|
||||
'/app/security/network/ip/127.0.0.1/source?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)'
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -113,7 +113,7 @@ describe('ml conditional links', () => {
|
|||
visitWithoutDateRange(mlNetworkMultipleIpNullKqlQuery);
|
||||
cy.url().should(
|
||||
'include',
|
||||
'app/security/network/flows?query=(language:kuery,query:%27((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))'
|
||||
'app/security/network/flows?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&query=(language:kuery,query:%27((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%27)'
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -121,7 +121,7 @@ describe('ml conditional links', () => {
|
|||
visitWithoutDateRange(mlNetworkMultipleIpKqlQuery);
|
||||
cy.url().should(
|
||||
'include',
|
||||
'/app/security/network/flows?query=(language:kuery,query:%27((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))'
|
||||
'/app/security/network/flows?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&query=(language:kuery,query:%27((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))%27)'
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -138,7 +138,7 @@ describe('ml conditional links', () => {
|
|||
|
||||
cy.url().should(
|
||||
'include',
|
||||
`/app/security/network/flows?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))`
|
||||
`/app/security/network/flows?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)`
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -162,7 +162,7 @@ describe('ml conditional links', () => {
|
|||
visitWithoutDateRange(mlHostSingleHostKqlQuery);
|
||||
cy.url().should(
|
||||
'include',
|
||||
'/app/security/hosts/siem-windows/anomalies?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))'
|
||||
'/app/security/hosts/siem-windows/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)'
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -170,7 +170,7 @@ describe('ml conditional links', () => {
|
|||
visitWithoutDateRange(mlHostMultiHostNullKqlQuery);
|
||||
cy.url().should(
|
||||
'include',
|
||||
'/app/security/hosts/anomalies?query=(language:kuery,query:%27(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))'
|
||||
'/app/security/hosts/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&query=(language:kuery,query:%27(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%27)'
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -178,7 +178,7 @@ describe('ml conditional links', () => {
|
|||
visitWithoutDateRange(mlHostMultiHostKqlQuery);
|
||||
cy.url().should(
|
||||
'include',
|
||||
'/app/security/hosts/anomalies?query=(language:kuery,query:%27(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))'
|
||||
'/app/security/hosts/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&query=(language:kuery,query:%27(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))%27)'
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -194,7 +194,7 @@ describe('ml conditional links', () => {
|
|||
visitWithoutDateRange(mlHostVariableHostKqlQuery);
|
||||
cy.url().should(
|
||||
'include',
|
||||
'/app/security/hosts/anomalies?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))'
|
||||
'/app/security/hosts/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -30,7 +30,12 @@ import { openFirstHostDetails, waitForAllHostsToBeLoaded } from '../../tasks/hos
|
|||
import { openAllHosts } from '../../tasks/hosts/main';
|
||||
|
||||
import { waitForIpsTableToBeLoaded } from '../../tasks/network/flows';
|
||||
import { clearSearchBar, kqlSearch, navigateFromHeaderTo } from '../../tasks/security_header';
|
||||
import {
|
||||
clearSearchBar,
|
||||
kqlSearch,
|
||||
navigateFromHeaderTo,
|
||||
saveQuery,
|
||||
} from '../../tasks/security_header';
|
||||
import { openTimelineUsingToggle } from '../../tasks/security_main';
|
||||
import { addNameToTimeline, closeTimeline, populateTimeline } from '../../tasks/timeline';
|
||||
|
||||
|
@ -39,6 +44,10 @@ import { ABSOLUTE_DATE_RANGE } from '../../urls/state';
|
|||
|
||||
import { getTimeline } from '../../objects/timeline';
|
||||
import { TIMELINE } from '../../screens/create_new_case';
|
||||
import {
|
||||
GLOBAL_SEARCH_BAR_FILTER_ITEM_AT,
|
||||
GLOBAL_SEARCH_BAR_PINNED_FILTER,
|
||||
} from '../../screens/search_bar';
|
||||
|
||||
const ABSOLUTE_DATE = {
|
||||
endTime: 'Aug 1, 2019 @ 20:33:29.186',
|
||||
|
@ -58,6 +67,29 @@ describe('url state', () => {
|
|||
login();
|
||||
});
|
||||
|
||||
it('sets filters from the url', () => {
|
||||
visitWithoutDateRange(ABSOLUTE_DATE_RANGE.urlFiltersHostsHosts);
|
||||
|
||||
cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM_AT(0)).should('have.text', 'host.name: test-host');
|
||||
cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM_AT(0))
|
||||
.find(GLOBAL_SEARCH_BAR_PINNED_FILTER)
|
||||
.should('exist');
|
||||
cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM_AT(1)).should('have.text', 'host.os.name: test-os');
|
||||
});
|
||||
|
||||
it('sets saved query from the url', () => {
|
||||
visitWithoutDateRange(ABSOLUTE_DATE_RANGE.urlFiltersHostsHosts);
|
||||
saveQuery('test-query');
|
||||
// refresh the page to force loading the saved query from the URL
|
||||
cy.reload();
|
||||
|
||||
cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM_AT(0)).should('have.text', 'host.name: test-host');
|
||||
cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM_AT(0))
|
||||
.find(GLOBAL_SEARCH_BAR_PINNED_FILTER)
|
||||
.should('exist');
|
||||
cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM_AT(1)).should('have.text', 'host.os.name: test-os');
|
||||
});
|
||||
|
||||
it('sets the global start and end dates from the url', () => {
|
||||
visitWithoutDateRange(ABSOLUTE_DATE_RANGE.url);
|
||||
cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON).should(
|
||||
|
@ -184,7 +216,7 @@ describe('url state', () => {
|
|||
cy.get(NETWORK).should(
|
||||
'have.attr',
|
||||
'href',
|
||||
`/app/security/network?query=(language:kuery,query:'source.ip:%20%2210.142.0.9%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2019-08-01T20:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2019-08-01T20:33:29.186Z')))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))`
|
||||
`/app/security/network?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2019-08-01T20:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2019-08-01T20:33:29.186Z')))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&query=(language:kuery,query:'source.ip:%20%2210.142.0.9%22%20')`
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -197,12 +229,12 @@ describe('url state', () => {
|
|||
cy.get(HOSTS).should(
|
||||
'have.attr',
|
||||
'href',
|
||||
`/app/security/hosts?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))`
|
||||
`/app/security/hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')`
|
||||
);
|
||||
cy.get(NETWORK).should(
|
||||
'have.attr',
|
||||
'href',
|
||||
`/app/security/network?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))`
|
||||
`/app/security/network?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')`
|
||||
);
|
||||
cy.get(HOSTS_NAMES).first().should('have.text', 'siem-kibana');
|
||||
|
||||
|
@ -221,14 +253,14 @@ describe('url state', () => {
|
|||
.should(
|
||||
'have.attr',
|
||||
'href',
|
||||
`/app/security/hosts?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))`
|
||||
`/app/security/hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')`
|
||||
);
|
||||
cy.get(BREADCRUMBS)
|
||||
.eq(2)
|
||||
.should(
|
||||
'have.attr',
|
||||
'href',
|
||||
`/app/security/hosts/siem-kibana?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))`
|
||||
`/app/security/hosts/siem-kibana?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')`
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -28,3 +28,7 @@ export const ADD_FILTER_FORM_FILTER_VALUE_INPUT = '[data-test-subj="filterParams
|
|||
export const ADD_FILTER_FORM_SAVE_BUTTON = '[data-test-subj="saveFilter"]';
|
||||
|
||||
export const GLOBAL_SEARCH_BAR_FILTER_ITEM = '#popoverFor_filter0';
|
||||
|
||||
export const GLOBAL_SEARCH_BAR_FILTER_ITEM_AT = (value: number) => `#popoverFor_filter${value}`;
|
||||
|
||||
export const GLOBAL_SEARCH_BAR_PINNED_FILTER = '.globalFilterItem-isPinned';
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { TOASTER } from '../screens/alerts_detection_rules';
|
||||
import { KQL_INPUT, REFRESH_BUTTON } from '../screens/security_header';
|
||||
|
||||
export const clearSearchBar = () => {
|
||||
|
@ -25,3 +26,14 @@ export const refreshPage = () => {
|
|||
.click({ force: true })
|
||||
.should('not.have.attr', 'aria-label', 'Needs updating');
|
||||
};
|
||||
|
||||
export const saveQuery = (name: string) => {
|
||||
const random = Math.floor(Math.random() * 100000);
|
||||
const queryName = `${name}-${random}`;
|
||||
cy.get('div[data-test-subj="globalDatePicker"] [data-test-subj="queryBarMenuPopover"]').click();
|
||||
cy.get('[data-test-subj="saved-query-management-save-button"]').click();
|
||||
cy.get('[data-test-subj="saveQueryFormTitle"]').type(queryName);
|
||||
cy.get('[data-test-subj="savedQueryFormSaveButton"]').click();
|
||||
cy.get(TOASTER).should('have.text', `Your query "${queryName}" was saved`);
|
||||
return queryName;
|
||||
};
|
||||
|
|
|
@ -50,3 +50,8 @@ export const RULE_CREATION = 'app/security/rules/create';
|
|||
export const TIMELINES_URL = '/app/security/timelines';
|
||||
export const TIMELINE_TEMPLATES_URL = '/app/security/timelines/template';
|
||||
export const LOGOUT_URL = '/logout';
|
||||
|
||||
export const DISCOVER_WITH_FILTER_URL =
|
||||
"/app/discover#/?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now%2Fd,to:now%2Fd))&_a=(columns:!(),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:security-solution-default,key:host.name,negate:!f,params:(query:test-host),type:phrase),query:(match_phrase:(host.name:test-host)))),index:security-solution-default,interval:auto,query:(language:kuery,query:''),sort:!(!('@timestamp',desc)))";
|
||||
export const DISCOVER_WITH_PINNED_FILTER_URL =
|
||||
"/app/discover#/?_g=(filters:!(('$state':(store:globalState),meta:(alias:!n,disabled:!f,index:security-solution-default,key:host.name,negate:!f,params:(query:test-host),type:phrase),query:(match_phrase:(host.name:test-host)))),refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(columns:!(),filters:!(),index:security-solution-default,interval:auto,query:(language:kuery,query:''),sort:!(!('@timestamp',desc)))";
|
||||
|
|
|
@ -20,4 +20,7 @@ export const ABSOLUTE_DATE_RANGE = {
|
|||
'/app/security/hosts/allHosts?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-01T20:03:29.186Z%27,kind:absolute,to:%272019-08-01T20:33:29.186Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-01T20:03:29.186Z%27,kind:absolute,to:%272019-08-01T20:33:29.186Z%27)))',
|
||||
urlHostNew:
|
||||
'/app/security/hosts/allHosts?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-01T20:03:29.186Z%27,kind:absolute,to:%272023-01-01T21:33:29.186Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-01T20:03:29.186Z%27,kind:absolute,to:%272023-01-01T21:33:29.186Z%27)))',
|
||||
|
||||
urlFiltersHostsHosts:
|
||||
'/app/security/hosts/allHosts?filters=!((%27$state%27:(store:globalState),meta:(alias:!n,disabled:!f,key:host.name,negate:!f,params:(query:test-host),type:phrase),query:(match_phrase:(host.name:(query:test-host)))),(%27$state%27:(store:appState),meta:(alias:!n,disabled:!f,key:host.os.name,negate:!f,params:(query:test-os),type:phrase),query:(match_phrase:(host.os.name:(query:test-os)))))',
|
||||
};
|
||||
|
|
294
x-pack/plugins/security_solution/public/app/home/index.test.tsx
Normal file
294
x-pack/plugins/security_solution/public/app/home/index.test.tsx
Normal file
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
* 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 { render, waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { HomePage } from '.';
|
||||
import type { SavedQuery } from '@kbn/data-plugin/public';
|
||||
import { FilterManager } from '@kbn/data-plugin/public';
|
||||
import { CONSTANTS } from '../../common/components/url_state/constants';
|
||||
|
||||
import {
|
||||
createSecuritySolutionStorageMock,
|
||||
kibanaObservable,
|
||||
mockGlobalState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
TestProviders,
|
||||
} from '../../common/mock';
|
||||
import { inputsActions } from '../../common/store/inputs';
|
||||
import { setSearchBarFilter } from '../../common/store/inputs/actions';
|
||||
import { coreMock } from '@kbn/core/public/mocks';
|
||||
import type { Filter } from '@kbn/es-query';
|
||||
import { createStore } from '../../common/store';
|
||||
|
||||
jest.mock('../../common/store/inputs/actions');
|
||||
|
||||
const DummyComponent = ({ children }: { children: React.ReactNode }) => <>{children}</>;
|
||||
|
||||
const mockedUseInitializeUrlParam = jest.fn();
|
||||
|
||||
const mockUseInitializeUrlParam = (urlParamKey: string, state: unknown) => {
|
||||
mockedUseInitializeUrlParam.mockImplementation((key, fn) => {
|
||||
if (urlParamKey === key) {
|
||||
fn(state);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
jest.mock('../../common/utils/global_query_string', () => {
|
||||
const original = jest.requireActual('../../common/utils/global_query_string');
|
||||
return {
|
||||
...original,
|
||||
useInitializeUrlParam: (...params: unknown[]) => mockedUseInitializeUrlParam(...params),
|
||||
useSyncGlobalQueryString: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../common/components/drag_and_drop/drag_drop_context_wrapper', () => ({
|
||||
DragDropContextWrapper: DummyComponent,
|
||||
}));
|
||||
jest.mock('./template_wrapper', () => ({
|
||||
SecuritySolutionTemplateWrapper: DummyComponent,
|
||||
}));
|
||||
|
||||
jest.mock('react-router-dom', () => {
|
||||
const original = jest.requireActual('react-router-dom');
|
||||
return {
|
||||
...original,
|
||||
useLocation: jest.fn().mockReturnValue({ pathname: '/test', search: '?' }),
|
||||
};
|
||||
});
|
||||
|
||||
const mockedFilterManager = new FilterManager(coreMock.createStart().uiSettings);
|
||||
const mockGetSavedQuery = jest.fn();
|
||||
|
||||
const dummyFilter: Filter = {
|
||||
meta: {
|
||||
alias: null,
|
||||
negate: false,
|
||||
disabled: false,
|
||||
type: 'phrase',
|
||||
key: 'dummy',
|
||||
params: {
|
||||
query: 'value',
|
||||
},
|
||||
},
|
||||
query: {
|
||||
term: {
|
||||
dummy: 'value',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
jest.mock('../../common/lib/kibana', () => {
|
||||
const original = jest.requireActual('../../common/lib/kibana');
|
||||
return {
|
||||
...original,
|
||||
useKibana: () => ({
|
||||
...original.useKibana(),
|
||||
services: {
|
||||
...original.useKibana().services,
|
||||
data: {
|
||||
...original.useKibana().services.data,
|
||||
query: {
|
||||
...original.useKibana().services.data.query,
|
||||
filterManager: mockedFilterManager,
|
||||
savedQueries: { getSavedQuery: mockGetSavedQuery },
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const mockDispatch = jest.fn();
|
||||
|
||||
jest.mock('react-redux', () => {
|
||||
const original = jest.requireActual('react-redux');
|
||||
return {
|
||||
...original,
|
||||
useDispatch: () => mockDispatch,
|
||||
};
|
||||
});
|
||||
|
||||
describe('HomePage', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockedFilterManager.setFilters([]);
|
||||
});
|
||||
|
||||
it('calls useInitializeUrlParam for appQuery, filters and savedQuery', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<HomePage onAppLeave={jest.fn()} setHeaderActionMenu={jest.fn()}>
|
||||
<span />
|
||||
</HomePage>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(mockedUseInitializeUrlParam).toHaveBeenCalledWith(
|
||||
CONSTANTS.appQuery,
|
||||
expect.any(Function)
|
||||
);
|
||||
expect(mockedUseInitializeUrlParam).toHaveBeenCalledWith(
|
||||
CONSTANTS.filters,
|
||||
expect.any(Function)
|
||||
);
|
||||
expect(mockedUseInitializeUrlParam).toHaveBeenCalledWith(
|
||||
CONSTANTS.savedQuery,
|
||||
expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('dispatches setFilterQuery when initializing appQuery', () => {
|
||||
const state = { query: 'testQuery', language: 'en' };
|
||||
mockUseInitializeUrlParam(CONSTANTS.appQuery, state);
|
||||
|
||||
render(
|
||||
<TestProviders>
|
||||
<HomePage onAppLeave={jest.fn()} setHeaderActionMenu={jest.fn()}>
|
||||
<span />
|
||||
</HomePage>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(mockDispatch).toHaveBeenCalledWith(
|
||||
inputsActions.setFilterQuery({
|
||||
id: 'global',
|
||||
query: state.query,
|
||||
language: state.language,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('initializes saved query store', async () => {
|
||||
const state = 'test-query-id';
|
||||
const savedQueryData: SavedQuery = {
|
||||
id: 'testSavedquery',
|
||||
attributes: {
|
||||
title: 'testtitle',
|
||||
description: 'testDescription',
|
||||
query: { query: 'testQuery', language: 'testLanguage' },
|
||||
filters: [
|
||||
{
|
||||
meta: {
|
||||
alias: null,
|
||||
negate: false,
|
||||
disabled: false,
|
||||
},
|
||||
query: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
mockGetSavedQuery.mockResolvedValue(savedQueryData);
|
||||
mockUseInitializeUrlParam(CONSTANTS.savedQuery, state);
|
||||
|
||||
render(
|
||||
<TestProviders>
|
||||
<HomePage onAppLeave={jest.fn()} setHeaderActionMenu={jest.fn()}>
|
||||
<span />
|
||||
</HomePage>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockDispatch).toHaveBeenCalledWith(
|
||||
inputsActions.setSavedQuery({ id: 'global', savedQuery: savedQueryData })
|
||||
);
|
||||
|
||||
expect(mockDispatch).toHaveBeenCalledWith(
|
||||
inputsActions.setFilterQuery({
|
||||
id: 'global',
|
||||
...savedQueryData.attributes.query,
|
||||
})
|
||||
);
|
||||
expect(setSearchBarFilter).toHaveBeenCalledWith({
|
||||
id: 'global',
|
||||
filters: savedQueryData.attributes.filters,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Filters', () => {
|
||||
it('sets filter initial value in the store and filterManager', () => {
|
||||
const state = [{ testFilter: 'test' }];
|
||||
mockUseInitializeUrlParam(CONSTANTS.filters, state);
|
||||
const spySetFilters = jest.spyOn(mockedFilterManager, 'setFilters');
|
||||
|
||||
render(
|
||||
<TestProviders>
|
||||
<HomePage onAppLeave={jest.fn()} setHeaderActionMenu={jest.fn()}>
|
||||
<span />
|
||||
</HomePage>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(setSearchBarFilter).toHaveBeenCalledWith({
|
||||
id: 'global',
|
||||
filters: state,
|
||||
});
|
||||
|
||||
expect(spySetFilters).toHaveBeenCalledWith(state);
|
||||
});
|
||||
|
||||
it('sets filter from store when URL param has no value', () => {
|
||||
const state = null;
|
||||
mockUseInitializeUrlParam(CONSTANTS.filters, state);
|
||||
const spySetAppFilters = jest.spyOn(mockedFilterManager, 'setAppFilters');
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
|
||||
const mockstate = {
|
||||
...mockGlobalState,
|
||||
inputs: {
|
||||
...mockGlobalState.inputs,
|
||||
global: {
|
||||
...mockGlobalState.inputs.global,
|
||||
filters: [dummyFilter],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const mockStore = createStore(mockstate, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
|
||||
render(
|
||||
<TestProviders store={mockStore}>
|
||||
<HomePage onAppLeave={jest.fn()} setHeaderActionMenu={jest.fn()}>
|
||||
<span />
|
||||
</HomePage>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(spySetAppFilters).toHaveBeenCalledWith([dummyFilter]);
|
||||
});
|
||||
|
||||
it('preserves pinned filters when URL param has no value', () => {
|
||||
const state = null;
|
||||
mockUseInitializeUrlParam(CONSTANTS.filters, state);
|
||||
// pin filter
|
||||
mockedFilterManager.setGlobalFilters([dummyFilter]);
|
||||
|
||||
render(
|
||||
<TestProviders>
|
||||
<HomePage onAppLeave={jest.fn()} setHeaderActionMenu={jest.fn()}>
|
||||
<span />
|
||||
</HomePage>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(mockedFilterManager.getFilters()).toEqual([
|
||||
{
|
||||
...dummyFilter,
|
||||
$state: {
|
||||
store: 'globalState',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -24,6 +24,8 @@ import { GlobalHeader } from './global_header';
|
|||
import { SecuritySolutionTemplateWrapper } from './template_wrapper';
|
||||
import { ConsoleManager } from '../../management/components/console/components/console_manager';
|
||||
import { useSyncGlobalQueryString } from '../../common/utils/global_query_string';
|
||||
import { useInitSearchBarUrlParams } from '../../common/hooks/search_bar/use_init_search_bar_url_params';
|
||||
|
||||
interface HomePageProps {
|
||||
children: React.ReactNode;
|
||||
onAppLeave: (handler: AppLeaveHandler) => void;
|
||||
|
@ -38,6 +40,7 @@ const HomePageComponent: React.FC<HomePageProps> = ({
|
|||
const { pathname } = useLocation();
|
||||
useSyncGlobalQueryString();
|
||||
useInitSourcerer(getScopeFromPath(pathname));
|
||||
useInitSearchBarUrlParams();
|
||||
|
||||
const { browserFields, indexPattern } = useSourcererDataView(getScopeFromPath(pathname));
|
||||
// side effect: this will attempt to upgrade the endpoint package if it is not up to date
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { isEmpty } from 'lodash/fp';
|
||||
import type { Location } from 'history';
|
||||
|
||||
import type { Filter, Query } from '@kbn/es-query';
|
||||
|
@ -47,19 +46,7 @@ export const getUrlStateSearch = (urlState: UrlState): string =>
|
|||
(myLocation: Location, urlKey: KeyUrlState) => {
|
||||
let urlStateToReplace: Filter[] | Query | TimelineUrl | UrlInputsModel | string = '';
|
||||
|
||||
if (urlKey === CONSTANTS.appQuery && urlState.query != null) {
|
||||
if (urlState.query.query === '') {
|
||||
urlStateToReplace = '';
|
||||
} else {
|
||||
urlStateToReplace = urlState.query;
|
||||
}
|
||||
} else if (urlKey === CONSTANTS.filters && urlState.filters != null) {
|
||||
if (isEmpty(urlState.filters)) {
|
||||
urlStateToReplace = '';
|
||||
} else {
|
||||
urlStateToReplace = urlState.filters;
|
||||
}
|
||||
} else if (urlKey === CONSTANTS.timerange) {
|
||||
if (urlKey === CONSTANTS.timerange) {
|
||||
urlStateToReplace = urlState[CONSTANTS.timerange];
|
||||
} else if (urlKey === CONSTANTS.timeline && urlState[CONSTANTS.timeline] != null) {
|
||||
const timeline = urlState[CONSTANTS.timeline];
|
||||
|
|
|
@ -91,8 +91,6 @@ describe('SIEM Navigation', () => {
|
|||
linkTo: ['global'],
|
||||
},
|
||||
},
|
||||
[CONSTANTS.appQuery]: { query: '', language: 'kuery' },
|
||||
[CONSTANTS.filters]: [],
|
||||
[CONSTANTS.timeline]: {
|
||||
activeTab: TimelineTabs.query,
|
||||
id: '',
|
||||
|
|
|
@ -78,13 +78,10 @@ export const TabNavigationComponent: React.FC<
|
|||
|
||||
return (
|
||||
<TabNavigation
|
||||
query={urlState.query}
|
||||
display={display}
|
||||
filters={urlState.filters}
|
||||
navTabs={navTabs}
|
||||
pageName={pageName}
|
||||
pathName={pathName}
|
||||
savedQuery={urlState.savedQuery}
|
||||
tabName={tabName}
|
||||
timeline={urlState.timeline}
|
||||
timerange={urlState.timerange}
|
||||
|
|
|
@ -88,8 +88,6 @@ describe('Table Navigation', () => {
|
|||
linkTo: ['global'],
|
||||
},
|
||||
},
|
||||
[CONSTANTS.appQuery]: { query: 'host.name:"siem-es"', language: 'kuery' },
|
||||
[CONSTANTS.filters]: [],
|
||||
[CONSTANTS.timeline]: {
|
||||
activeTab: TimelineTabs.query,
|
||||
id: '',
|
||||
|
|
|
@ -5,11 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { Filter, Query } from '@kbn/es-query';
|
||||
import type { UrlInputsModel } from '../../../store/inputs/model';
|
||||
import type { CONSTANTS } from '../../url_state/constants';
|
||||
import type { TimelineUrl } from '../../../../timelines/store/timeline/model';
|
||||
|
||||
import type { SecuritySolutionTabNavigationProps } from '../types';
|
||||
import type { SiemRouteType } from '../../../utils/route/types';
|
||||
|
||||
|
@ -17,9 +15,6 @@ export interface TabNavigationProps extends SecuritySolutionTabNavigationProps {
|
|||
pathName: string;
|
||||
pageName: string;
|
||||
tabName: SiemRouteType | undefined;
|
||||
[CONSTANTS.appQuery]?: Query;
|
||||
[CONSTANTS.filters]?: Filter[];
|
||||
[CONSTANTS.savedQuery]?: string;
|
||||
[CONSTANTS.timerange]: UrlInputsModel;
|
||||
[CONSTANTS.timeline]: TimelineUrl;
|
||||
}
|
||||
|
|
|
@ -8,10 +8,10 @@ Object {
|
|||
"id": "main",
|
||||
"items": Array [
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/get_started?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-href": "securitySolutionUI/get_started?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-test-subj": "navigation-get_started",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/get_started?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"href": "securitySolutionUI/get_started?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"id": "get_started",
|
||||
"isSelected": false,
|
||||
"name": "Get started",
|
||||
|
@ -24,20 +24,20 @@ Object {
|
|||
"id": "dashboards",
|
||||
"items": Array [
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/overview?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-href": "securitySolutionUI/overview?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-test-subj": "navigation-overview",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/overview?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"href": "securitySolutionUI/overview?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"id": "overview",
|
||||
"isSelected": false,
|
||||
"name": "Overview",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/detection_response?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-href": "securitySolutionUI/detection_response?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-test-subj": "navigation-detection_response",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/detection_response?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"href": "securitySolutionUI/detection_response?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"id": "detection_response",
|
||||
"isSelected": false,
|
||||
"name": "Detection & Response",
|
||||
|
@ -50,30 +50,30 @@ Object {
|
|||
"id": "detect",
|
||||
"items": Array [
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/alerts?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-href": "securitySolutionUI/alerts?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-test-subj": "navigation-alerts",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/alerts?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"href": "securitySolutionUI/alerts?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"id": "alerts",
|
||||
"isSelected": false,
|
||||
"name": "Alerts",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/rules?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-href": "securitySolutionUI/rules?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-test-subj": "navigation-rules",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/rules?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"href": "securitySolutionUI/rules?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"id": "rules",
|
||||
"isSelected": false,
|
||||
"name": "Rules",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/exceptions?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-href": "securitySolutionUI/exceptions?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-test-subj": "navigation-exceptions",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/exceptions?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"href": "securitySolutionUI/exceptions?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"id": "exceptions",
|
||||
"isSelected": false,
|
||||
"name": "Exception lists",
|
||||
|
@ -86,30 +86,30 @@ Object {
|
|||
"id": "explore",
|
||||
"items": Array [
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/hosts?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-href": "securitySolutionUI/hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-test-subj": "navigation-hosts",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/hosts?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"href": "securitySolutionUI/hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"id": "hosts",
|
||||
"isSelected": true,
|
||||
"name": "Hosts",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/network?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-href": "securitySolutionUI/network?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-test-subj": "navigation-network",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/network?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"href": "securitySolutionUI/network?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"id": "network",
|
||||
"isSelected": false,
|
||||
"name": "Network",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/users?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-href": "securitySolutionUI/users?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-test-subj": "navigation-users",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/users?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"href": "securitySolutionUI/users?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"id": "users",
|
||||
"isSelected": false,
|
||||
"name": "Users",
|
||||
|
@ -122,20 +122,20 @@ Object {
|
|||
"id": "investigate",
|
||||
"items": Array [
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/timelines?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-href": "securitySolutionUI/timelines?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-test-subj": "navigation-timelines",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/timelines?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"href": "securitySolutionUI/timelines?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"id": "timelines",
|
||||
"isSelected": false,
|
||||
"name": "Timelines",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/cases?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-href": "securitySolutionUI/cases?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-test-subj": "navigation-cases",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/cases?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"href": "securitySolutionUI/cases?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"id": "cases",
|
||||
"isSelected": false,
|
||||
"name": "Cases",
|
||||
|
|
|
@ -37,8 +37,6 @@ jest.mock('../../../../management/pages/host_isolation_exceptions/view/hooks');
|
|||
|
||||
describe('useSecuritySolutionNavigation', () => {
|
||||
const mockUrlState = {
|
||||
[CONSTANTS.appQuery]: { query: 'host.name:"security-solution-es"', language: 'kuery' },
|
||||
[CONSTANTS.savedQuery]: '',
|
||||
[CONSTANTS.timeline]: {
|
||||
activeTab: TimelineTabs.query,
|
||||
id: '',
|
||||
|
@ -171,10 +169,10 @@ describe('useSecuritySolutionNavigation', () => {
|
|||
);
|
||||
expect(caseNavItem).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/cases?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-href": "securitySolutionUI/cases?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-test-subj": "navigation-cases",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/cases?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"href": "securitySolutionUI/cases?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"id": "cases",
|
||||
"isSelected": false,
|
||||
"name": "Cases",
|
||||
|
|
|
@ -72,11 +72,8 @@ export const useSecuritySolutionNavigation = () => {
|
|||
]);
|
||||
|
||||
return usePrimaryNavigation({
|
||||
query: urlState.query,
|
||||
filters: urlState.filters,
|
||||
navTabs: enabledNavTabs,
|
||||
pageName,
|
||||
savedQuery: urlState.savedQuery,
|
||||
tabName,
|
||||
timeline: urlState.timeline,
|
||||
timerange: urlState.timerange,
|
||||
|
|
|
@ -19,11 +19,8 @@ const translatedNavTitle = i18n.translate('xpack.securitySolution.navigation.mai
|
|||
});
|
||||
|
||||
export const usePrimaryNavigation = ({
|
||||
filters,
|
||||
query,
|
||||
navTabs,
|
||||
pageName,
|
||||
savedQuery,
|
||||
tabName,
|
||||
timeline,
|
||||
timerange,
|
||||
|
@ -49,9 +46,6 @@ export const usePrimaryNavigation = ({
|
|||
const navItems = usePrimaryNavigationItems({
|
||||
navTabs,
|
||||
selectedTabId,
|
||||
filters,
|
||||
query,
|
||||
savedQuery,
|
||||
timeline,
|
||||
timerange,
|
||||
});
|
||||
|
|
|
@ -6,14 +6,25 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import {
|
||||
createSecuritySolutionStorageMock,
|
||||
kibanaObservable,
|
||||
mockGlobalState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
TestProviders,
|
||||
} from '../../mock';
|
||||
import { render, fireEvent, waitFor } from '@testing-library/react';
|
||||
import type { InputsModelId } from '../../store/inputs/constants';
|
||||
import { SearchBarComponent } from '.';
|
||||
import { TestProviders } from '../../mock';
|
||||
import type { SavedQuery } from '@kbn/data-plugin/public';
|
||||
import { FilterManager } from '@kbn/data-plugin/public';
|
||||
import { coreMock } from '@kbn/core/public/mocks';
|
||||
import { createStore } from '../../store';
|
||||
import { inputsActions } from '../../store/inputs';
|
||||
|
||||
const mockSetAppFilters = jest.fn();
|
||||
const mockFilterManager = new FilterManager(coreMock.createStart().uiSettings);
|
||||
mockFilterManager.setAppFilters = mockSetAppFilters;
|
||||
jest.mock('../../lib/kibana', () => {
|
||||
const original = jest.requireActual('../../lib/kibana');
|
||||
return {
|
||||
|
@ -45,6 +56,11 @@ jest.mock('../../lib/kibana', () => {
|
|||
};
|
||||
});
|
||||
|
||||
const mockUpdateUrlParam = jest.fn();
|
||||
jest.mock('../../utils/global_query_string', () => ({
|
||||
useUpdateUrlParam: () => mockUpdateUrlParam,
|
||||
}));
|
||||
|
||||
describe('SearchBarComponent', () => {
|
||||
const props = {
|
||||
id: 'global' as InputsModelId,
|
||||
|
@ -73,16 +89,6 @@ describe('SearchBarComponent', () => {
|
|||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('calls setSearchBarFilter on mount', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<SearchBarComponent {...props} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(props.setSearchBarFilter).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls pollForSignalIndex on Refresh button click', () => {
|
||||
const { getByTestId } = render(
|
||||
<TestProviders>
|
||||
|
@ -102,4 +108,174 @@ describe('SearchBarComponent', () => {
|
|||
fireEvent.click(getByTestId('querySubmitButton'));
|
||||
expect(pollForSignalIndex).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls useUpdateUrlParam for filter and query', () => {
|
||||
const query = { query: 'testQuery', language: 'kuery' };
|
||||
const filters = [
|
||||
{
|
||||
meta: {
|
||||
negate: false,
|
||||
alias: null,
|
||||
disabled: false,
|
||||
type: 'phrase',
|
||||
key: 'host.name',
|
||||
},
|
||||
query: { match_phrase: { 'host.name': 'testValue' } },
|
||||
},
|
||||
];
|
||||
|
||||
const state = {
|
||||
...mockGlobalState,
|
||||
inputs: {
|
||||
...mockGlobalState.inputs,
|
||||
global: {
|
||||
...mockGlobalState.inputs.global,
|
||||
filters,
|
||||
query,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
|
||||
render(
|
||||
<TestProviders store={store}>
|
||||
<SearchBarComponent {...props} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(mockUpdateUrlParam).toHaveBeenCalledWith(filters);
|
||||
expect(mockUpdateUrlParam).toHaveBeenCalledWith(query);
|
||||
// For updateSavedQueryUrlParam
|
||||
expect(mockUpdateUrlParam).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it('calls useUpdateUrlParam for savedQuery', () => {
|
||||
const savedQuery: SavedQuery = {
|
||||
id: 'testSavedquery',
|
||||
attributes: {
|
||||
title: 'testtitle',
|
||||
description: 'testDescription',
|
||||
query: { query: 'testQuery', language: 'kuery' },
|
||||
},
|
||||
};
|
||||
|
||||
const state = {
|
||||
...mockGlobalState,
|
||||
inputs: {
|
||||
...mockGlobalState.inputs,
|
||||
global: {
|
||||
...mockGlobalState.inputs.global,
|
||||
savedQuery,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
|
||||
render(
|
||||
<TestProviders store={store}>
|
||||
<SearchBarComponent {...props} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
// For filters and query
|
||||
expect(mockUpdateUrlParam).toHaveBeenNthCalledWith(2, null);
|
||||
expect(mockUpdateUrlParam).toHaveBeenCalledWith(savedQuery.id);
|
||||
});
|
||||
|
||||
it('calls useUpdateUrlParam when query state changes', async () => {
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
|
||||
render(
|
||||
<TestProviders store={store}>
|
||||
<SearchBarComponent {...props} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
jest.clearAllMocks();
|
||||
const newQuery = { query: 'testQuery', language: 'new testLanguage' };
|
||||
|
||||
store.dispatch(
|
||||
inputsActions.setFilterQuery({
|
||||
id: 'global',
|
||||
...newQuery,
|
||||
})
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUpdateUrlParam).toHaveBeenCalledWith(newQuery);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls useUpdateUrlParam when filters change', async () => {
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
|
||||
render(
|
||||
<TestProviders store={store}>
|
||||
<SearchBarComponent {...props} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
jest.clearAllMocks();
|
||||
const filters = [
|
||||
{
|
||||
meta: {
|
||||
negate: false,
|
||||
alias: null,
|
||||
disabled: false,
|
||||
type: 'phrase',
|
||||
key: 'host.name',
|
||||
},
|
||||
query: { match_phrase: { 'host.name': 'testValue' } },
|
||||
},
|
||||
];
|
||||
|
||||
store.dispatch(
|
||||
inputsActions.setSearchBarFilter({
|
||||
id: 'global',
|
||||
filters,
|
||||
})
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUpdateUrlParam).toHaveBeenCalledWith(filters);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls useUpdateUrlParam when savedQuery changes', async () => {
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
|
||||
render(
|
||||
<TestProviders store={store}>
|
||||
<SearchBarComponent {...props} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
jest.clearAllMocks();
|
||||
const savedQuery: SavedQuery = {
|
||||
id: 'testSavedQuery123',
|
||||
attributes: {
|
||||
title: 'testtitle',
|
||||
description: 'testDescription',
|
||||
query: { query: 'testQuery', language: 'kuery' },
|
||||
},
|
||||
};
|
||||
|
||||
store.dispatch(
|
||||
inputsActions.setSavedQuery({
|
||||
id: 'global',
|
||||
savedQuery,
|
||||
})
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUpdateUrlParam).toHaveBeenCalledWith(savedQuery.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -40,8 +40,7 @@ import { useKibana } from '../../lib/kibana';
|
|||
import { usersActions } from '../../../users/store';
|
||||
import { hostsActions } from '../../../hosts/store';
|
||||
import { networkActions } from '../../../network/store';
|
||||
|
||||
const APP_STATE_STORAGE_KEY = 'securitySolution.searchBar.appState';
|
||||
import { useSyncSearchBarUrlParams } from '../../hooks/search_bar/use_sync_search_bar_url_param';
|
||||
|
||||
interface SiemSearchBarProps {
|
||||
id: InputsModelId;
|
||||
|
@ -80,7 +79,6 @@ export const SearchBarComponent = memo<SiemSearchBarProps & PropsFromRedux>(
|
|||
filterManager,
|
||||
},
|
||||
},
|
||||
storage,
|
||||
unifiedSearch: {
|
||||
ui: { SearchBar },
|
||||
},
|
||||
|
@ -93,6 +91,8 @@ export const SearchBarComponent = memo<SiemSearchBarProps & PropsFromRedux>(
|
|||
dispatch(networkActions.setNetworkTablesActivePageToZero());
|
||||
}, [dispatch]);
|
||||
|
||||
useSyncSearchBarUrlParams();
|
||||
|
||||
useEffect(() => {
|
||||
if (fromStr != null && toStr != null) {
|
||||
timefilter.setTime({ from: fromStr, to: toStr });
|
||||
|
@ -269,16 +269,6 @@ export const SearchBarComponent = memo<SiemSearchBarProps & PropsFromRedux>(
|
|||
setTablesActivePageToZero,
|
||||
]);
|
||||
|
||||
const saveAppStateToStorage = useCallback(
|
||||
(filters: Filter[]) => storage.set(APP_STATE_STORAGE_KEY, filters),
|
||||
[storage]
|
||||
);
|
||||
|
||||
const getAppStateFromStorage = useCallback(
|
||||
() => storage.get(APP_STATE_STORAGE_KEY) ?? [],
|
||||
[storage]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
let isSubscribed = true;
|
||||
const subscriptions = new Subscription();
|
||||
|
@ -287,25 +277,15 @@ export const SearchBarComponent = memo<SiemSearchBarProps & PropsFromRedux>(
|
|||
filterManager.getUpdates$().subscribe({
|
||||
next: () => {
|
||||
if (isSubscribed) {
|
||||
saveAppStateToStorage(filterManager.getAppFilters());
|
||||
setSearchBarFilter({
|
||||
id,
|
||||
filters: filterManager.getFilters(),
|
||||
});
|
||||
const filters = filterManager.getFilters();
|
||||
|
||||
setSearchBarFilter({ id, filters });
|
||||
setTablesActivePageToZero();
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
// for the initial state
|
||||
filterManager.setAppFilters(getAppStateFromStorage());
|
||||
setSearchBarFilter({
|
||||
id,
|
||||
filters: filterManager.getFilters(),
|
||||
});
|
||||
|
||||
return () => {
|
||||
isSubscribed = false;
|
||||
subscriptions.unsubscribe();
|
||||
|
@ -405,8 +385,8 @@ export const dispatchUpdateSearch =
|
|||
savedQuery,
|
||||
start,
|
||||
timelineId,
|
||||
filterManager,
|
||||
updateTime = false,
|
||||
filterManager,
|
||||
setTablesActivePageToZero,
|
||||
}: UpdateReduxSearchBar): void => {
|
||||
if (updateTime) {
|
||||
|
@ -465,6 +445,7 @@ export const dispatchUpdateSearch =
|
|||
if (filters != null) {
|
||||
filterManager.setFilters(filters);
|
||||
}
|
||||
|
||||
if (savedQuery != null || resetSavedQuery) {
|
||||
dispatch(inputsActions.setSavedQuery({ id, savedQuery }));
|
||||
}
|
||||
|
|
|
@ -35,42 +35,6 @@ describe('Helpers Url_State', () => {
|
|||
});
|
||||
|
||||
describe('isQueryStateEmpty', () => {
|
||||
test('returns true if queryState is undefined', () => {
|
||||
const result = isQueryStateEmpty(undefined, CONSTANTS.savedQuery);
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
test('returns true if queryState is null', () => {
|
||||
const result = isQueryStateEmpty(null, CONSTANTS.savedQuery);
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
test('returns true if url key is "query" and queryState is empty string', () => {
|
||||
const result = isQueryStateEmpty('', CONSTANTS.appQuery);
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
test('returns false if url key is "query" and queryState is not empty', () => {
|
||||
const result = isQueryStateEmpty(
|
||||
{ query: { query: '*:*' }, language: 'kuery' },
|
||||
CONSTANTS.appQuery
|
||||
);
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
||||
test('returns true if url key is "filters" and queryState is empty', () => {
|
||||
const result = isQueryStateEmpty([], CONSTANTS.filters);
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
test('returns false if url key is "filters" and queryState is not empty', () => {
|
||||
const result = isQueryStateEmpty(
|
||||
[{ query: { query: '*:*' }, meta: { key: '123' } }],
|
||||
CONSTANTS.filters
|
||||
);
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
||||
// TODO: Is this a bug, or intended?
|
||||
test('returns false if url key is "timeline" and queryState is empty', () => {
|
||||
const result = isQueryStateEmpty({} as ValueUrlState, CONSTANTS.timeline);
|
||||
|
|
|
@ -5,13 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { isEmpty } from 'lodash/fp';
|
||||
import { parse, stringify } from 'query-string';
|
||||
import { decode, encode } from 'rison-node';
|
||||
import type * as H from 'history';
|
||||
|
||||
import type { Filter, Query } from '@kbn/es-query';
|
||||
|
||||
import { url } from '@kbn/kibana-utils-plugin/public';
|
||||
|
||||
import { TimelineId, TimelineTabs } from '../../../../common/types/timeline';
|
||||
|
@ -127,9 +124,6 @@ export const getTitle = (pageName: string, navTabs: Record<string, NavTab>): str
|
|||
|
||||
export const makeMapStateToProps = () => {
|
||||
const getInputsSelector = inputsSelectors.inputsSelector();
|
||||
const getGlobalQuerySelector = inputsSelectors.globalQuerySelector();
|
||||
const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector();
|
||||
const getGlobalSavedQuerySelector = inputsSelectors.globalSavedQuerySelector();
|
||||
const getTimeline = timelineSelectors.getTimelineByIdSelector();
|
||||
const mapStateToProps = (state: State) => {
|
||||
const inputState = getInputsSelector(state);
|
||||
|
@ -147,24 +141,8 @@ export const makeMapStateToProps = () => {
|
|||
}
|
||||
: { id: '', isOpen: false, activeTab: TimelineTabs.query, graphEventId: '' };
|
||||
|
||||
let searchAttr: {
|
||||
[CONSTANTS.appQuery]?: Query;
|
||||
[CONSTANTS.filters]?: Filter[];
|
||||
[CONSTANTS.savedQuery]?: string;
|
||||
} = {
|
||||
[CONSTANTS.appQuery]: getGlobalQuerySelector(state),
|
||||
[CONSTANTS.filters]: getGlobalFiltersQuerySelector(state),
|
||||
};
|
||||
const savedQuery = getGlobalSavedQuerySelector(state);
|
||||
if (savedQuery != null && savedQuery.id !== '') {
|
||||
searchAttr = {
|
||||
[CONSTANTS.savedQuery]: savedQuery.id,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
urlState: {
|
||||
...searchAttr,
|
||||
[CONSTANTS.timerange]: {
|
||||
global: {
|
||||
[CONSTANTS.timerange]: globalTimerange,
|
||||
|
@ -204,10 +182,7 @@ export const isQueryStateEmpty = (
|
|||
queryState: ValueUrlState | undefined | null,
|
||||
urlKey: KeyUrlState
|
||||
): boolean =>
|
||||
queryState == null ||
|
||||
(urlKey === CONSTANTS.appQuery && isEmpty((queryState as Query).query)) ||
|
||||
(urlKey === CONSTANTS.filters && isEmpty(queryState)) ||
|
||||
(urlKey === CONSTANTS.timeline && (queryState as TimelineUrl).id === '');
|
||||
queryState == null || (urlKey === CONSTANTS.timeline && (queryState as TimelineUrl).id === '');
|
||||
|
||||
export const replaceStatesInLocation = (
|
||||
states: ReplaceStateInLocation[],
|
||||
|
|
|
@ -15,7 +15,6 @@ import { CONSTANTS } from './constants';
|
|||
import {
|
||||
getMockPropsObj,
|
||||
mockHistory,
|
||||
mockSetFilterQuery,
|
||||
mockSetAbsoluteRangeDatePicker,
|
||||
mockSetRelativeRangeDatePicker,
|
||||
testCases,
|
||||
|
@ -181,62 +180,6 @@ describe('UrlStateContainer', () => {
|
|||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('appQuery action is called with correct data on component mount', () => {
|
||||
test.each(testCases.slice(0, 4))(
|
||||
' %o',
|
||||
(page, namespaceLower, namespaceUpper, examplePath, type, pageName, detailName) => {
|
||||
mockProps = getMockPropsObj({ page, examplePath, namespaceLower, pageName, detailName })
|
||||
.relativeTimeSearch.undefinedQuery;
|
||||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
mount(<HookWrapper hookProps={mockProps} hook={(args) => useUrlStateHooks(args)} />);
|
||||
|
||||
expect(mockSetFilterQuery.mock.calls[0][0]).toEqual({
|
||||
id: 'global',
|
||||
language: 'kuery',
|
||||
query: 'host.name:"siem-es"',
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Redux updates URL state', () => {
|
||||
describe('appQuery url state is set from redux data on component mount', () => {
|
||||
test.each(testCases)(
|
||||
'%o',
|
||||
(page, namespaceLower, namespaceUpper, examplePath, type, pageName, detailName) => {
|
||||
mockProps = getMockPropsObj({
|
||||
page,
|
||||
examplePath,
|
||||
namespaceLower,
|
||||
pageName,
|
||||
detailName,
|
||||
}).noSearch.definedQuery;
|
||||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
mount(<HookWrapper hookProps={mockProps} hook={(args) => useUrlStateHooks(args)} />);
|
||||
|
||||
expect(
|
||||
mockHistory.replace.mock.calls[mockHistory.replace.mock.calls.length - 1][0]
|
||||
).toEqual({
|
||||
hash: '',
|
||||
pathname: examplePath,
|
||||
search: `?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`,
|
||||
state: '',
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("it doesn't update URL state when pathName and browserPAth are out of sync", () => {
|
||||
|
@ -298,32 +241,6 @@ describe('UrlStateContainer', () => {
|
|||
expect(mockHistory.replace.mock.calls[0][0].search).toBe('?');
|
||||
});
|
||||
|
||||
it('it removes empty AppQuery state from URL', () => {
|
||||
mockProps = {
|
||||
...getMockProps(
|
||||
{
|
||||
hash: '',
|
||||
pathname: '/network',
|
||||
search: "?query=(query:'')",
|
||||
state: '',
|
||||
},
|
||||
CONSTANTS.networkPage,
|
||||
null,
|
||||
SecurityPageName.network,
|
||||
undefined
|
||||
),
|
||||
};
|
||||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
mount(<HookWrapper hookProps={mockProps} hook={(args) => useUrlStateHooks(args)} />);
|
||||
|
||||
expect(mockHistory.replace.mock.calls[0][0].search).not.toContain('query=');
|
||||
});
|
||||
|
||||
it('it removes empty timeline state from URL', () => {
|
||||
mockProps = {
|
||||
...getMockProps(
|
||||
|
|
|
@ -126,43 +126,9 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
expect(mockHistory.replace.mock.calls[1][0]).toStrictEqual({
|
||||
hash: '',
|
||||
pathname: '/network',
|
||||
search:
|
||||
"?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
state: '',
|
||||
});
|
||||
});
|
||||
|
||||
test('kql query redux state updates the url', () => {
|
||||
mockProps = getMockPropsObj({
|
||||
page: CONSTANTS.networkPage,
|
||||
examplePath: '/network',
|
||||
namespaceLower: 'network',
|
||||
pageName: SecurityPageName.network,
|
||||
detailName: undefined,
|
||||
}).noSearch.undefinedQuery;
|
||||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
const wrapper = mount(
|
||||
<HookWrapper hookProps={mockProps} hook={(args) => useUrlStateHooks(args)} />
|
||||
);
|
||||
const newUrlState = {
|
||||
...mockProps.urlState,
|
||||
[CONSTANTS.appQuery]: getFilterQuery(),
|
||||
};
|
||||
wrapper.setProps({
|
||||
hookProps: { ...mockProps, urlState: newUrlState, isInitializing: false },
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
expect(mockHistory.replace.mock.calls[1][0]).toStrictEqual({
|
||||
hash: '',
|
||||
pathname: '/network',
|
||||
search:
|
||||
"?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
|
||||
search: expect.stringContaining(
|
||||
"timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))"
|
||||
),
|
||||
state: '',
|
||||
});
|
||||
});
|
||||
|
@ -427,7 +393,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
wrapper.update();
|
||||
|
||||
expect(mockHistory.replace.mock.calls[1][0].search).toEqual(
|
||||
"?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))"
|
||||
"?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))"
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import type { Dispatch } from 'redux';
|
|||
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import type { Filter, Query } from '@kbn/es-query';
|
||||
import { inputsActions } from '../../store/actions';
|
||||
import type { InputsModelId, TimeRangeKinds } from '../../store/inputs/constants';
|
||||
import type {
|
||||
|
@ -42,52 +41,12 @@ export const useSetInitialStateFromUrl = () => {
|
|||
);
|
||||
|
||||
const setInitialStateFromUrl = useCallback(
|
||||
({
|
||||
filterManager,
|
||||
indexPattern,
|
||||
pageName,
|
||||
savedQueries,
|
||||
urlStateToUpdate,
|
||||
}: SetInitialStateFromUrl) => {
|
||||
({ urlStateToUpdate }: SetInitialStateFromUrl) => {
|
||||
urlStateToUpdate.forEach(({ urlKey, newUrlStateString }) => {
|
||||
if (urlKey === CONSTANTS.timerange) {
|
||||
updateTimerange(newUrlStateString, dispatch);
|
||||
}
|
||||
|
||||
if (urlKey === CONSTANTS.appQuery && indexPattern != null) {
|
||||
const appQuery = decodeRisonUrlState<Query>(newUrlStateString);
|
||||
if (appQuery != null) {
|
||||
dispatch(
|
||||
inputsActions.setFilterQuery({
|
||||
id: 'global',
|
||||
query: appQuery.query,
|
||||
language: appQuery.language,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (urlKey === CONSTANTS.filters) {
|
||||
const filters = decodeRisonUrlState<Filter[]>(newUrlStateString);
|
||||
filterManager.setFilters(filters || []);
|
||||
}
|
||||
|
||||
if (urlKey === CONSTANTS.savedQuery) {
|
||||
const savedQueryId = decodeRisonUrlState<string>(newUrlStateString);
|
||||
if (savedQueryId != null && savedQueryId !== '') {
|
||||
savedQueries.getSavedQuery(savedQueryId).then((savedQueryData) => {
|
||||
filterManager.setFilters(savedQueryData.attributes.filters || []);
|
||||
dispatch(
|
||||
inputsActions.setFilterQuery({
|
||||
id: 'global',
|
||||
...savedQueryData.attributes.query,
|
||||
})
|
||||
);
|
||||
dispatch(inputsActions.setSavedQuery({ id: 'global', savedQuery: savedQueryData }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (urlKey === CONSTANTS.timeline) {
|
||||
const timeline = decodeRisonUrlState<TimelineUrl>(newUrlStateString);
|
||||
if (timeline != null && timeline.id !== '') {
|
||||
|
|
|
@ -113,8 +113,6 @@ export const defaultProps: UrlStateContainerPropTypes = {
|
|||
linkTo: ['global'],
|
||||
},
|
||||
},
|
||||
[CONSTANTS.appQuery]: { query: '', language: 'kuery' },
|
||||
[CONSTANTS.filters]: [],
|
||||
[CONSTANTS.timeline]: {
|
||||
activeTab: TimelineTabs.query,
|
||||
id: '',
|
||||
|
@ -137,7 +135,6 @@ export const getMockProps = (
|
|||
...defaultProps,
|
||||
urlState: {
|
||||
...defaultProps.urlState,
|
||||
[CONSTANTS.appQuery]: kqlQueryValue || { query: '', language: 'kuery' },
|
||||
},
|
||||
history: {
|
||||
...mockHistory,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { DataViewBase, Filter, Query } from '@kbn/es-query';
|
||||
import type { DataViewBase } from '@kbn/es-query';
|
||||
import type { FilterManager, SavedQueryService } from '@kbn/data-plugin/public';
|
||||
import type { UrlInputsModel } from '../../store/inputs/model';
|
||||
import type { TimelineUrl } from '../../../timelines/store/timeline/model';
|
||||
|
@ -15,13 +15,7 @@ import type { SecurityNav } from '../navigation/types';
|
|||
import type { UrlStateType } from './constants';
|
||||
import { CONSTANTS } from './constants';
|
||||
|
||||
export const ALL_URL_STATE_KEYS: KeyUrlState[] = [
|
||||
CONSTANTS.appQuery,
|
||||
CONSTANTS.filters,
|
||||
CONSTANTS.savedQuery,
|
||||
CONSTANTS.timerange,
|
||||
CONSTANTS.timeline,
|
||||
];
|
||||
export const ALL_URL_STATE_KEYS: KeyUrlState[] = [CONSTANTS.timerange, CONSTANTS.timeline];
|
||||
|
||||
export const isAdministration = (urlKey: UrlStateType): boolean => 'administration' === urlKey;
|
||||
|
||||
|
@ -39,9 +33,6 @@ export type LocationTypes =
|
|||
| CONSTANTS.unknown;
|
||||
|
||||
export interface UrlState {
|
||||
[CONSTANTS.appQuery]?: Query;
|
||||
[CONSTANTS.filters]?: Filter[];
|
||||
[CONSTANTS.savedQuery]?: string;
|
||||
[CONSTANTS.timerange]: UrlInputsModel;
|
||||
[CONSTANTS.timeline]: TimelineUrl;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import type { Filter, Query } from '@kbn/es-query';
|
||||
import { useKibana } from '../../lib/kibana';
|
||||
import { inputsSelectors } from '../../store';
|
||||
import { inputsActions } from '../../store/inputs';
|
||||
import { useInitializeUrlParam } from '../../utils/global_query_string';
|
||||
import { CONSTANTS } from '../../components/url_state/constants';
|
||||
|
||||
export const useInitSearchBarUrlParams = () => {
|
||||
const dispatch = useDispatch();
|
||||
const { filterManager, savedQueries } = useKibana().services.data.query;
|
||||
const getGlobalFiltersQuerySelector = useMemo(
|
||||
() => inputsSelectors.globalFiltersQuerySelector(),
|
||||
[]
|
||||
);
|
||||
const filtersFromStore = useSelector(getGlobalFiltersQuerySelector);
|
||||
|
||||
const onInitializeAppQueryUrlParam = useCallback(
|
||||
(initialState: Query | null) => {
|
||||
if (initialState != null) {
|
||||
dispatch(
|
||||
inputsActions.setFilterQuery({
|
||||
id: 'global',
|
||||
query: initialState.query,
|
||||
language: initialState.language,
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const onInitializeFiltersUrlParam = useCallback(
|
||||
(initialState: Filter[] | null) => {
|
||||
if (initialState != null) {
|
||||
filterManager.setFilters(initialState);
|
||||
dispatch(
|
||||
inputsActions.setSearchBarFilter({
|
||||
id: 'global',
|
||||
filters: initialState,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
// Clear app filters and preserve pinned filters. It ensures that other App filters don't leak into security solution.
|
||||
filterManager.setAppFilters(filtersFromStore);
|
||||
|
||||
dispatch(
|
||||
inputsActions.setSearchBarFilter({
|
||||
id: 'global',
|
||||
filters: filterManager.getFilters(),
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
[filterManager, dispatch, filtersFromStore]
|
||||
);
|
||||
|
||||
const onInitializeSavedQueryUrlParam = useCallback(
|
||||
(savedQueryId: string | null) => {
|
||||
if (savedQueryId != null && savedQueryId !== '') {
|
||||
savedQueries.getSavedQuery(savedQueryId).then((savedQueryData) => {
|
||||
const filters = savedQueryData.attributes.filters || [];
|
||||
const query = savedQueryData.attributes.query;
|
||||
|
||||
filterManager.setFilters(filters);
|
||||
dispatch(
|
||||
inputsActions.setSearchBarFilter({
|
||||
id: 'global',
|
||||
filters,
|
||||
})
|
||||
);
|
||||
|
||||
dispatch(
|
||||
inputsActions.setFilterQuery({
|
||||
id: 'global',
|
||||
...query,
|
||||
})
|
||||
);
|
||||
dispatch(inputsActions.setSavedQuery({ id: 'global', savedQuery: savedQueryData }));
|
||||
});
|
||||
}
|
||||
},
|
||||
[dispatch, filterManager, savedQueries]
|
||||
);
|
||||
|
||||
useInitializeUrlParam(CONSTANTS.appQuery, onInitializeAppQueryUrlParam);
|
||||
useInitializeUrlParam(CONSTANTS.filters, onInitializeFiltersUrlParam);
|
||||
useInitializeUrlParam(CONSTANTS.savedQuery, onInitializeSavedQueryUrlParam);
|
||||
};
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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 { useSelector } from 'react-redux';
|
||||
import { useMemo, useEffect } from 'react';
|
||||
import type { Filter, Query } from '@kbn/es-query';
|
||||
import { isEmpty } from 'lodash/fp';
|
||||
import { inputsSelectors } from '../../store';
|
||||
import { CONSTANTS } from '../../components/url_state/constants';
|
||||
import { useUpdateUrlParam } from '../../utils/global_query_string';
|
||||
|
||||
export const useSyncSearchBarUrlParams = () => {
|
||||
const updateSavedQueryUrlParam = useUpdateUrlParam<string>(CONSTANTS.savedQuery);
|
||||
const updateAppQueryUrlParam = useUpdateUrlParam<Query>(CONSTANTS.appQuery);
|
||||
const updateFilterUrlParam = useUpdateUrlParam<Filter[]>(CONSTANTS.filters);
|
||||
|
||||
const getGlobalQuerySelector = useMemo(() => inputsSelectors.globalQuerySelector(), []);
|
||||
const getGlobalFiltersQuerySelector = useMemo(
|
||||
() => inputsSelectors.globalFiltersQuerySelector(),
|
||||
[]
|
||||
);
|
||||
const getGlobalSavedQuerySelector = useMemo(() => inputsSelectors.globalSavedQuerySelector(), []);
|
||||
|
||||
const query = useSelector(getGlobalQuerySelector);
|
||||
const filters = useSelector(getGlobalFiltersQuerySelector);
|
||||
const savedQuery = useSelector(getGlobalSavedQuerySelector);
|
||||
|
||||
useEffect(() => {
|
||||
if (savedQuery != null && savedQuery.id !== '') {
|
||||
updateSavedQueryUrlParam(savedQuery?.id ?? null);
|
||||
updateAppQueryUrlParam(null);
|
||||
updateFilterUrlParam(null);
|
||||
} else {
|
||||
updateSavedQueryUrlParam(null);
|
||||
updateAppQueryUrlParam(isEmpty(query.query) ? null : query);
|
||||
updateFilterUrlParam(isEmpty(filters) ? null : filters);
|
||||
}
|
||||
}, [
|
||||
savedQuery,
|
||||
query,
|
||||
filters,
|
||||
updateSavedQueryUrlParam,
|
||||
updateAppQueryUrlParam,
|
||||
updateFilterUrlParam,
|
||||
]);
|
||||
};
|
|
@ -37,12 +37,9 @@ jest.mock('react-redux', () => {
|
|||
};
|
||||
});
|
||||
|
||||
const mockLocation = jest.fn();
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useHistory: () => mockHistory,
|
||||
useLocation: () => mockLocation(),
|
||||
}));
|
||||
|
||||
const defaultLinkInfo: LinkInfo = {
|
||||
|
@ -95,7 +92,7 @@ describe('global query string', () => {
|
|||
describe('useInitializeUrlParam', () => {
|
||||
it('calls onInitialize with decoded URL param value', () => {
|
||||
const urlParamKey = 'testKey';
|
||||
mockLocation.mockReturnValue({ search: '?testKey=(test:(value:123))' });
|
||||
window.location.search = '?testKey=(test:(value:123))';
|
||||
|
||||
const onInitialize = jest.fn();
|
||||
|
||||
|
@ -108,7 +105,7 @@ describe('global query string', () => {
|
|||
|
||||
it('deregister during unmount', () => {
|
||||
const urlParamKey = 'testKey';
|
||||
mockLocation.mockReturnValue({ search: "?testKey='123'" });
|
||||
window.location.search = "?testKey='123'";
|
||||
|
||||
const { unmount } = renderHook(() => useInitializeUrlParam(urlParamKey, () => {}), {
|
||||
wrapper: makeWrapper(),
|
||||
|
@ -125,7 +122,7 @@ describe('global query string', () => {
|
|||
it('calls registerUrlParam global URL param action', () => {
|
||||
const urlParamKey = 'testKey';
|
||||
const initialValue = 123;
|
||||
mockLocation.mockReturnValue({ search: `?testKey=${initialValue}` });
|
||||
window.location.search = `?testKey=${initialValue}`;
|
||||
|
||||
renderHook(() => useInitializeUrlParam(urlParamKey, () => {}), {
|
||||
wrapper: makeWrapper(),
|
||||
|
|
|
@ -12,7 +12,7 @@ import { useCallback, useEffect, useMemo } from 'react';
|
|||
|
||||
import { url } from '@kbn/kibana-utils-plugin/public';
|
||||
import { isEmpty, pickBy } from 'lodash/fp';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import {
|
||||
decodeRisonUrlState,
|
||||
|
@ -32,7 +32,7 @@ import { getLinkInfo } from '../../links';
|
|||
* So it is only called when the application starts instead of on every page.
|
||||
*
|
||||
* @param urlParamKey Must not change.
|
||||
* @param onInitialize Called once when initializing.
|
||||
* @param onInitialize Called once when initializing. It must not change.
|
||||
*/
|
||||
export const useInitializeUrlParam = <State>(
|
||||
urlParamKey: string,
|
||||
|
@ -42,10 +42,14 @@ export const useInitializeUrlParam = <State>(
|
|||
onInitialize: (state: State | null) => void
|
||||
) => {
|
||||
const dispatch = useDispatch();
|
||||
const { search } = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
const initialValue = getParamFromQueryString(getQueryStringFromLocation(search), urlParamKey);
|
||||
// window.location.search provides the most updated representation of the url search.
|
||||
// It also guarantees that we don't overwrite URL param managed outside react-router.
|
||||
const initialValue = getParamFromQueryString(
|
||||
getQueryStringFromLocation(window.location.search),
|
||||
urlParamKey
|
||||
);
|
||||
|
||||
dispatch(
|
||||
globalUrlParamActions.registerUrlParam({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue