Skip to content

Commit

Permalink
Merge branch 'main' into fix-flaky-metering-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
CohenIdo authored Oct 28, 2024
2 parents 461bd09 + 37a4691 commit 00c60a0
Show file tree
Hide file tree
Showing 11 changed files with 280 additions and 96 deletions.
2 changes: 1 addition & 1 deletion x-pack/packages/kbn-cloud-security-posture/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
export * from './src/types';
export * from './src/constants/component_constants';
export * from './src/constants/navigation';
export type { NavFilter } from './src/hooks/use_navigate_findings';
export type { NavFilter } from './src/utils/query_utils';
export { showErrorToast } from './src/utils/show_error_toast';
export { encodeQuery, decodeQuery } from './src/utils/query_utils';
export { CspEvaluationBadge } from './src/components/csp_evaluation_badge';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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 { CoreStart } from '@kbn/core-lifecycle-browser';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { useCallback } from 'react';
import { CspClientPluginStartDeps } from '../types';
import { NavFilter, encodeQueryUrl, composeQueryFilters } from '../utils/query_utils';

export const useGetNavigationUrlParams = () => {
const { services } = useKibana<CoreStart & CspClientPluginStartDeps>();

const getNavUrlParams = useCallback(
(
filterParams: NavFilter = {},
findingsType?: 'configurations' | 'vulnerabilities',
groupBy?: string[]
) => {
const filters = composeQueryFilters(filterParams);

const searchParams = new URLSearchParams(encodeQueryUrl(services.data, filters, groupBy));

return `${findingsType ? findingsType : ''}?${searchParams.toString()}`;
},
[services.data]
);

return getNavUrlParams;
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,74 +7,28 @@

import { useCallback } from 'react';
import { useHistory } from 'react-router-dom';
import { Filter } from '@kbn/es-query';
import {
SECURITY_DEFAULT_DATA_VIEW_ID,
CDR_MISCONFIGURATIONS_DATA_VIEW_ID_PREFIX,
} from '@kbn/cloud-security-posture-common';
import { CDR_MISCONFIGURATIONS_DATA_VIEW_ID_PREFIX } from '@kbn/cloud-security-posture-common';
import type { CoreStart } from '@kbn/core/public';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { findingsNavigation } from '../constants/navigation';
import { useDataView } from './use_data_view';
import { CspClientPluginStartDeps } from '../..';
import { encodeQuery } from '../utils/query_utils';
import { NavFilter, encodeQueryUrl, composeQueryFilters } from '../utils/query_utils';

interface NegatedValue {
value: string | number;
negate: boolean;
}

type FilterValue = string | number | NegatedValue;

export type NavFilter = Record<string, FilterValue>;

const createFilter = (key: string, filterValue: FilterValue, dataViewId: string): Filter => {
let negate = false;
let value = filterValue;
if (typeof filterValue === 'object') {
negate = filterValue.negate;
value = filterValue.value;
}
// If the value is '*', we want to create an exists filter
if (value === '*') {
return {
query: { exists: { field: key } },
meta: { type: 'exists', index: dataViewId },
};
}
return {
meta: {
alias: null,
negate,
disabled: false,
type: 'phrase',
key,
index: dataViewId,
},
query: { match_phrase: { [key]: value } },
};
};
const useNavigate = (pathname: string, dataViewId = SECURITY_DEFAULT_DATA_VIEW_ID) => {
const useNavigate = (pathname: string, dataViewId?: string) => {
const history = useHistory();
const { services } = useKibana<CoreStart & CspClientPluginStartDeps>();

const { services } = useKibana<CoreStart & CspClientPluginStartDeps>();
return useCallback(
(filterParams: NavFilter = {}, groupBy?: string[]) => {
const filters = Object.entries(filterParams).map(([key, filterValue]) =>
createFilter(key, filterValue, dataViewId)
);
const filters = composeQueryFilters(filterParams, dataViewId);

history.push({
pathname,
search: encodeQuery({
// Set query language from user's preference
query: services.data.query.queryString.getDefaultQuery(),
filters,
...(groupBy && { groupBy }),
}),
search: encodeQueryUrl(services.data, filters, groupBy),
});
},
[history, pathname, services.data.query.queryString, dataViewId]
[dataViewId, history, pathname, services.data]
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* 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 { encodeQueryUrl, composeQueryFilters } from './query_utils';
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';

const DEFAULT_DATA_VIEW_ID = 'security-solution-default';

describe('composeQueryFilters', () => {
it('Should return correct filters given some filterParams', () => {
const testFilterParams = {
test_field: 'test_value',
};
const testResult = [
{
meta: {
alias: null,
negate: false,
disabled: false,
type: 'phrase',
key: 'test_field',
index: DEFAULT_DATA_VIEW_ID,
},
query: { match_phrase: { test_field: 'test_value' } },
},
];
expect(composeQueryFilters(testFilterParams)).toEqual(testResult);
});

it('Should return empty filters given empty filterParams', () => {
expect(composeQueryFilters({})).toEqual([]);
});

it('Should return correct filters given some filterParams and dataviewId', () => {
const testFilterParams = {
test_field: 'test_value',
};
const testResult = [
{
meta: {
alias: null,
negate: false,
disabled: false,
type: 'phrase',
key: 'test_field',
index: 'test-data-view',
},
query: { match_phrase: { test_field: 'test_value' } },
},
];
expect(composeQueryFilters(testFilterParams, 'test-data-view')).toEqual(testResult);
});
});

describe('encodeQueryUrl', () => {
const getServicesMock = () => ({
data: dataPluginMock.createStartContract(),
});

it('Should return correct URL given empty filters', () => {
const result = 'cspq=(filters:!())';
expect(encodeQueryUrl(getServicesMock().data, [])).toEqual(result);
});

it('should return correct URL given filters', () => {
const filter = [
{
meta: {
alias: null,
negate: false,
disabled: false,
type: 'phrase',
key: 'test_field',
index: DEFAULT_DATA_VIEW_ID,
},
query: { match_phrase: { test_field: 'test_value' } },
},
];
const result =
'cspq=(filters:!((meta:(alias:!n,disabled:!f,index:security-solution-default,key:test_field,negate:!f,type:phrase),query:(match_phrase:(test_field:test_value)))))';
expect(encodeQueryUrl(getServicesMock().data, filter)).toEqual(result);
});

it('should return correct URL given filters and group by', () => {
const filter = [
{
meta: {
alias: null,
negate: false,
disabled: false,
type: 'phrase',
key: 'test_field',
index: DEFAULT_DATA_VIEW_ID,
},
query: { match_phrase: { test_field: 'test_value' } },
},
];
const groupByFilter = ['filterA'];
const result =
'cspq=(filters:!((meta:(alias:!n,disabled:!f,index:security-solution-default,key:test_field,negate:!f,type:phrase),query:(match_phrase:(test_field:test_value)))),groupBy:!(filterA))';
expect(encodeQueryUrl(getServicesMock().data, filter, groupByFilter)).toEqual(result);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,21 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { encode, decode } from '@kbn/rison';
import type { LocationDescriptorObject } from 'history';
import { Filter } from '@kbn/es-query';
import { SECURITY_DEFAULT_DATA_VIEW_ID } from '@kbn/cloud-security-posture-common';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';

interface NegatedValue {
value: string | number;
negate: boolean;
}

type FilterValue = string | number | NegatedValue;

export type NavFilter = Record<string, FilterValue>;

const encodeRison = (v: any): string | undefined => {
try {
Expand Down Expand Up @@ -38,3 +51,52 @@ export const decodeQuery = <T extends unknown>(search?: string): Partial<T> | un
if (!risonQuery) return;
return decodeRison<T>(risonQuery);
};

export const encodeQueryUrl = (
servicesStart: DataPublicPluginStart,
filters: Filter[],
groupBy?: string[]
): any => {
return encodeQuery({
query: servicesStart.query.queryString.getDefaultQuery(),
filters,
...(groupBy && { groupBy }),
});
};

// dataViewId is used to prevent FilterManager from falling back to the default in the sorcerer (logs-*)
export const composeQueryFilters = (
filterParams: NavFilter = {},
dataViewId = SECURITY_DEFAULT_DATA_VIEW_ID
): Filter[] => {
return Object.entries(filterParams).map(([key, filterValue]) =>
createFilter(key, filterValue, dataViewId)
);
};

export const createFilter = (key: string, filterValue: FilterValue, dataViewId: string): Filter => {
let negate = false;
let value = filterValue;
if (typeof filterValue === 'object') {
negate = filterValue.negate;
value = filterValue.value;
}
// If the value is '*', we want to create an exists filter
if (value === '*') {
return {
query: { exists: { field: key } },
meta: { type: 'exists', index: dataViewId },
};
}
return {
meta: {
alias: null,
negate,
disabled: false,
type: 'phrase',
key,
index: dataViewId,
},
query: { match_phrase: { [key]: value } },
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@
"@kbn/ui-theme",
"@kbn/i18n-react",
"@kbn/rison",
"@kbn/core-lifecycle-browser",
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
import { useNavigateFindings } from '@kbn/cloud-security-posture/src/hooks/use_navigate_findings';
import type { NavFilter } from '@kbn/cloud-security-posture/src/hooks/use_navigate_findings';
import type { NavFilter } from '@kbn/cloud-security-posture/src/utils/query_utils';
import type {
BenchmarkData,
ComplianceDashboardDataV2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import { i18n } from '@kbn/i18n';
import { css } from '@emotion/react';
import { CSPM_POLICY_TEMPLATE, KSPM_POLICY_TEMPLATE } from '@kbn/cloud-security-posture-common';
import type { NavFilter } from '@kbn/cloud-security-posture/src/hooks/use_navigate_findings';
import type { NavFilter } from '@kbn/cloud-security-posture/src/utils/query_utils';
import { useNavigateFindings } from '@kbn/cloud-security-posture/src/hooks/use_navigate_findings';
import { useCspIntegrationLink } from '../../../common/navigation/use_csp_integration_link';
import { DASHBOARD_COUNTER_CARDS, DASHBOARD_SUMMARY_CONTAINER } from '../test_subjects';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
useEuiTheme,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import type { NavFilter } from '@kbn/cloud-security-posture/src/hooks/use_navigate_findings';
import type { NavFilter } from '@kbn/cloud-security-posture/src/utils/query_utils';
import { useNavigateVulnerabilities } from '@kbn/cloud-security-posture/src/hooks/use_navigate_findings';
import type { VulnSeverity } from '@kbn/cloud-security-posture-common';
import { CVSScoreBadge, SeverityStatusBadge } from '@kbn/cloud-security-posture';
Expand Down
Loading

0 comments on commit 00c60a0

Please sign in to comment.