mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Security Solution] Improve url state management (#134210)
* Implement global query string POC
This commit is contained in:
parent
44ba4bcecc
commit
819c7e5d8b
45 changed files with 979 additions and 212 deletions
|
@ -121,6 +121,7 @@ export enum SecurityPageName {
|
|||
kubernetes = 'kubernetes',
|
||||
exploreLanding = 'explore',
|
||||
dashboardsLanding = 'dashboards',
|
||||
noPage = '',
|
||||
}
|
||||
|
||||
export const EXPLORE_PATH = '/explore' as const;
|
||||
|
|
|
@ -184,7 +184,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')&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&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')))`
|
||||
`/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-*')))`
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -197,12 +197,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')&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&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')))`
|
||||
`/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-*')))`
|
||||
);
|
||||
cy.get(NETWORK).should(
|
||||
'have.attr',
|
||||
'href',
|
||||
`/app/security/network?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&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')))`
|
||||
`/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-*')))`
|
||||
);
|
||||
cy.get(HOSTS_NAMES).first().should('have.text', 'siem-kibana');
|
||||
|
||||
|
@ -213,21 +213,22 @@ describe('url state', () => {
|
|||
cy.get(ANOMALIES_TAB).should(
|
||||
'have.attr',
|
||||
'href',
|
||||
"/app/security/hosts/siem-kibana/anomalies?sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&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')))&query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')"
|
||||
"/app/security/hosts/siem-kibana/anomalies?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(1)
|
||||
.should(
|
||||
'have.attr',
|
||||
'href',
|
||||
`/app/security/hosts?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&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')))`
|
||||
`/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-*')))`
|
||||
);
|
||||
cy.get(BREADCRUMBS)
|
||||
.eq(2)
|
||||
.should(
|
||||
'have.attr',
|
||||
'href',
|
||||
`/app/security/hosts/siem-kibana?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&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')))`
|
||||
`/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-*')))`
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import { useUpgradeSecurityPackages } from '../../common/hooks/use_upgrade_secur
|
|||
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';
|
||||
interface HomePageProps {
|
||||
children: React.ReactNode;
|
||||
onAppLeave: (handler: AppLeaveHandler) => void;
|
||||
|
@ -36,7 +36,7 @@ const HomePageComponent: React.FC<HomePageProps> = ({
|
|||
setHeaderActionMenu,
|
||||
}) => {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
useSyncGlobalQueryString();
|
||||
useInitSourcerer(getScopeFromPath(pathname));
|
||||
|
||||
const { browserFields, indexPattern } = useSourcererDataView(getScopeFromPath(pathname));
|
||||
|
|
|
@ -16,7 +16,7 @@ import { AdministrationSubTab } from '../../../../management/types';
|
|||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { TestProviders } from '../../../mock';
|
||||
import { GetSecuritySolutionUrl } from '../../link_to';
|
||||
import { APP_UI_ID } from '../../../../../common/constants';
|
||||
import { APP_UI_ID, SecurityPageName } from '../../../../../common/constants';
|
||||
import { useDeepEqualSelector } from '../../../hooks/use_selector';
|
||||
import { useIsGroupedNavigationEnabled } from '../helpers';
|
||||
import { navTabs } from '../../../../app/home/home_navigations';
|
||||
|
@ -55,7 +55,7 @@ const mockDefaultTab = (pageName: string): SiemRouteType | undefined => {
|
|||
};
|
||||
|
||||
const getMockObject = (
|
||||
pageName: string,
|
||||
pageName: SecurityPageName,
|
||||
pathName: string,
|
||||
detailName: string | undefined
|
||||
): RouteSpyState & ObjectWithNavTabs => ({
|
||||
|
@ -100,7 +100,6 @@ const getMockObject = (
|
|||
},
|
||||
},
|
||||
},
|
||||
sourcerer: {},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
@ -191,7 +190,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
describe('getBreadcrumbsForRoute', () => {
|
||||
test('should return Overview breadcrumbs when supplied overview pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('overview', '/', undefined),
|
||||
getMockObject(SecurityPageName.overview, '/', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
false
|
||||
);
|
||||
|
@ -206,7 +205,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return Host breadcrumbs when supplied hosts pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('hosts', '/', undefined),
|
||||
getMockObject(SecurityPageName.hosts, '/', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
false
|
||||
);
|
||||
|
@ -222,7 +221,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return Network breadcrumbs when supplied network pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('network', '/', undefined),
|
||||
getMockObject(SecurityPageName.network, '/', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
false
|
||||
);
|
||||
|
@ -238,7 +237,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return Timelines breadcrumbs when supplied timelines pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('timelines', '/', undefined),
|
||||
getMockObject(SecurityPageName.timelines, '/', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
false
|
||||
);
|
||||
|
@ -253,7 +252,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return Host Details breadcrumbs when supplied a pathname with hostName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('hosts', '/', hostName),
|
||||
getMockObject(SecurityPageName.hosts, '/', hostName),
|
||||
getSecuritySolutionUrl,
|
||||
false
|
||||
);
|
||||
|
@ -270,7 +269,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return IP Details breadcrumbs when supplied pathname with ipv4', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('network', '/', ipv4),
|
||||
getMockObject(SecurityPageName.network, '/', ipv4),
|
||||
getSecuritySolutionUrl,
|
||||
false
|
||||
);
|
||||
|
@ -287,7 +286,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return IP Details breadcrumbs when supplied pathname with ipv6', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('network', '/', ipv6Encoded),
|
||||
getMockObject(SecurityPageName.network, '/', ipv6Encoded),
|
||||
getSecuritySolutionUrl,
|
||||
false
|
||||
);
|
||||
|
@ -304,7 +303,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return Alerts breadcrumbs when supplied alerts pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('alerts', '/alerts', undefined),
|
||||
getMockObject(SecurityPageName.alerts, '/alerts', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
false
|
||||
);
|
||||
|
@ -319,7 +318,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return Exceptions breadcrumbs when supplied exceptions pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('exceptions', '/exceptions', undefined),
|
||||
getMockObject(SecurityPageName.exceptions, '/exceptions', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
false
|
||||
);
|
||||
|
@ -334,7 +333,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return Rules breadcrumbs when supplied rules pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('rules', '/rules', undefined),
|
||||
getMockObject(SecurityPageName.rules, '/rules', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
false
|
||||
);
|
||||
|
@ -349,7 +348,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return Rules breadcrumbs when supplied rules Creation pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('rules', '/rules/create', undefined),
|
||||
getMockObject(SecurityPageName.rules, '/rules/create', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
false
|
||||
);
|
||||
|
@ -368,7 +367,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
const mockRuleName = 'ALERT_RULE_NAME';
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
{
|
||||
...getMockObject('rules', `/rules/id/${mockDetailName}`, undefined),
|
||||
...getMockObject(SecurityPageName.rules, `/rules/id/${mockDetailName}`, undefined),
|
||||
detailName: mockDetailName,
|
||||
state: {
|
||||
ruleName: mockRuleName,
|
||||
|
@ -392,7 +391,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
const mockRuleName = 'ALERT_RULE_NAME';
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
{
|
||||
...getMockObject('rules', `/rules/id/${mockDetailName}/edit`, undefined),
|
||||
...getMockObject(SecurityPageName.rules, `/rules/id/${mockDetailName}/edit`, undefined),
|
||||
detailName: mockDetailName,
|
||||
state: {
|
||||
ruleName: mockRuleName,
|
||||
|
@ -417,7 +416,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return null breadcrumbs when supplied Cases pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('cases', '/', undefined),
|
||||
getMockObject(SecurityPageName.case, '/', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
false
|
||||
);
|
||||
|
@ -431,7 +430,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
};
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
{
|
||||
...getMockObject('cases', `/${sampleCase.id}`, sampleCase.id),
|
||||
...getMockObject(SecurityPageName.case, `/${sampleCase.id}`, sampleCase.id),
|
||||
state: { caseTitle: sampleCase.name },
|
||||
},
|
||||
getSecuritySolutionUrl,
|
||||
|
@ -442,7 +441,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return Admin breadcrumbs when supplied endpoints pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('administration', '/endpoints', undefined),
|
||||
getMockObject(SecurityPageName.administration, '/endpoints', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
false
|
||||
);
|
||||
|
@ -461,22 +460,26 @@ describe('Navigation Breadcrumbs', () => {
|
|||
test('should call chrome breadcrumb service with correct breadcrumbs', () => {
|
||||
const navigateToUrlMock = jest.fn();
|
||||
const { result } = renderHook(() => useSetBreadcrumbs(), { wrapper: TestProviders });
|
||||
result.current(getMockObject('hosts', '/', hostName), chromeMock, navigateToUrlMock);
|
||||
result.current(
|
||||
getMockObject(SecurityPageName.hosts, '/', hostName),
|
||||
chromeMock,
|
||||
navigateToUrlMock
|
||||
);
|
||||
|
||||
expect(setBreadcrumbsMock).toBeCalledWith([
|
||||
expect.objectContaining({
|
||||
text: 'Security',
|
||||
href: "securitySolutionUI/get_started?sourcerer=()&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)))",
|
||||
href: "securitySolutionUI/get_started?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)))",
|
||||
onClick: expect.any(Function),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
text: 'Hosts',
|
||||
href: "securitySolutionUI/hosts?sourcerer=()&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)))",
|
||||
href: "securitySolutionUI/hosts?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)))",
|
||||
onClick: expect.any(Function),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
text: 'siem-kibana',
|
||||
href: "securitySolutionUI/hosts/siem-kibana?sourcerer=()&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)))",
|
||||
href: "securitySolutionUI/hosts/siem-kibana?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)))",
|
||||
onClick: expect.any(Function),
|
||||
}),
|
||||
{
|
||||
|
@ -496,7 +499,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
describe('getBreadcrumbsForRoute', () => {
|
||||
test('should return Overview breadcrumbs when supplied overview pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('overview', '/', undefined),
|
||||
getMockObject(SecurityPageName.overview, '/', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
true
|
||||
);
|
||||
|
@ -515,7 +518,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return Host breadcrumbs when supplied hosts pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('hosts', '/', undefined),
|
||||
getMockObject(SecurityPageName.hosts, '/', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
true
|
||||
);
|
||||
|
@ -532,7 +535,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return Network breadcrumbs when supplied network pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('network', '/', undefined),
|
||||
getMockObject(SecurityPageName.network, '/', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
true
|
||||
);
|
||||
|
@ -549,7 +552,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return Timelines breadcrumbs when supplied timelines pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('timelines', '/', undefined),
|
||||
getMockObject(SecurityPageName.timelines, '/', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
true
|
||||
);
|
||||
|
@ -564,7 +567,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return Host Details breadcrumbs when supplied a pathname with hostName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('hosts', '/', hostName),
|
||||
getMockObject(SecurityPageName.hosts, '/', hostName),
|
||||
getSecuritySolutionUrl,
|
||||
true
|
||||
);
|
||||
|
@ -582,7 +585,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return IP Details breadcrumbs when supplied pathname with ipv4', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('network', '/', ipv4),
|
||||
getMockObject(SecurityPageName.network, '/', ipv4),
|
||||
getSecuritySolutionUrl,
|
||||
true
|
||||
);
|
||||
|
@ -600,7 +603,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return IP Details breadcrumbs when supplied pathname with ipv6', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('network', '/', ipv6Encoded),
|
||||
getMockObject(SecurityPageName.network, '/', ipv6Encoded),
|
||||
getSecuritySolutionUrl,
|
||||
true
|
||||
);
|
||||
|
@ -618,7 +621,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return Alerts breadcrumbs when supplied alerts pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('alerts', '/alerts', undefined),
|
||||
getMockObject(SecurityPageName.alerts, '/alerts', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
true
|
||||
);
|
||||
|
@ -633,7 +636,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return Exceptions breadcrumbs when supplied exceptions pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('exceptions', '/exceptions', undefined),
|
||||
getMockObject(SecurityPageName.exceptions, '/exceptions', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
true
|
||||
);
|
||||
|
@ -649,7 +652,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return Rules breadcrumbs when supplied rules pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('rules', '/rules', undefined),
|
||||
getMockObject(SecurityPageName.rules, '/rules', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
true
|
||||
);
|
||||
|
@ -665,7 +668,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return Rules breadcrumbs when supplied rules Creation pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('rules', '/rules/create', undefined),
|
||||
getMockObject(SecurityPageName.rules, '/rules/create', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
true
|
||||
);
|
||||
|
@ -685,7 +688,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
const mockRuleName = 'ALERT_RULE_NAME';
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
{
|
||||
...getMockObject('rules', `/rules/id/${mockDetailName}`, undefined),
|
||||
...getMockObject(SecurityPageName.rules, `/rules/id/${mockDetailName}`, undefined),
|
||||
detailName: mockDetailName,
|
||||
state: {
|
||||
ruleName: mockRuleName,
|
||||
|
@ -710,7 +713,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
const mockRuleName = 'ALERT_RULE_NAME';
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
{
|
||||
...getMockObject('rules', `/rules/id/${mockDetailName}/edit`, undefined),
|
||||
...getMockObject(SecurityPageName.rules, `/rules/id/${mockDetailName}/edit`, undefined),
|
||||
detailName: mockDetailName,
|
||||
state: {
|
||||
ruleName: mockRuleName,
|
||||
|
@ -736,7 +739,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return null breadcrumbs when supplied Cases pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('cases', '/', undefined),
|
||||
getMockObject(SecurityPageName.case, '/', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
true
|
||||
);
|
||||
|
@ -750,7 +753,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
};
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
{
|
||||
...getMockObject('cases', `/${sampleCase.id}`, sampleCase.id),
|
||||
...getMockObject(SecurityPageName.case, `/${sampleCase.id}`, sampleCase.id),
|
||||
state: { caseTitle: sampleCase.name },
|
||||
},
|
||||
getSecuritySolutionUrl,
|
||||
|
@ -761,7 +764,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
|
||||
test('should return Admin breadcrumbs when supplied endpoints pageName', () => {
|
||||
const breadcrumbs = getBreadcrumbsForRoute(
|
||||
getMockObject('administration', '/endpoints', undefined),
|
||||
getMockObject(SecurityPageName.administration, '/endpoints', undefined),
|
||||
getSecuritySolutionUrl,
|
||||
true
|
||||
);
|
||||
|
@ -781,9 +784,13 @@ describe('Navigation Breadcrumbs', () => {
|
|||
test('should call chrome breadcrumb service with correct breadcrumbs', () => {
|
||||
const navigateToUrlMock = jest.fn();
|
||||
const { result } = renderHook(() => useSetBreadcrumbs(), { wrapper: TestProviders });
|
||||
result.current(getMockObject('hosts', '/', hostName), chromeMock, navigateToUrlMock);
|
||||
result.current(
|
||||
getMockObject(SecurityPageName.hosts, '/', hostName),
|
||||
chromeMock,
|
||||
navigateToUrlMock
|
||||
);
|
||||
const searchString =
|
||||
"?sourcerer=()&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)))";
|
||||
|
||||
expect(setBreadcrumbsMock).toBeCalledWith([
|
||||
expect.objectContaining({
|
||||
|
|
|
@ -84,7 +84,7 @@ export const getBreadcrumbsForRoute = (
|
|||
}
|
||||
|
||||
const newMenuLeadingBreadcrumbs = getLeadingBreadcrumbsForSecurityPage(
|
||||
spyState.pageName as SecurityPageName,
|
||||
spyState.pageName,
|
||||
getSecuritySolutionUrl,
|
||||
object.navTabs,
|
||||
isGroupedNavigationEnabled
|
||||
|
|
|
@ -20,28 +20,31 @@ import {
|
|||
} from '../url_state/helpers';
|
||||
|
||||
import { SearchNavTab } from './types';
|
||||
import { SourcererUrlState } from '../../store/sourcerer/model';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features';
|
||||
import { useUiSetting$ } from '../../lib/kibana';
|
||||
import { ENABLE_GROUPED_NAVIGATION } from '../../../../common/constants';
|
||||
|
||||
export const getSearch = (tab: SearchNavTab, urlState: UrlState): string => {
|
||||
export const getSearch = (
|
||||
tab: SearchNavTab,
|
||||
urlState: UrlState,
|
||||
globalQueryString: string
|
||||
): string => {
|
||||
if (tab && tab.urlKey != null && !isAdministration(tab.urlKey)) {
|
||||
return getUrlStateSearch(urlState);
|
||||
// TODO: Temporary code while we are migrating all query strings to global_query_string_manager
|
||||
if (globalQueryString.length > 0) {
|
||||
return `${getUrlStateSearch(urlState)}&${globalQueryString}`;
|
||||
} else {
|
||||
return getUrlStateSearch(urlState);
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
export const getUrlStateSearch = (urlState: UrlState): string =>
|
||||
ALL_URL_STATE_KEYS.reduce<Location>(
|
||||
(myLocation: Location, urlKey: KeyUrlState) => {
|
||||
let urlStateToReplace:
|
||||
| Filter[]
|
||||
| Query
|
||||
| SourcererUrlState
|
||||
| TimelineUrl
|
||||
| UrlInputsModel
|
||||
| string = '';
|
||||
let urlStateToReplace: Filter[] | Query | TimelineUrl | UrlInputsModel | string = '';
|
||||
|
||||
if (urlKey === CONSTANTS.appQuery && urlState.query != null) {
|
||||
if (urlState.query.query === '') {
|
||||
|
@ -57,8 +60,6 @@ export const getUrlStateSearch = (urlState: UrlState): string =>
|
|||
}
|
||||
} else if (urlKey === CONSTANTS.timerange) {
|
||||
urlStateToReplace = urlState[CONSTANTS.timerange];
|
||||
} else if (urlKey === CONSTANTS.sourcerer) {
|
||||
urlStateToReplace = urlState[CONSTANTS.sourcerer];
|
||||
} else if (urlKey === CONSTANTS.timeline && urlState[CONSTANTS.timeline] != null) {
|
||||
const timeline = urlState[CONSTANTS.timeline];
|
||||
if (timeline.id === '') {
|
||||
|
|
|
@ -15,6 +15,7 @@ import { HostsTableType } from '../../../hosts/store/model';
|
|||
import { RouteSpyState } from '../../utils/route/types';
|
||||
import { TabNavigationComponentProps, SecuritySolutionTabNavigationProps } from './types';
|
||||
import { TimelineTabs } from '../../../../common/types/timeline';
|
||||
import { SecurityPageName } from '../../../app/types';
|
||||
|
||||
jest.mock('react-router-dom', () => {
|
||||
const original = jest.requireActual('react-router-dom');
|
||||
|
@ -61,7 +62,7 @@ describe('SIEM Navigation', () => {
|
|||
const mockProps: TabNavigationComponentProps &
|
||||
SecuritySolutionTabNavigationProps &
|
||||
RouteSpyState = {
|
||||
pageName: 'hosts',
|
||||
pageName: SecurityPageName.hosts,
|
||||
pathName: '/',
|
||||
detailName: undefined,
|
||||
search: '',
|
||||
|
@ -92,7 +93,6 @@ describe('SIEM Navigation', () => {
|
|||
},
|
||||
[CONSTANTS.appQuery]: { query: '', language: 'kuery' },
|
||||
[CONSTANTS.filters]: [],
|
||||
[CONSTANTS.sourcerer]: {},
|
||||
[CONSTANTS.timeline]: {
|
||||
activeTab: TimelineTabs.query,
|
||||
id: '',
|
||||
|
|
|
@ -84,7 +84,6 @@ export const TabNavigationComponent: React.FC<
|
|||
navTabs={navTabs}
|
||||
pageName={pageName}
|
||||
pathName={pathName}
|
||||
sourcerer={urlState.sourcerer}
|
||||
savedQuery={urlState.savedQuery}
|
||||
tabName={tabName}
|
||||
timeline={urlState.timeline}
|
||||
|
|
|
@ -130,7 +130,7 @@ const useSideNavItems = () => {
|
|||
const useSelectedId = (): SecurityPageName => {
|
||||
const [{ pageName }] = useRouteSpy();
|
||||
const selectedId = useMemo(() => {
|
||||
const [rootLinkInfo] = getAncestorLinksInfo(pageName as SecurityPageName);
|
||||
const [rootLinkInfo] = getAncestorLinksInfo(pageName);
|
||||
return rootLinkInfo?.id ?? '';
|
||||
}, [pageName]);
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import { RouteSpyState } from '../../../utils/route/types';
|
|||
import { CONSTANTS } from '../../url_state/constants';
|
||||
import { TabNavigationComponent } from '.';
|
||||
import { TabNavigationProps } from './types';
|
||||
import { SecurityPageName } from '../../../../app/types';
|
||||
|
||||
jest.mock('../../link_to');
|
||||
jest.mock('../../../lib/kibana/kibana_react', () => {
|
||||
|
@ -54,7 +55,7 @@ describe('Table Navigation', () => {
|
|||
const mockRiskyHostEnabled = true;
|
||||
|
||||
const mockProps: TabNavigationProps & RouteSpyState = {
|
||||
pageName: 'hosts',
|
||||
pageName: SecurityPageName.hosts,
|
||||
pathName: '/hosts',
|
||||
detailName: hostName,
|
||||
search: '',
|
||||
|
@ -89,7 +90,6 @@ describe('Table Navigation', () => {
|
|||
},
|
||||
[CONSTANTS.appQuery]: { query: 'host.name:"siem-es"', language: 'kuery' },
|
||||
[CONSTANTS.filters]: [],
|
||||
[CONSTANTS.sourcerer]: {},
|
||||
[CONSTANTS.timeline]: {
|
||||
activeTab: TimelineTabs.query,
|
||||
id: '',
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import type { Filter, Query } from '@kbn/es-query';
|
||||
import { UrlInputsModel } from '../../../store/inputs/model';
|
||||
import { CONSTANTS } from '../../url_state/constants';
|
||||
import { SourcererUrlState } from '../../../store/sourcerer/model';
|
||||
import { TimelineUrl } from '../../../../timelines/store/timeline/model';
|
||||
|
||||
import { SecuritySolutionTabNavigationProps } from '../types';
|
||||
|
@ -21,7 +20,6 @@ export interface TabNavigationProps extends SecuritySolutionTabNavigationProps {
|
|||
[CONSTANTS.appQuery]?: Query;
|
||||
[CONSTANTS.filters]?: Filter[];
|
||||
[CONSTANTS.savedQuery]?: string;
|
||||
[CONSTANTS.sourcerer]: SourcererUrlState;
|
||||
[CONSTANTS.timerange]: UrlInputsModel;
|
||||
[CONSTANTS.timeline]: TimelineUrl;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import { useDeepEqualSelector } from '../../hooks/use_selector';
|
||||
import { useGlobalQueryString } from '../../utils/global_query_string';
|
||||
import { makeMapStateToProps } from '../url_state/helpers';
|
||||
import { getSearch, getUrlStateSearch } from './helpers';
|
||||
import { SearchNavTab } from './types';
|
||||
|
@ -15,13 +16,27 @@ import { SearchNavTab } from './types';
|
|||
export const useGetUrlSearch = (tab?: SearchNavTab) => {
|
||||
const mapState = makeMapStateToProps();
|
||||
const { urlState } = useDeepEqualSelector(mapState);
|
||||
const urlSearch = useMemo(() => (tab ? getSearch(tab, urlState) : ''), [tab, urlState]);
|
||||
const globalQueryString = useGlobalQueryString();
|
||||
const urlSearch = useMemo(
|
||||
() => (tab ? getSearch(tab, urlState, globalQueryString) : ''),
|
||||
[tab, urlState, globalQueryString]
|
||||
);
|
||||
|
||||
return urlSearch;
|
||||
};
|
||||
|
||||
export const useGetUrlStateQueryString = () => {
|
||||
const mapState = makeMapStateToProps();
|
||||
const { urlState } = useDeepEqualSelector(mapState);
|
||||
const getUrlStateQueryString = useCallback(() => getUrlStateSearch(urlState), [urlState]);
|
||||
const globalQueryString = useGlobalQueryString();
|
||||
const getUrlStateQueryString = useCallback(() => {
|
||||
// TODO: Temporary code while we are migrating all query strings to global_query_string_manager
|
||||
if (globalQueryString.length > 0) {
|
||||
return `${getUrlStateSearch(urlState)}&${globalQueryString}`;
|
||||
}
|
||||
|
||||
return getUrlStateSearch(urlState);
|
||||
}, [urlState, globalQueryString]);
|
||||
|
||||
return getUrlStateQueryString;
|
||||
};
|
||||
|
|
|
@ -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')&sourcerer=()&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?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-test-subj": "navigation-get_started",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/get_started?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&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?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)))",
|
||||
"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')&sourcerer=()&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?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-test-subj": "navigation-overview",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/overview?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&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?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)))",
|
||||
"id": "overview",
|
||||
"isSelected": false,
|
||||
"name": "Overview",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/detection_response?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&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?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-test-subj": "navigation-detection_response",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/detection_response?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&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?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)))",
|
||||
"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')&sourcerer=()&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?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-test-subj": "navigation-alerts",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/alerts?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&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?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)))",
|
||||
"id": "alerts",
|
||||
"isSelected": false,
|
||||
"name": "Alerts",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/rules?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&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?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-test-subj": "navigation-rules",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/rules?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&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?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)))",
|
||||
"id": "rules",
|
||||
"isSelected": false,
|
||||
"name": "Rules",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/exceptions?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&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?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-test-subj": "navigation-exceptions",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/exceptions?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&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?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)))",
|
||||
"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')&sourcerer=()&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?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-test-subj": "navigation-hosts",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/hosts?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&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?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)))",
|
||||
"id": "hosts",
|
||||
"isSelected": true,
|
||||
"name": "Hosts",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/network?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&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?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-test-subj": "navigation-network",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/network?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&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?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)))",
|
||||
"id": "network",
|
||||
"isSelected": false,
|
||||
"name": "Network",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/users?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&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?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-test-subj": "navigation-users",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/users?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&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?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)))",
|
||||
"id": "users",
|
||||
"isSelected": false,
|
||||
"name": "Users",
|
||||
|
@ -122,10 +122,10 @@ Object {
|
|||
"id": "investigate",
|
||||
"items": Array [
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/timelines?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&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?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-test-subj": "navigation-timelines",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/timelines?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&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?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)))",
|
||||
"id": "timelines",
|
||||
"isSelected": false,
|
||||
"name": "Timelines",
|
||||
|
|
|
@ -32,7 +32,6 @@ describe('useSecuritySolutionNavigation', () => {
|
|||
const mockUrlState = {
|
||||
[CONSTANTS.appQuery]: { query: 'host.name:"security-solution-es"', language: 'kuery' },
|
||||
[CONSTANTS.savedQuery]: '',
|
||||
[CONSTANTS.sourcerer]: {},
|
||||
[CONSTANTS.timeline]: {
|
||||
activeTab: TimelineTabs.query,
|
||||
id: '',
|
||||
|
@ -165,10 +164,10 @@ describe('useSecuritySolutionNavigation', () => {
|
|||
);
|
||||
expect(caseNavItem).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/cases?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&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?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-test-subj": "navigation-cases",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/cases?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&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?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)))",
|
||||
"id": "cases",
|
||||
"isSelected": false,
|
||||
"name": "Cases",
|
||||
|
|
|
@ -76,7 +76,6 @@ export const useSecuritySolutionNavigation = () => {
|
|||
filters: urlState.filters,
|
||||
navTabs: enabledNavTabs,
|
||||
pageName,
|
||||
sourcerer: urlState.sourcerer,
|
||||
savedQuery: urlState.savedQuery,
|
||||
tabName,
|
||||
timeline: urlState.timeline,
|
||||
|
|
|
@ -18,6 +18,7 @@ import { NavTab, SecurityNavGroupKey } from '../types';
|
|||
import { SecurityPageName } from '../../../../../common/constants';
|
||||
import { useCanSeeHostIsolationExceptionsMenu } from '../../../../management/pages/host_isolation_exceptions/view/hooks';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features';
|
||||
import { useGlobalQueryString } from '../../../utils/global_query_string';
|
||||
|
||||
export const usePrimaryNavigationItems = ({
|
||||
navTabs,
|
||||
|
@ -25,11 +26,13 @@ export const usePrimaryNavigationItems = ({
|
|||
...urlStateProps
|
||||
}: PrimaryNavigationItemsProps): Array<EuiSideNavItemType<{}>> => {
|
||||
const { navigateTo, getAppUrl } = useNavigation();
|
||||
const globalQueryString = useGlobalQueryString();
|
||||
|
||||
const getSideNav = useCallback(
|
||||
(tab: NavTab) => {
|
||||
const { id, name, disabled } = tab;
|
||||
const isSelected = selectedTabId === id;
|
||||
const urlSearch = getSearch(tab, urlStateProps);
|
||||
const urlSearch = getSearch(tab, urlStateProps, globalQueryString);
|
||||
|
||||
const handleClick = (ev: React.MouseEvent) => {
|
||||
ev.preventDefault();
|
||||
|
@ -49,7 +52,7 @@ export const usePrimaryNavigationItems = ({
|
|||
onClick: handleClick,
|
||||
};
|
||||
},
|
||||
[getAppUrl, navigateTo, selectedTabId, urlStateProps]
|
||||
[getAppUrl, navigateTo, selectedTabId, urlStateProps, globalQueryString]
|
||||
);
|
||||
|
||||
const navItemsToDisplay = usePrimaryNavigationItemsToDisplay(navTabs);
|
||||
|
|
|
@ -24,7 +24,6 @@ export const usePrimaryNavigation = ({
|
|||
navTabs,
|
||||
pageName,
|
||||
savedQuery,
|
||||
sourcerer,
|
||||
tabName,
|
||||
timeline,
|
||||
timerange,
|
||||
|
@ -53,7 +52,6 @@ export const usePrimaryNavigation = ({
|
|||
filters,
|
||||
query,
|
||||
savedQuery,
|
||||
sourcerer,
|
||||
timeline,
|
||||
timerange,
|
||||
});
|
||||
|
|
|
@ -54,6 +54,16 @@ jest.mock('@kbn/kibana-react-plugin/public', () => {
|
|||
};
|
||||
});
|
||||
|
||||
const mockUpdateUrlParam = jest.fn();
|
||||
jest.mock('../../utils/global_query_string', () => {
|
||||
const original = jest.requireActual('../../utils/global_query_string');
|
||||
|
||||
return {
|
||||
...original,
|
||||
useUpdateUrlParam: () => mockUpdateUrlParam,
|
||||
};
|
||||
});
|
||||
|
||||
const mockOptions = DEFAULT_INDEX_PATTERN.map((index) => ({ label: index, value: index }));
|
||||
|
||||
const defaultProps = {
|
||||
|
@ -411,6 +421,50 @@ describe('Sourcerer component', () => {
|
|||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('onSave updates the URL param', () => {
|
||||
store = createStore(
|
||||
{
|
||||
...mockGlobalState,
|
||||
sourcerer: {
|
||||
...mockGlobalState.sourcerer,
|
||||
kibanaDataViews: [
|
||||
mockGlobalState.sourcerer.defaultDataView,
|
||||
{
|
||||
...mockGlobalState.sourcerer.defaultDataView,
|
||||
id: '1234',
|
||||
title: 'filebeat-*',
|
||||
patternList: ['filebeat-*'],
|
||||
},
|
||||
],
|
||||
sourcererScopes: {
|
||||
...mockGlobalState.sourcerer.sourcererScopes,
|
||||
[SourcererScopeName.default]: {
|
||||
...mockGlobalState.sourcerer.sourcererScopes[SourcererScopeName.default],
|
||||
selectedDataViewId: id,
|
||||
selectedPatterns: patternListNoSignals.slice(0, 2),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
SUB_PLUGINS_REDUCER,
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
const wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<Sourcerer {...defaultProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
wrapper.find(`[data-test-subj="sourcerer-trigger"]`).first().simulate('click');
|
||||
wrapper.find(`[data-test-subj="comboBoxInput"]`).first().simulate('click');
|
||||
wrapper.find(`[data-test-subj="sourcerer-combo-option"]`).first().simulate('click');
|
||||
wrapper.find(`[data-test-subj="sourcerer-save"]`).first().simulate('click');
|
||||
|
||||
expect(mockUpdateUrlParam).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('resets to default index pattern', async () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
|
|
|
@ -20,7 +20,7 @@ import { useDispatch } from 'react-redux';
|
|||
import * as i18n from './translations';
|
||||
import { sourcererActions, sourcererModel, sourcererSelectors } from '../../store/sourcerer';
|
||||
import { useDeepEqualSelector } from '../../hooks/use_selector';
|
||||
import { SourcererScopeName } from '../../store/sourcerer/model';
|
||||
import { SourcererScopeName, SourcererUrlState } from '../../store/sourcerer/model';
|
||||
import { usePickIndexPatterns } from './use_pick_index_patterns';
|
||||
import { FormRow, PopoverContent, StyledButton, StyledFormRow } from './helpers';
|
||||
import { TemporarySourcerer } from './temporary';
|
||||
|
@ -29,6 +29,8 @@ import { useUpdateDataView } from './use_update_data_view';
|
|||
import { Trigger } from './trigger';
|
||||
import { AlertsCheckbox, SaveButtons, SourcererCallout } from './sub_components';
|
||||
import { useSignalHelpers } from '../../containers/sourcerer/use_signal_helpers';
|
||||
import { useUpdateUrlParam } from '../../utils/global_query_string';
|
||||
import { CONSTANTS } from '../url_state/constants';
|
||||
|
||||
export interface SourcererComponentProps {
|
||||
scope: sourcererModel.SourcererScopeName;
|
||||
|
@ -38,6 +40,8 @@ export const Sourcerer = React.memo<SourcererComponentProps>(({ scope: scopeId }
|
|||
const dispatch = useDispatch();
|
||||
const isDetectionsSourcerer = scopeId === SourcererScopeName.detections;
|
||||
const isTimelineSourcerer = scopeId === SourcererScopeName.timeline;
|
||||
const isDefaultSourcerer = scopeId === SourcererScopeName.default;
|
||||
const updateUrlParam = useUpdateUrlParam<SourcererUrlState>(CONSTANTS.sourcerer);
|
||||
|
||||
const sourcererScopeSelector = useMemo(() => sourcererSelectors.getSourcererScopeSelector(), []);
|
||||
const {
|
||||
|
@ -144,8 +148,17 @@ export const Sourcerer = React.memo<SourcererComponentProps>(({ scope: scopeId }
|
|||
shouldValidateSelectedPatterns,
|
||||
})
|
||||
);
|
||||
|
||||
if (isDefaultSourcerer) {
|
||||
updateUrlParam({
|
||||
[SourcererScopeName.default]: {
|
||||
id: newSelectedDataView,
|
||||
selectedPatterns: newSelectedPatterns,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
[dispatch, scopeId]
|
||||
[dispatch, scopeId, isDefaultSourcerer, updateUrlParam]
|
||||
);
|
||||
|
||||
const onChangeDataView = useCallback(
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { navTabs } from '../../../app/home/home_navigations';
|
||||
import { getTitle, isQueryStateEmpty } from './helpers';
|
||||
import { CONSTANTS } from './constants';
|
||||
import { ValueUrlState } from './types';
|
||||
|
||||
describe('Helpers Url_State', () => {
|
||||
describe('getTitle', () => {
|
||||
|
@ -45,7 +46,7 @@ describe('Helpers Url_State', () => {
|
|||
});
|
||||
|
||||
test('returns true if url key is "query" and queryState is empty string', () => {
|
||||
const result = isQueryStateEmpty({}, CONSTANTS.appQuery);
|
||||
const result = isQueryStateEmpty('', CONSTANTS.appQuery);
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
|
@ -72,7 +73,7 @@ describe('Helpers Url_State', () => {
|
|||
|
||||
// TODO: Is this a bug, or intended?
|
||||
test('returns false if url key is "timeline" and queryState is empty', () => {
|
||||
const result = isQueryStateEmpty({}, CONSTANTS.timeline);
|
||||
const result = isQueryStateEmpty({} as ValueUrlState, CONSTANTS.timeline);
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
||||
|
|
|
@ -24,8 +24,6 @@ import { formatDate } from '../super_date_picker';
|
|||
import { NavTab } from '../navigation/types';
|
||||
import { CONSTANTS, UrlStateType } from './constants';
|
||||
import { ReplaceStateInLocation, KeyUrlState, ValueUrlState } from './types';
|
||||
import { sourcererSelectors } from '../../store/sourcerer';
|
||||
import { SourcererScopeName, SourcererUrlState } from '../../store/sourcerer/model';
|
||||
|
||||
export const isDetectionsPages = (pageName: string) =>
|
||||
pageName === SecurityPageName.alerts ||
|
||||
|
@ -49,7 +47,10 @@ export const encodeRisonUrlState = (state: any) => encode(state);
|
|||
|
||||
export const getQueryStringFromLocation = (search: string) => search.substring(1);
|
||||
|
||||
export const getParamFromQueryString = (queryString: string, key: string) => {
|
||||
export const getParamFromQueryString = (
|
||||
queryString: string,
|
||||
key: string
|
||||
): string | undefined | null => {
|
||||
const parsedQueryString = parse(queryString, { sort: false });
|
||||
const queryParam = parsedQueryString[key];
|
||||
|
||||
|
@ -128,7 +129,6 @@ export const makeMapStateToProps = () => {
|
|||
const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector();
|
||||
const getGlobalSavedQuerySelector = inputsSelectors.globalSavedQuerySelector();
|
||||
const getTimeline = timelineSelectors.getTimelineByIdSelector();
|
||||
const getSourcererScopes = sourcererSelectors.scopesSelector();
|
||||
const mapStateToProps = (state: State) => {
|
||||
const inputState = getInputsSelector(state);
|
||||
const { linkTo: globalLinkTo, timerange: globalTimerange } = inputState.global;
|
||||
|
@ -159,25 +159,10 @@ export const makeMapStateToProps = () => {
|
|||
[CONSTANTS.savedQuery]: savedQuery.id,
|
||||
};
|
||||
}
|
||||
const sourcerer = getSourcererScopes(state);
|
||||
const activeScopes: SourcererScopeName[] = Object.keys(sourcerer) as SourcererScopeName[];
|
||||
const selectedPatterns: SourcererUrlState = activeScopes
|
||||
.filter((scope) => scope === SourcererScopeName.default)
|
||||
.reduce(
|
||||
(acc, scope) => ({
|
||||
...acc,
|
||||
[scope]: {
|
||||
id: sourcerer[scope]?.selectedDataViewId,
|
||||
selectedPatterns: sourcerer[scope]?.selectedPatterns,
|
||||
},
|
||||
}),
|
||||
{}
|
||||
);
|
||||
|
||||
return {
|
||||
urlState: {
|
||||
...searchAttr,
|
||||
[CONSTANTS.sourcerer]: selectedPatterns,
|
||||
[CONSTANTS.timerange]: {
|
||||
global: {
|
||||
[CONSTANTS.timerange]: globalTimerange,
|
||||
|
|
|
@ -125,6 +125,7 @@ describe('UrlStateContainer', () => {
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
mount(<HookWrapper hookProps={mockProps} hook={(args) => useUrlStateHooks(args)} />);
|
||||
|
@ -159,6 +160,7 @@ describe('UrlStateContainer', () => {
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
mount(<HookWrapper hookProps={mockProps} hook={(args) => useUrlStateHooks(args)} />);
|
||||
|
@ -189,6 +191,7 @@ describe('UrlStateContainer', () => {
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
mount(<HookWrapper hookProps={mockProps} hook={(args) => useUrlStateHooks(args)} />);
|
||||
|
@ -218,6 +221,7 @@ describe('UrlStateContainer', () => {
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
mount(<HookWrapper hookProps={mockProps} hook={(args) => useUrlStateHooks(args)} />);
|
||||
|
@ -227,7 +231,7 @@ describe('UrlStateContainer', () => {
|
|||
).toEqual({
|
||||
hash: '',
|
||||
pathname: examplePath,
|
||||
search: `?query=(language:kuery,query:'host.name:%22siem-es%22')&sourcerer=()&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: `?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: '',
|
||||
});
|
||||
}
|
||||
|
@ -246,6 +250,7 @@ describe('UrlStateContainer', () => {
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: 'out of sync path',
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
mount(<HookWrapper hookProps={mockProps} hook={(args) => useUrlStateHooks(args)} />);
|
||||
|
@ -265,6 +270,7 @@ describe('UrlStateContainer', () => {
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
mount(<HookWrapper hookProps={mockProps} hook={(args) => useUrlStateHooks(args)} />);
|
||||
|
@ -284,6 +290,7 @@ describe('UrlStateContainer', () => {
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
mount(<HookWrapper hookProps={mockProps} hook={(args) => useUrlStateHooks(args)} />);
|
||||
|
@ -309,6 +316,7 @@ describe('UrlStateContainer', () => {
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
mount(<HookWrapper hookProps={mockProps} hook={(args) => useUrlStateHooks(args)} />);
|
||||
|
@ -334,6 +342,7 @@ describe('UrlStateContainer', () => {
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
mount(<HookWrapper hookProps={mockProps} hook={(args) => useUrlStateHooks(args)} />);
|
||||
|
@ -356,6 +365,7 @@ describe('UrlStateContainer', () => {
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
const wrapper = mount(
|
||||
|
|
|
@ -88,6 +88,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
const wrapper = mount(
|
||||
|
@ -126,7 +127,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
hash: '',
|
||||
pathname: '/network',
|
||||
search:
|
||||
"?query=(language:kuery,query:'host.name:%22siem-es%22')&sourcerer=()&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)))",
|
||||
"?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: '',
|
||||
});
|
||||
});
|
||||
|
@ -142,6 +143,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
const wrapper = mount(
|
||||
|
@ -160,7 +162,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
hash: '',
|
||||
pathname: '/network',
|
||||
search:
|
||||
"?query=(language:kuery,query:'host.name:%22siem-es%22')&sourcerer=()&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)))",
|
||||
"?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: '',
|
||||
});
|
||||
});
|
||||
|
@ -176,6 +178,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
const wrapper = mount(
|
||||
|
@ -195,42 +198,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
hash: '',
|
||||
pathname: '/network',
|
||||
search:
|
||||
"?sourcerer=()&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)))&timeline=(id:hello_timeline_id,isOpen:!t)",
|
||||
state: '',
|
||||
});
|
||||
});
|
||||
|
||||
test('sourcerer 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,
|
||||
});
|
||||
|
||||
const wrapper = mount(
|
||||
<HookWrapper hookProps={mockProps} hook={(args) => useUrlStateHooks(args)} />
|
||||
);
|
||||
const newUrlState = {
|
||||
...mockProps.urlState,
|
||||
sourcerer: ['cool', 'patterns'],
|
||||
};
|
||||
|
||||
wrapper.setProps({
|
||||
hookProps: { ...mockProps, urlState: newUrlState, isInitializing: false },
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
expect(mockHistory.replace.mock.calls[1][0]).toStrictEqual({
|
||||
hash: '',
|
||||
pathname: '/network',
|
||||
search:
|
||||
"?sourcerer=!(cool,patterns)&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)))&timeline=(id:hello_timeline_id,isOpen:!t)",
|
||||
state: '',
|
||||
});
|
||||
});
|
||||
|
@ -286,6 +254,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
const wrapper = mount(
|
||||
|
@ -297,6 +266,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: updatedMockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
wrapper.setProps({
|
||||
|
@ -307,7 +277,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
expect(mockHistory.replace.mock.calls[1][0]).toStrictEqual({
|
||||
hash: '',
|
||||
pathname: MANAGEMENT_PATH,
|
||||
search: '?',
|
||||
search: mockProps.search,
|
||||
state: '',
|
||||
});
|
||||
});
|
||||
|
@ -363,6 +333,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
const wrapper = mount(
|
||||
|
@ -374,6 +345,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: updatedMockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
wrapper.setProps({
|
||||
|
@ -384,7 +356,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
expect(mockHistory.replace.mock.calls[1][0]).toStrictEqual({
|
||||
hash: '',
|
||||
pathname: DASHBOARDS_PATH,
|
||||
search: '?',
|
||||
search: mockProps.search,
|
||||
state: '',
|
||||
});
|
||||
});
|
||||
|
@ -401,6 +373,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
mount(<HookWrapper hookProps={mockProps} hook={(args) => useUrlStateHooks(args)} />);
|
||||
|
@ -409,7 +382,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
hash: '',
|
||||
pathname: examplePath,
|
||||
search:
|
||||
"?sourcerer=()&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)))",
|
||||
state: '',
|
||||
});
|
||||
}
|
||||
|
@ -433,6 +406,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
const wrapper = mount(
|
||||
|
@ -440,11 +414,12 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
);
|
||||
|
||||
expect(mockHistory.replace.mock.calls[0][0].search).toEqual(
|
||||
"?sourcerer=()&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)))"
|
||||
);
|
||||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: updatedProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
wrapper.setProps({ hookProps: updatedProps });
|
||||
|
@ -452,7 +427,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')&sourcerer=()&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)))"
|
||||
"?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)))"
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -478,6 +453,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: mockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
const wrapper = mount(
|
||||
|
@ -485,11 +461,12 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
|
|||
);
|
||||
|
||||
expect(mockHistory.replace.mock.calls[0][0].search).toEqual(
|
||||
"?sourcerer=()&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)))"
|
||||
);
|
||||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
pathname: updatedMockProps.pathName,
|
||||
search: mockProps.search,
|
||||
});
|
||||
|
||||
wrapper.setProps({ hookProps: updatedMockProps });
|
||||
|
|
|
@ -11,7 +11,7 @@ import { Dispatch } from 'redux';
|
|||
import { useCallback, useMemo } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import type { Filter, Query } from '@kbn/es-query';
|
||||
import { inputsActions, sourcererActions } from '../../store/actions';
|
||||
import { inputsActions } from '../../store/actions';
|
||||
import { InputsModelId, TimeRangeKinds } from '../../store/inputs/constants';
|
||||
import {
|
||||
UrlInputsModel,
|
||||
|
@ -21,14 +21,13 @@ import {
|
|||
} from '../../store/inputs/model';
|
||||
import { TimelineUrl } from '../../../timelines/store/timeline/model';
|
||||
import { CONSTANTS } from './constants';
|
||||
import { decodeRisonUrlState, isDetectionsPages } from './helpers';
|
||||
import { decodeRisonUrlState } from './helpers';
|
||||
import { normalizeTimeRange } from './normalize_time_range';
|
||||
import { SetInitialStateFromUrl } from './types';
|
||||
import {
|
||||
queryTimelineById,
|
||||
dispatchUpdateTimeline,
|
||||
} from '../../../timelines/components/open_timeline/helpers';
|
||||
import { SourcererScopeName, SourcererUrlState } from '../../store/sourcerer/model';
|
||||
import { timelineActions } from '../../../timelines/store/timeline';
|
||||
|
||||
export const useSetInitialStateFromUrl = () => {
|
||||
|
@ -54,23 +53,6 @@ export const useSetInitialStateFromUrl = () => {
|
|||
if (urlKey === CONSTANTS.timerange) {
|
||||
updateTimerange(newUrlStateString, dispatch);
|
||||
}
|
||||
if (urlKey === CONSTANTS.sourcerer) {
|
||||
const sourcererState = decodeRisonUrlState<SourcererUrlState>(newUrlStateString);
|
||||
if (sourcererState != null) {
|
||||
const activeScopes: SourcererScopeName[] = Object.keys(sourcererState).filter(
|
||||
(key) => !(key === SourcererScopeName.default && isDetectionsPages(pageName))
|
||||
) as SourcererScopeName[];
|
||||
activeScopes.forEach((scope) =>
|
||||
dispatch(
|
||||
sourcererActions.setSelectedDataView({
|
||||
id: scope,
|
||||
selectedDataViewId: sourcererState[scope]?.id ?? null,
|
||||
selectedPatterns: sourcererState[scope]?.selectedPatterns ?? [],
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (urlKey === CONSTANTS.appQuery && indexPattern != null) {
|
||||
const appQuery = decodeRisonUrlState<Query>(newUrlStateString);
|
||||
|
|
|
@ -120,7 +120,6 @@ export const defaultProps: UrlStateContainerPropTypes = {
|
|||
id: '',
|
||||
isOpen: false,
|
||||
},
|
||||
[CONSTANTS.sourcerer]: {},
|
||||
},
|
||||
history: {
|
||||
...mockHistory,
|
||||
|
@ -132,7 +131,7 @@ export const getMockProps = (
|
|||
location = defaultLocation,
|
||||
kqlQueryKey = CONSTANTS.networkPage,
|
||||
kqlQueryValue: Query | null,
|
||||
pageName: string,
|
||||
pageName: SecurityPageName,
|
||||
detailName: string | undefined
|
||||
): UrlStateContainerPropTypes => ({
|
||||
...defaultProps,
|
||||
|
@ -154,7 +153,7 @@ interface GetMockPropsObj {
|
|||
examplePath: string;
|
||||
namespaceLower: string;
|
||||
page: LocationTypes;
|
||||
pageName: string;
|
||||
pageName: SecurityPageName;
|
||||
detailName: string | undefined;
|
||||
}
|
||||
|
||||
|
@ -270,7 +269,7 @@ export const getMockPropsObj = ({ page, examplePath, pageName, detailName }: Get
|
|||
// silly that this needs to be an array and not an object
|
||||
// https://jestjs.io/docs/en/api#testeachtable-name-fn-timeout
|
||||
export const testCases: Array<
|
||||
[LocationTypes, string, string, string, string | null, string, undefined | string]
|
||||
[LocationTypes, string, string, string, string | null, SecurityPageName, undefined | string]
|
||||
> = [
|
||||
[
|
||||
/* page */ CONSTANTS.networkPage,
|
||||
|
|
|
@ -13,13 +13,11 @@ import { RouteSpyState } from '../../utils/route/types';
|
|||
import { SecurityNav } from '../navigation/types';
|
||||
|
||||
import { CONSTANTS, UrlStateType } from './constants';
|
||||
import { SourcererUrlState } from '../../store/sourcerer/model';
|
||||
|
||||
export const ALL_URL_STATE_KEYS: KeyUrlState[] = [
|
||||
CONSTANTS.appQuery,
|
||||
CONSTANTS.filters,
|
||||
CONSTANTS.savedQuery,
|
||||
CONSTANTS.sourcerer,
|
||||
CONSTANTS.timerange,
|
||||
CONSTANTS.timeline,
|
||||
];
|
||||
|
@ -43,7 +41,6 @@ export interface UrlState {
|
|||
[CONSTANTS.appQuery]?: Query;
|
||||
[CONSTANTS.filters]?: Filter[];
|
||||
[CONSTANTS.savedQuery]?: string;
|
||||
[CONSTANTS.sourcerer]: SourcererUrlState;
|
||||
[CONSTANTS.timerange]: UrlInputsModel;
|
||||
[CONSTANTS.timeline]: TimelineUrl;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ import { TimelineUrl } from '../../../timelines/store/timeline/model';
|
|||
import { UrlInputsModel } from '../../store/inputs/model';
|
||||
import { queryTimelineByIdOnUrlChange } from './query_timeline_by_id_on_url_change';
|
||||
import { getLinkInfo } from '../../links';
|
||||
import { SecurityPageName } from '../../../app/types';
|
||||
import { useIsGroupedNavigationEnabled } from '../navigation/helpers';
|
||||
|
||||
function usePrevious(value: PreviousLocationUrlState) {
|
||||
|
@ -57,17 +56,16 @@ export const useUrlStateHooks = ({
|
|||
navTabs,
|
||||
pageName,
|
||||
urlState,
|
||||
search,
|
||||
pathName,
|
||||
history,
|
||||
}: UrlStateContainerPropTypes) => {
|
||||
const [isFirstPageLoad, setIsFirstPageLoad] = useState(true);
|
||||
const { filterManager, savedQueries } = useKibana().services.data.query;
|
||||
const { pathname: browserPathName } = useLocation();
|
||||
const { pathname: browserPathName, search } = useLocation();
|
||||
const prevProps = usePrevious({ pathName, pageName, urlState, search });
|
||||
const isGroupedNavEnabled = useIsGroupedNavigationEnabled();
|
||||
|
||||
const linkInfo = pageName ? getLinkInfo(pageName as SecurityPageName) : undefined;
|
||||
const linkInfo = pageName ? getLinkInfo(pageName) : undefined;
|
||||
const { setInitialStateFromUrl, updateTimeline, updateTimelineIsLoading } =
|
||||
useSetInitialStateFromUrl();
|
||||
|
||||
|
|
|
@ -13,7 +13,11 @@ import { Provider } from 'react-redux';
|
|||
import { getScopeFromPath, useInitSourcerer, useSourcererDataView } from '.';
|
||||
import { mockPatterns } from './mocks';
|
||||
import { RouteSpyState } from '../../utils/route/types';
|
||||
import { DEFAULT_INDEX_PATTERN, SecurityPageName } from '../../../../common/constants';
|
||||
import {
|
||||
DEFAULT_DATA_VIEW_ID,
|
||||
DEFAULT_INDEX_PATTERN,
|
||||
SecurityPageName,
|
||||
} from '../../../../common/constants';
|
||||
import { createStore } from '../../store';
|
||||
import {
|
||||
useUserInfo,
|
||||
|
@ -25,9 +29,12 @@ import {
|
|||
mockGlobalState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
mockSourcererState,
|
||||
TestProviders,
|
||||
} from '../../mock';
|
||||
import { SelectedDataView, SourcererScopeName } from '../../store/sourcerer/model';
|
||||
import { postSourcererDataView } from './api';
|
||||
import { sourcererActions } from '../../store/sourcerer';
|
||||
import { useInitializeUrlParam, useUpdateUrlParam } from '../../utils/global_query_string';
|
||||
|
||||
const mockRouteSpy: RouteSpyState = {
|
||||
pageName: SecurityPageName.overview,
|
||||
|
@ -40,6 +47,7 @@ const mockDispatch = jest.fn();
|
|||
const mockUseUserInfo = useUserInfo as jest.Mock;
|
||||
jest.mock('../../../detections/components/user_info');
|
||||
jest.mock('./api');
|
||||
jest.mock('../../utils/global_query_string');
|
||||
jest.mock('react-redux', () => {
|
||||
const original = jest.requireActual('react-redux');
|
||||
|
||||
|
@ -52,6 +60,8 @@ jest.mock('../../utils/route/use_route_spy', () => ({
|
|||
useRouteSpy: () => [mockRouteSpy],
|
||||
}));
|
||||
|
||||
(useInitializeUrlParam as jest.Mock).mockImplementation((_, onInitialize) => onInitialize({}));
|
||||
|
||||
const mockSearch = jest.fn();
|
||||
|
||||
const mockAddWarning = jest.fn();
|
||||
|
@ -188,6 +198,50 @@ describe('Sourcerer Hooks', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('initilizes dataview with data from query string', async () => {
|
||||
const selectedPatterns = ['testPattern-*'];
|
||||
const selectedDataViewId = 'security-solution-default';
|
||||
(useInitializeUrlParam as jest.Mock).mockImplementation((_, onInitialize) =>
|
||||
onInitialize({
|
||||
[SourcererScopeName.default]: {
|
||||
id: selectedDataViewId,
|
||||
selectedPatterns,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
renderHook<string, void>(() => useInitSourcerer(), {
|
||||
wrapper: ({ children }) => <TestProviders>{children}</TestProviders>,
|
||||
});
|
||||
|
||||
expect(mockDispatch).toHaveBeenCalledWith(
|
||||
sourcererActions.setSelectedDataView({
|
||||
id: SourcererScopeName.default,
|
||||
selectedDataViewId,
|
||||
selectedPatterns,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('sets default selected patterns to the URL when there is no sorcerer URL param in the query string', async () => {
|
||||
const updateUrlParam = jest.fn();
|
||||
(useUpdateUrlParam as jest.Mock).mockReturnValue(updateUrlParam);
|
||||
(useInitializeUrlParam as jest.Mock).mockImplementation((_, onInitialize) =>
|
||||
onInitialize(null)
|
||||
);
|
||||
|
||||
renderHook<string, void>(() => useInitSourcerer(), {
|
||||
wrapper: ({ children }) => <TestProviders>{children}</TestProviders>,
|
||||
});
|
||||
|
||||
expect(updateUrlParam).toHaveBeenCalledWith({
|
||||
[SourcererScopeName.default]: {
|
||||
id: DEFAULT_DATA_VIEW_ID,
|
||||
selectedPatterns: DEFAULT_INDEX_PATTERN,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('calls addWarning if defaultDataView has an error', async () => {
|
||||
store = createStore(
|
||||
{
|
||||
|
|
|
@ -14,17 +14,18 @@ import {
|
|||
SelectedDataView,
|
||||
SourcererDataView,
|
||||
SourcererScopeName,
|
||||
SourcererUrlState,
|
||||
} from '../../store/sourcerer/model';
|
||||
import { useUserInfo } from '../../../detections/components/user_info';
|
||||
import { timelineSelectors } from '../../../timelines/store/timeline';
|
||||
import {
|
||||
ALERTS_PATH,
|
||||
CASES_PATH,
|
||||
HOSTS_PATH,
|
||||
USERS_PATH,
|
||||
NETWORK_PATH,
|
||||
OVERVIEW_PATH,
|
||||
RULES_PATH,
|
||||
CASES_PATH,
|
||||
} from '../../../../common/constants';
|
||||
import { TimelineId } from '../../../../common/types';
|
||||
import { useDeepEqualSelector } from '../../hooks/use_selector';
|
||||
|
@ -37,6 +38,8 @@ import { useAppToasts } from '../../hooks/use_app_toasts';
|
|||
import { postSourcererDataView } from './api';
|
||||
import { useDataView } from '../source/use_data_view';
|
||||
import { useFetchIndex } from '../source';
|
||||
import { useInitializeUrlParam, useUpdateUrlParam } from '../../utils/global_query_string';
|
||||
import { CONSTANTS } from '../../components/url_state/constants';
|
||||
|
||||
export const useInitSourcerer = (
|
||||
scopeId: SourcererScopeName.default | SourcererScopeName.detections = SourcererScopeName.default
|
||||
|
@ -46,6 +49,7 @@ export const useInitSourcerer = (
|
|||
const initialTimelineSourcerer = useRef(true);
|
||||
const initialDetectionSourcerer = useRef(true);
|
||||
const { loading: loadingSignalIndex, isSignalIndexExists, signalIndexName } = useUserInfo();
|
||||
const updateUrlParam = useUpdateUrlParam<SourcererUrlState>(CONSTANTS.sourcerer);
|
||||
|
||||
const getDataViewsSelector = useMemo(
|
||||
() => sourcererSelectors.getSourcererDataViewsSelector(),
|
||||
|
@ -88,6 +92,41 @@ export const useInitSourcerer = (
|
|||
} = useDeepEqualSelector((state) => scopeIdSelector(state, SourcererScopeName.timeline));
|
||||
const { indexFieldsSearch } = useDataView();
|
||||
|
||||
const onInitializeUrlParam = useCallback(
|
||||
(initialState: SourcererUrlState | null) => {
|
||||
// Initialize the store with value from UrlParam.
|
||||
if (initialState != null) {
|
||||
(Object.keys(initialState) as SourcererScopeName[]).forEach((scope) => {
|
||||
if (
|
||||
!(scope === SourcererScopeName.default && scopeId === SourcererScopeName.detections)
|
||||
) {
|
||||
dispatch(
|
||||
sourcererActions.setSelectedDataView({
|
||||
id: scope,
|
||||
selectedDataViewId: initialState[scope]?.id ?? null,
|
||||
selectedPatterns: initialState[scope]?.selectedPatterns ?? [],
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Initialize the UrlParam with values from the store.
|
||||
// It isn't strictly necessary but I am keeping it for compatibility with the previous implementation.
|
||||
if (scopeDataViewId) {
|
||||
updateUrlParam({
|
||||
[SourcererScopeName.default]: {
|
||||
id: scopeDataViewId,
|
||||
selectedPatterns,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
[dispatch, scopeDataViewId, scopeId, selectedPatterns, updateUrlParam]
|
||||
);
|
||||
|
||||
useInitializeUrlParam<SourcererUrlState>(CONSTANTS.sourcerer, onInitializeUrlParam);
|
||||
|
||||
/*
|
||||
* Note for future engineer:
|
||||
* we changed the logic to not fetch all the index fields for every data view on the loading of the app
|
||||
|
|
|
@ -388,6 +388,7 @@ export const mockGlobalState: State = {
|
|||
},
|
||||
},
|
||||
},
|
||||
globalUrlParam: {},
|
||||
/**
|
||||
* These state's are wrapped in `Immutable`, but for compatibility with the overall app architecture,
|
||||
* they are cast to mutable versions here.
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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 actionCreatorFactory from 'typescript-fsa';
|
||||
|
||||
const actionCreator = actionCreatorFactory('x-pack/security_solution/local/global_url_param');
|
||||
|
||||
export const registerUrlParam = actionCreator<{ key: string; initialValue: string | null }>(
|
||||
'REGISTER_URL_PARAM'
|
||||
);
|
||||
|
||||
export const deregisterUrlParam = actionCreator<{ key: string }>('DEREGISTER_URL_PARAM');
|
||||
|
||||
export const updateUrlParam = actionCreator<{ key: string; value: string | null }>(
|
||||
'UPDATE_URL_PARAM'
|
||||
);
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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 * as globalUrlParamActions from './actions';
|
||||
import * as globalUrlParamSelectors from './selectors';
|
||||
|
||||
export { globalUrlParamActions, globalUrlParamSelectors };
|
||||
|
||||
export * from './reducer';
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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 { deregisterUrlParam, registerUrlParam, updateUrlParam } from './actions';
|
||||
import { globalUrlParamReducer, initialGlobalUrlParam } from './reducer';
|
||||
|
||||
const error = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
describe('globalUrlParamReducer', () => {
|
||||
describe('#registerUrlParam', () => {
|
||||
it('registers the URL param', () => {
|
||||
const key = 'testKey';
|
||||
const initialValue = 'testValue';
|
||||
const state = globalUrlParamReducer(
|
||||
initialGlobalUrlParam,
|
||||
registerUrlParam({ key, initialValue })
|
||||
);
|
||||
|
||||
expect(state).toEqual({ [key]: initialValue });
|
||||
});
|
||||
|
||||
it('throws exception when a key is register twice', () => {
|
||||
const key = 'testKey';
|
||||
const initialValue = 'testValue';
|
||||
const newState = globalUrlParamReducer(
|
||||
initialGlobalUrlParam,
|
||||
registerUrlParam({ key, initialValue })
|
||||
);
|
||||
|
||||
globalUrlParamReducer(newState, registerUrlParam({ key, initialValue }));
|
||||
|
||||
expect(error).toHaveBeenCalledWith("Url param key 'testKey' is already being used.");
|
||||
});
|
||||
});
|
||||
|
||||
describe('#deregisterUrlParam', () => {
|
||||
it('deregisters the URL param', () => {
|
||||
const key = 'testKey';
|
||||
const initialValue = 'testValue';
|
||||
let state = globalUrlParamReducer(
|
||||
initialGlobalUrlParam,
|
||||
registerUrlParam({ key, initialValue })
|
||||
);
|
||||
|
||||
expect(state).toEqual({ [key]: initialValue });
|
||||
|
||||
state = globalUrlParamReducer(initialGlobalUrlParam, deregisterUrlParam({ key }));
|
||||
|
||||
expect(state).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#updateUrlParam', () => {
|
||||
it('updates URL param', () => {
|
||||
const key = 'testKey';
|
||||
const value = 'new test value';
|
||||
|
||||
const state = globalUrlParamReducer(
|
||||
{ [key]: 'old test value' },
|
||||
updateUrlParam({ key, value })
|
||||
);
|
||||
|
||||
expect(state).toEqual({ [key]: value });
|
||||
});
|
||||
|
||||
it("doesn't update the URL param if key isn't registered", () => {
|
||||
const key = 'testKey';
|
||||
const value = 'testValue';
|
||||
|
||||
const state = globalUrlParamReducer(initialGlobalUrlParam, updateUrlParam({ key, value }));
|
||||
|
||||
expect(state).toEqual(initialGlobalUrlParam);
|
||||
});
|
||||
|
||||
it("doesn't update the state if new value is equal to store value", () => {
|
||||
const key = 'testKey';
|
||||
const value = 'testValue';
|
||||
const intialState = { [key]: value };
|
||||
|
||||
const state = globalUrlParamReducer(intialState, updateUrlParam({ key, value }));
|
||||
|
||||
expect(state).toBe(intialState);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 { reducerWithInitialState } from 'typescript-fsa-reducers';
|
||||
import { registerUrlParam, updateUrlParam, deregisterUrlParam } from './actions';
|
||||
|
||||
export type GlobalUrlParam = Record<string, string | null>;
|
||||
|
||||
export const initialGlobalUrlParam: GlobalUrlParam = {};
|
||||
|
||||
export const globalUrlParamReducer = reducerWithInitialState(initialGlobalUrlParam)
|
||||
.case(registerUrlParam, (state, { key, initialValue }) => {
|
||||
// It doesn't allow the query param to be used twice
|
||||
if (state[key] !== undefined) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Url param key '${key}' is already being used.`);
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
[key]: initialValue,
|
||||
};
|
||||
})
|
||||
.case(deregisterUrlParam, (state, { key }) => {
|
||||
const nextState = { ...state };
|
||||
|
||||
delete nextState[key];
|
||||
|
||||
return nextState;
|
||||
})
|
||||
.case(updateUrlParam, (state, { key, value }) => {
|
||||
// Only update the URL after the query param is registered and if the current value is different than the previous value
|
||||
if (state[key] === undefined || state[key] === value) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
[key]: value,
|
||||
};
|
||||
})
|
||||
.build();
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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 { GlobalUrlParam } from '.';
|
||||
import { State } from '../types';
|
||||
|
||||
export const selectGlobalUrlParam = (state: State): GlobalUrlParam => state.globalUrlParam;
|
|
@ -24,6 +24,7 @@ import { AppAction } from './actions';
|
|||
import { initDataView, SourcererModel, SourcererScopeName } from './sourcerer/model';
|
||||
import { ExperimentalFeatures } from '../../../common/experimental_features';
|
||||
import { getScopePatternListSelection } from './sourcerer/helpers';
|
||||
import { globalUrlParamReducer, initialGlobalUrlParam } from './global_url_param';
|
||||
|
||||
export type SubPluginsInitReducer = HostsPluginReducer &
|
||||
UsersPluginReducer &
|
||||
|
@ -36,7 +37,7 @@ export type SubPluginsInitReducer = HostsPluginReducer &
|
|||
export const createInitialState = (
|
||||
pluginsInitState: Omit<
|
||||
SecuritySubPlugins['store']['initialState'],
|
||||
'app' | 'dragAndDrop' | 'inputs' | 'sourcerer'
|
||||
'app' | 'dragAndDrop' | 'inputs' | 'sourcerer' | 'globalUrlParam'
|
||||
>,
|
||||
{
|
||||
defaultDataView,
|
||||
|
@ -100,6 +101,7 @@ export const createInitialState = (
|
|||
kibanaDataViews: kibanaDataViews.map((dataView) => ({ ...initDataView, ...dataView })),
|
||||
signalIndexName,
|
||||
},
|
||||
globalUrlParam: initialGlobalUrlParam,
|
||||
};
|
||||
|
||||
return preloadedState;
|
||||
|
@ -116,5 +118,6 @@ export const createReducer: (
|
|||
dragAndDrop: dragAndDropReducer,
|
||||
inputs: inputsReducer,
|
||||
sourcerer: sourcererReducer,
|
||||
globalUrlParam: globalUrlParamReducer,
|
||||
...pluginsReducer,
|
||||
});
|
||||
|
|
|
@ -20,6 +20,7 @@ import { TimelinePluginState } from '../../timelines/store/timeline';
|
|||
import { NetworkPluginState } from '../../network/store';
|
||||
import { ManagementPluginState } from '../../management';
|
||||
import { UsersPluginState } from '../../users/store';
|
||||
import { GlobalUrlParam } from './global_url_param';
|
||||
|
||||
export type StoreState = HostsPluginState &
|
||||
UsersPluginState &
|
||||
|
@ -31,6 +32,7 @@ export type StoreState = HostsPluginState &
|
|||
dragAndDrop: DragAndDropState;
|
||||
inputs: InputsState;
|
||||
sourcerer: SourcererState;
|
||||
globalUrlParam: GlobalUrlParam;
|
||||
};
|
||||
/**
|
||||
* The redux `State` type for the Security App.
|
||||
|
|
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import {
|
||||
useInitializeUrlParam,
|
||||
useGlobalQueryString,
|
||||
useSyncGlobalQueryString,
|
||||
useUpdateUrlParam,
|
||||
} from '.';
|
||||
import { GlobalUrlParam, globalUrlParamActions } from '../../store/global_url_param';
|
||||
import { mockHistory } from '../route/mocks';
|
||||
import {
|
||||
createSecuritySolutionStorageMock,
|
||||
kibanaObservable,
|
||||
mockGlobalState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
TestProviders,
|
||||
} from '../../mock';
|
||||
import { createStore } from '../../store';
|
||||
import { LinkInfo } from '../../links';
|
||||
import { SecurityPageName } from '../../../app/types';
|
||||
|
||||
const mockDispatch = jest.fn();
|
||||
|
||||
jest.mock('react-redux', () => {
|
||||
const original = jest.requireActual('react-redux');
|
||||
return {
|
||||
...original,
|
||||
useDispatch: () => mockDispatch,
|
||||
};
|
||||
});
|
||||
|
||||
const mockLocation = jest.fn();
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useHistory: () => mockHistory,
|
||||
useLocation: () => mockLocation(),
|
||||
}));
|
||||
|
||||
const defaultLinkInfo: LinkInfo = {
|
||||
id: SecurityPageName.alerts,
|
||||
path: '/test',
|
||||
title: 'test title',
|
||||
skipUrlState: false,
|
||||
};
|
||||
|
||||
const mockLinkInfo = jest.fn().mockResolvedValue(defaultLinkInfo);
|
||||
|
||||
jest.mock('../../links', () => ({
|
||||
...jest.requireActual('../../links'),
|
||||
getLinkInfo: () => mockLinkInfo(),
|
||||
}));
|
||||
|
||||
describe('global query string', () => {
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
|
||||
const makeStore = (globalUrlParam: GlobalUrlParam) =>
|
||||
createStore(
|
||||
{
|
||||
...mockGlobalState,
|
||||
globalUrlParam,
|
||||
},
|
||||
SUB_PLUGINS_REDUCER,
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
const makeWrapper = (globalUrlParam?: GlobalUrlParam) => {
|
||||
const wrapper = ({ children }: { children: React.ReactElement }) => (
|
||||
<TestProviders store={makeStore(globalUrlParam ?? {})}>{children}</TestProviders>
|
||||
);
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
// allow window.location.search to be redefined
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: {
|
||||
search: '?',
|
||||
},
|
||||
});
|
||||
});
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
window.location.search = '?';
|
||||
});
|
||||
describe('useInitializeUrlParam', () => {
|
||||
it('calls onInitialize with decoded URL param value', () => {
|
||||
const urlParamKey = 'testKey';
|
||||
mockLocation.mockReturnValue({ search: '?testKey=(test:(value:123))' });
|
||||
|
||||
const onInitialize = jest.fn();
|
||||
|
||||
renderHook(() => useInitializeUrlParam(urlParamKey, onInitialize), {
|
||||
wrapper: makeWrapper(),
|
||||
});
|
||||
|
||||
expect(onInitialize).toHaveBeenCalledWith({ test: { value: 123 } });
|
||||
});
|
||||
|
||||
it('deregister during unmount', () => {
|
||||
const urlParamKey = 'testKey';
|
||||
mockLocation.mockReturnValue({ search: "?testKey='123'" });
|
||||
|
||||
const { unmount } = renderHook(() => useInitializeUrlParam(urlParamKey, () => {}), {
|
||||
wrapper: makeWrapper(),
|
||||
});
|
||||
unmount();
|
||||
|
||||
expect(mockDispatch).toBeCalledWith(
|
||||
globalUrlParamActions.deregisterUrlParam({
|
||||
key: urlParamKey,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('calls registerUrlParam global URL param action', () => {
|
||||
const urlParamKey = 'testKey';
|
||||
const initialValue = 123;
|
||||
mockLocation.mockReturnValue({ search: `?testKey=${initialValue}` });
|
||||
|
||||
renderHook(() => useInitializeUrlParam(urlParamKey, () => {}), {
|
||||
wrapper: makeWrapper(),
|
||||
});
|
||||
|
||||
expect(mockDispatch).toBeCalledWith(
|
||||
globalUrlParamActions.registerUrlParam({
|
||||
key: urlParamKey,
|
||||
initialValue: initialValue.toString(),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateUrlParam', () => {
|
||||
it('dispatch updateUrlParam action', () => {
|
||||
const urlParamKey = 'testKey';
|
||||
const value = { test: 123 };
|
||||
const encodedVaue = '(test:123)';
|
||||
|
||||
const globalUrlParam = {
|
||||
[urlParamKey]: 'oldValue',
|
||||
};
|
||||
|
||||
const {
|
||||
result: { current: updateUrlParam },
|
||||
} = renderHook(() => useUpdateUrlParam(urlParamKey), {
|
||||
wrapper: makeWrapper(globalUrlParam),
|
||||
});
|
||||
updateUrlParam(value);
|
||||
|
||||
expect(mockDispatch).toBeCalledWith(
|
||||
globalUrlParamActions.updateUrlParam({
|
||||
key: urlParamKey,
|
||||
value: encodedVaue,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('dispatch updateUrlParam action with null value', () => {
|
||||
const urlParamKey = 'testKey';
|
||||
|
||||
const {
|
||||
result: { current: updateUrlParam },
|
||||
} = renderHook(() => useUpdateUrlParam(urlParamKey), {
|
||||
wrapper: makeWrapper(),
|
||||
});
|
||||
updateUrlParam(null);
|
||||
|
||||
expect(mockDispatch).toBeCalledWith(
|
||||
globalUrlParamActions.updateUrlParam({
|
||||
key: urlParamKey,
|
||||
value: null,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useGlobalQueryString', () => {
|
||||
it('returns global query string', () => {
|
||||
const store = createStore(
|
||||
{
|
||||
...mockGlobalState,
|
||||
globalUrlParam: {
|
||||
testNumber: '123',
|
||||
testObject: '(test:321)',
|
||||
testNull: null,
|
||||
testEmpty: '',
|
||||
},
|
||||
},
|
||||
SUB_PLUGINS_REDUCER,
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const wrapper = ({ children }: { children: React.ReactElement }) => (
|
||||
<TestProviders store={store}>{children}</TestProviders>
|
||||
);
|
||||
|
||||
const { result } = renderHook(() => useGlobalQueryString(), { wrapper });
|
||||
|
||||
expect(result.current).toEqual('testNumber=123&testObject=(test:321)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('useSyncGlobalQueryString', () => {
|
||||
it("doesn't delete other URL params when updating one", async () => {
|
||||
const urlParamKey = 'testKey';
|
||||
const value = '123';
|
||||
const globalUrlParam = {
|
||||
[urlParamKey]: value,
|
||||
};
|
||||
window.location.search = `?firstKey=111&${urlParamKey}=oldValue&lastKey=999`;
|
||||
|
||||
renderHook(() => useSyncGlobalQueryString(), { wrapper: makeWrapper(globalUrlParam) });
|
||||
|
||||
expect(mockHistory.replace).toHaveBeenCalledWith({
|
||||
search: `firstKey=111&${urlParamKey}=${value}&lastKey=999`,
|
||||
});
|
||||
});
|
||||
|
||||
it('updates URL params', async () => {
|
||||
const urlParamKey1 = 'testKey1';
|
||||
const value1 = '1111';
|
||||
const urlParamKey2 = 'testKey2';
|
||||
const value2 = '2222';
|
||||
const globalUrlParam = {
|
||||
[urlParamKey1]: value1,
|
||||
[urlParamKey2]: value2,
|
||||
};
|
||||
window.location.search = `?`;
|
||||
|
||||
renderHook(() => useSyncGlobalQueryString(), { wrapper: makeWrapper(globalUrlParam) });
|
||||
|
||||
expect(mockHistory.replace).toHaveBeenCalledWith({
|
||||
search: `${urlParamKey1}=${value1}&${urlParamKey2}=${value2}`,
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes URL param when value is null', async () => {
|
||||
const urlParamKey = 'testKey';
|
||||
const globalUrlParam = {
|
||||
[urlParamKey]: null,
|
||||
};
|
||||
window.location.search = `?${urlParamKey}=oldValue`;
|
||||
|
||||
renderHook(() => useSyncGlobalQueryString(), { wrapper: makeWrapper(globalUrlParam) });
|
||||
|
||||
expect(mockHistory.replace).toHaveBeenCalledWith({
|
||||
search: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes URL param when page has skipUrlState=true', async () => {
|
||||
const urlParamKey = 'testKey';
|
||||
const value = 'testValue';
|
||||
const globalUrlParam = {
|
||||
[urlParamKey]: value,
|
||||
};
|
||||
window.location.search = `?${urlParamKey}=${value}`;
|
||||
mockLinkInfo.mockReturnValue({ ...defaultLinkInfo, skipUrlState: true });
|
||||
|
||||
renderHook(() => useSyncGlobalQueryString(), { wrapper: makeWrapper(globalUrlParam) });
|
||||
|
||||
expect(mockHistory.replace).toHaveBeenCalledWith({
|
||||
search: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('does not replace URL param when the value does not change', async () => {
|
||||
const urlParamKey = 'testKey';
|
||||
const value = 'testValue';
|
||||
const globalUrlParam = {
|
||||
[urlParamKey]: value,
|
||||
};
|
||||
window.location.search = `?${urlParamKey}=${value}`;
|
||||
|
||||
renderHook(() => useSyncGlobalQueryString(), { wrapper: makeWrapper(globalUrlParam) });
|
||||
|
||||
expect(mockHistory.replace).not.toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it('does not replace URL param when the page doe not exist', async () => {
|
||||
const urlParamKey = 'testKey';
|
||||
const value = 'testValue';
|
||||
const globalUrlParam = {
|
||||
[urlParamKey]: value,
|
||||
};
|
||||
window.location.search = `?${urlParamKey}=oldValue`;
|
||||
mockLinkInfo.mockReturnValue(undefined);
|
||||
|
||||
renderHook(() => useSyncGlobalQueryString(), { wrapper: makeWrapper(globalUrlParam) });
|
||||
|
||||
expect(mockHistory.replace).not.toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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 * as H from 'history';
|
||||
import { parse, ParsedQuery, stringify } from 'query-string';
|
||||
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 { useDispatch } from 'react-redux';
|
||||
import {
|
||||
decodeRisonUrlState,
|
||||
encodeRisonUrlState,
|
||||
getParamFromQueryString,
|
||||
getQueryStringFromLocation,
|
||||
} from '../../components/url_state/helpers';
|
||||
import { useShallowEqualSelector } from '../../hooks/use_selector';
|
||||
import { globalUrlParamActions, globalUrlParamSelectors } from '../../store/global_url_param';
|
||||
import { useRouteSpy } from '../route/use_route_spy';
|
||||
import { getLinkInfo } from '../../links';
|
||||
|
||||
/**
|
||||
* Adds urlParamKey and the initial value to redux store.
|
||||
*
|
||||
* Please call this hook at the highest possible level of the rendering tree.
|
||||
* 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.
|
||||
*/
|
||||
export const useInitializeUrlParam = <State>(
|
||||
urlParamKey: string,
|
||||
/**
|
||||
* @param state Decoded URL param value.
|
||||
*/
|
||||
onInitialize: (state: State | null) => void
|
||||
) => {
|
||||
const dispatch = useDispatch();
|
||||
const { search } = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
const initialValue = getParamFromQueryString(getQueryStringFromLocation(search), urlParamKey);
|
||||
|
||||
dispatch(
|
||||
globalUrlParamActions.registerUrlParam({
|
||||
key: urlParamKey,
|
||||
initialValue: initialValue ?? null,
|
||||
})
|
||||
);
|
||||
|
||||
// execute consumer initialization
|
||||
onInitialize(decodeRisonUrlState<State>(initialValue ?? undefined));
|
||||
|
||||
return () => {
|
||||
dispatch(globalUrlParamActions.deregisterUrlParam({ key: urlParamKey }));
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps -- It must run only once when the application is initializing.
|
||||
}, []);
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates URL parameters in the url.
|
||||
*
|
||||
* Make sure to call `useInitializeUrlParam` before calling this function.
|
||||
*/
|
||||
export const useUpdateUrlParam = <State>(urlParamKey: string) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const updateUrlParam = useCallback(
|
||||
(value: State | null) => {
|
||||
const encodedValue = value !== null ? encodeRisonUrlState(value) : null;
|
||||
dispatch(globalUrlParamActions.updateUrlParam({ key: urlParamKey, value: encodedValue }));
|
||||
},
|
||||
[dispatch, urlParamKey]
|
||||
);
|
||||
|
||||
return updateUrlParam;
|
||||
};
|
||||
|
||||
export const useGlobalQueryString = (): string => {
|
||||
const globalUrlParam = useShallowEqualSelector(globalUrlParamSelectors.selectGlobalUrlParam);
|
||||
|
||||
const globalQueryString = useMemo(
|
||||
() => encodeQueryString(pickBy((value) => !isEmpty(value), globalUrlParam)),
|
||||
[globalUrlParam]
|
||||
);
|
||||
|
||||
return globalQueryString;
|
||||
};
|
||||
|
||||
/**
|
||||
* - It hides / shows the global query depending on the page.
|
||||
* - It updates the URL when globalUrlParam store updates.
|
||||
*/
|
||||
export const useSyncGlobalQueryString = () => {
|
||||
const history = useHistory();
|
||||
const [{ pageName }] = useRouteSpy();
|
||||
const globalUrlParam = useShallowEqualSelector(globalUrlParamSelectors.selectGlobalUrlParam);
|
||||
|
||||
useEffect(() => {
|
||||
const linkInfo = getLinkInfo(pageName) ?? { skipUrlState: true };
|
||||
const params = Object.entries(globalUrlParam).map(([key, value]) => ({
|
||||
key,
|
||||
value: linkInfo.skipUrlState ? null : value,
|
||||
}));
|
||||
|
||||
if (params.length > 0) {
|
||||
// window.location.search provides the most updated representation of the url search.
|
||||
// It prevents unnecessary re-renders which useLocation would create because 'replaceUrlParams' does update the location.
|
||||
// window.location.search also guarantees that we don't overwrite URL param managed outside react-router.
|
||||
replaceUrlParams(params, history, window.location.search);
|
||||
}
|
||||
}, [globalUrlParam, pageName, history]);
|
||||
};
|
||||
|
||||
const encodeQueryString = (urlParams: ParsedQuery<string>): string =>
|
||||
stringify(url.encodeQuery(urlParams), { sort: false, encode: false });
|
||||
|
||||
const replaceUrlParams = (
|
||||
params: Array<{ key: string; value: string | null }>,
|
||||
history: H.History,
|
||||
search: string
|
||||
) => {
|
||||
const urlParams = parse(search, { sort: false });
|
||||
|
||||
params.forEach(({ key, value }) => {
|
||||
if (value == null || value === '') {
|
||||
delete urlParams[key];
|
||||
} else {
|
||||
urlParams[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
const newSearch = encodeQueryString(urlParams);
|
||||
|
||||
if (getQueryStringFromLocation(search) !== newSearch) {
|
||||
history.replace({ search: newSearch });
|
||||
}
|
||||
};
|
|
@ -7,11 +7,12 @@
|
|||
|
||||
import { noop } from 'lodash/fp';
|
||||
import { createContext, Dispatch } from 'react';
|
||||
import { SecurityPageName } from '../../../app/types';
|
||||
|
||||
import { RouteSpyState, RouteSpyAction } from './types';
|
||||
|
||||
export const initRouteSpy: RouteSpyState = {
|
||||
pageName: '',
|
||||
pageName: SecurityPageName.noPage,
|
||||
detailName: undefined,
|
||||
tabName: undefined,
|
||||
search: '',
|
||||
|
|
|
@ -13,6 +13,7 @@ import { ManageRoutesSpy } from './manage_spy_routes';
|
|||
import { SpyRouteComponent } from './spy_routes';
|
||||
import { useRouteSpy } from './use_route_spy';
|
||||
import { generateHistoryMock, generateRoutesMock } from './mocks';
|
||||
import { SecurityPageName } from '../../../app/types';
|
||||
|
||||
const mockUseRouteSpy: jest.Mock = useRouteSpy as jest.Mock;
|
||||
jest.mock('./use_route_spy', () => ({
|
||||
|
@ -81,7 +82,7 @@ describe('Spy Routes', () => {
|
|||
flowTarget: undefined,
|
||||
},
|
||||
}}
|
||||
pageName="hosts"
|
||||
pageName={SecurityPageName.hosts}
|
||||
/>
|
||||
</ManageRoutesSpy>
|
||||
);
|
||||
|
@ -127,7 +128,7 @@ describe('Spy Routes', () => {
|
|||
flowTarget: undefined,
|
||||
},
|
||||
}}
|
||||
pageName="hosts"
|
||||
pageName={SecurityPageName.hosts}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -146,7 +147,7 @@ describe('Spy Routes', () => {
|
|||
path: newPathname,
|
||||
url: newPathname,
|
||||
params: {
|
||||
pageName: 'hosts',
|
||||
pageName: SecurityPageName.hosts,
|
||||
detailName: undefined,
|
||||
tabName: HostsTableType.authentications,
|
||||
search: '',
|
||||
|
@ -162,7 +163,7 @@ describe('Spy Routes', () => {
|
|||
route: {
|
||||
detailName: undefined,
|
||||
history: mockHistoryValue,
|
||||
pageName: 'hosts',
|
||||
pageName: SecurityPageName.hosts,
|
||||
pathName: newPathname,
|
||||
tabName: HostsTableType.authentications,
|
||||
search: '?updated="true"',
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { SecurityPageName } from '../../../app/types';
|
||||
import { RouteSpyState } from './types';
|
||||
|
||||
type Action = 'PUSH' | 'POP' | 'REPLACE';
|
||||
|
@ -35,7 +36,7 @@ export const generateHistoryMock = () => ({
|
|||
export const mockHistory = generateHistoryMock();
|
||||
|
||||
export const generateRoutesMock = (): RouteSpyState => ({
|
||||
pageName: '',
|
||||
pageName: SecurityPageName.noPage,
|
||||
detailName: undefined,
|
||||
tabName: undefined,
|
||||
search: '',
|
||||
|
|
|
@ -15,7 +15,7 @@ import { useRouteSpy } from './use_route_spy';
|
|||
import { SecurityPageName } from '../../../../common/constants';
|
||||
|
||||
export const SpyRouteComponent = memo<
|
||||
SpyRouteProps & { location: H.Location; pageName: string | undefined }
|
||||
SpyRouteProps & { location: H.Location; pageName: SecurityPageName | undefined }
|
||||
>(
|
||||
({
|
||||
location: { pathname, search },
|
||||
|
|
|
@ -16,6 +16,7 @@ import { NetworkRouteType } from '../../../network/pages/navigation/types';
|
|||
import { AdministrationSubTab as AdministrationType } from '../../../management/types';
|
||||
import { FlowTarget } from '../../../../common/search_strategy';
|
||||
import { UsersTableType } from '../../../users/store/model';
|
||||
import { SecurityPageName } from '../../../app/types';
|
||||
|
||||
export type SiemRouteType =
|
||||
| HostsTableType
|
||||
|
@ -24,7 +25,7 @@ export type SiemRouteType =
|
|||
| AdministrationType
|
||||
| UsersTableType;
|
||||
export interface RouteSpyState {
|
||||
pageName: string;
|
||||
pageName: SecurityPageName;
|
||||
detailName: string | undefined;
|
||||
tabName: SiemRouteType | undefined;
|
||||
search: string;
|
||||
|
|
|
@ -146,7 +146,7 @@ exports[`Field Renderers #hostIdRenderer it renders correctly against snapshot 1
|
|||
<a
|
||||
class="euiLink css-102pf9n-euiLink-primary"
|
||||
data-test-subj="host-details-button"
|
||||
href="securitySolutionUI/hosts/raspberrypi?sourcerer=(default:(id:security-solution,selectedPatterns:!('apm-*-transaction*','auditbeat-*','endgame-*','filebeat-*','logs-*','packetbeat-*','traces-apm*','winlogbeat-*','-*elastic-cloud-logs-*')))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now%2Fd,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now%2Fd)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now%2Fd,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now%2Fd)))"
|
||||
href="securitySolutionUI/hosts/raspberrypi?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now%2Fd,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now%2Fd)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now%2Fd,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now%2Fd)))"
|
||||
rel="noreferrer"
|
||||
>
|
||||
raspberrypi
|
||||
|
@ -201,7 +201,7 @@ exports[`Field Renderers #hostNameRenderer it renders correctly against snapshot
|
|||
<a
|
||||
class="euiLink css-102pf9n-euiLink-primary"
|
||||
data-test-subj="host-details-button"
|
||||
href="securitySolutionUI/hosts/raspberrypi?sourcerer=(default:(id:security-solution,selectedPatterns:!('apm-*-transaction*','auditbeat-*','endgame-*','filebeat-*','logs-*','packetbeat-*','traces-apm*','winlogbeat-*','-*elastic-cloud-logs-*')))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now%2Fd,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now%2Fd)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now%2Fd,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now%2Fd)))"
|
||||
href="securitySolutionUI/hosts/raspberrypi?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now%2Fd,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now%2Fd)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now%2Fd,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now%2Fd)))"
|
||||
rel="noreferrer"
|
||||
>
|
||||
raspberrypi
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue