mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Security solution] Grouping package - generic type argument (#152628)
This commit is contained in:
parent
1f6de13232
commit
dd75719f8c
9 changed files with 84 additions and 66 deletions
|
@ -18,7 +18,7 @@ import {
|
|||
} from './src';
|
||||
import type { NamedAggregation, GroupingFieldTotalAggregation, GroupingAggregation } from './src';
|
||||
|
||||
export const getGrouping = (props: GroupingProps): React.ReactElement<GroupingProps> => (
|
||||
export const getGrouping = <T,>(props: GroupingProps<T>): React.ReactElement<GroupingProps<T>> => (
|
||||
<Grouping {...props} />
|
||||
);
|
||||
|
||||
|
|
|
@ -21,21 +21,21 @@ import { statsContainerCss } from '../styles';
|
|||
import { TAKE_ACTION } from '../translations';
|
||||
import type { RawBucket } from '../types';
|
||||
|
||||
interface GroupStatsProps {
|
||||
interface GroupStatsProps<T> {
|
||||
badgeMetricStats?: BadgeMetric[];
|
||||
bucket: RawBucket;
|
||||
bucket: RawBucket<T>;
|
||||
customMetricStats?: CustomMetric[];
|
||||
onTakeActionsOpen?: () => void;
|
||||
takeActionItems: JSX.Element[];
|
||||
}
|
||||
|
||||
const GroupStatsComponent = ({
|
||||
const GroupStatsComponent = <T,>({
|
||||
badgeMetricStats,
|
||||
bucket,
|
||||
customMetricStats,
|
||||
onTakeActionsOpen,
|
||||
takeActionItems,
|
||||
}: GroupStatsProps) => {
|
||||
}: GroupStatsProps<T>) => {
|
||||
const [isPopoverOpen, setPopover] = useState(false);
|
||||
|
||||
const onButtonClick = useCallback(
|
||||
|
|
|
@ -25,16 +25,16 @@ export interface CustomMetric {
|
|||
customStatRenderer: JSX.Element;
|
||||
}
|
||||
|
||||
interface GroupPanelProps {
|
||||
interface GroupPanelProps<T> {
|
||||
customAccordionButtonClassName?: string;
|
||||
customAccordionClassName?: string;
|
||||
extraAction?: React.ReactNode;
|
||||
forceState?: 'open' | 'closed';
|
||||
groupBucket: RawBucket;
|
||||
groupBucket: RawBucket<T>;
|
||||
groupPanelRenderer?: JSX.Element;
|
||||
isLoading: boolean;
|
||||
level?: number;
|
||||
onToggleGroup?: (isOpen: boolean, groupBucket: RawBucket) => void;
|
||||
onToggleGroup?: (isOpen: boolean, groupBucket: RawBucket<T>) => void;
|
||||
renderChildComponent: (groupFilter: Filter[]) => React.ReactNode;
|
||||
selectedGroup: string;
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ const DefaultGroupPanelRenderer = ({ title }: { title: string }) => (
|
|||
</div>
|
||||
);
|
||||
|
||||
const GroupPanelComponent = ({
|
||||
const GroupPanelComponent = <T,>({
|
||||
customAccordionButtonClassName = 'groupingAccordionForm__button',
|
||||
customAccordionClassName = 'groupingAccordionForm',
|
||||
extraAction,
|
||||
|
@ -63,7 +63,7 @@ const GroupPanelComponent = ({
|
|||
onToggleGroup,
|
||||
renderChildComponent,
|
||||
selectedGroup,
|
||||
}: GroupPanelProps) => {
|
||||
}: GroupPanelProps<T>) => {
|
||||
const groupFieldValue = useMemo(() => firstNonNullValue(groupBucket.key), [groupBucket.key]);
|
||||
|
||||
const groupFilters = useMemo(
|
||||
|
|
|
@ -25,11 +25,11 @@ import { groupingContainerCss, groupsUnitCountCss } from './styles';
|
|||
import { GROUPS_UNIT } from './translations';
|
||||
import type { GroupingAggregation, GroupingFieldTotalAggregation, RawBucket } from './types';
|
||||
|
||||
export interface GroupingProps {
|
||||
badgeMetricStats?: (fieldBucket: RawBucket) => BadgeMetric[];
|
||||
customMetricStats?: (fieldBucket: RawBucket) => CustomMetric[];
|
||||
data?: GroupingAggregation & GroupingFieldTotalAggregation;
|
||||
groupPanelRenderer?: (fieldBucket: RawBucket) => JSX.Element | undefined;
|
||||
export interface GroupingProps<T> {
|
||||
badgeMetricStats?: (fieldBucket: RawBucket<T>) => BadgeMetric[];
|
||||
customMetricStats?: (fieldBucket: RawBucket<T>) => CustomMetric[];
|
||||
data?: GroupingAggregation<T> & GroupingFieldTotalAggregation;
|
||||
groupPanelRenderer?: (fieldBucket: RawBucket<T>) => JSX.Element | undefined;
|
||||
groupsSelector?: JSX.Element;
|
||||
inspectButton?: JSX.Element;
|
||||
isLoading: boolean;
|
||||
|
@ -46,7 +46,7 @@ export interface GroupingProps {
|
|||
unit?: (n: number) => string;
|
||||
}
|
||||
|
||||
const GroupingComponent = ({
|
||||
const GroupingComponent = <T,>({
|
||||
badgeMetricStats,
|
||||
customMetricStats,
|
||||
data,
|
||||
|
@ -59,9 +59,9 @@ const GroupingComponent = ({
|
|||
selectedGroup,
|
||||
takeActionItems,
|
||||
unit = defaultUnit,
|
||||
}: GroupingProps) => {
|
||||
}: GroupingProps<T>) => {
|
||||
const [trigger, setTrigger] = useState<
|
||||
Record<string, { state: 'open' | 'closed' | undefined; selectedBucket: RawBucket }>
|
||||
Record<string, { state: 'open' | 'closed' | undefined; selectedBucket: RawBucket<T> }>
|
||||
>({});
|
||||
|
||||
const groupsNumber = data?.groupsNumber?.value ?? 0;
|
||||
|
@ -196,4 +196,4 @@ const GroupingComponent = ({
|
|||
);
|
||||
};
|
||||
|
||||
export const Grouping = React.memo(GroupingComponent);
|
||||
export const Grouping = React.memo(GroupingComponent) as typeof GroupingComponent;
|
||||
|
|
|
@ -15,41 +15,12 @@ export interface GenericBuckets {
|
|||
|
||||
export const NONE_GROUP_KEY = 'none';
|
||||
|
||||
export type RawBucket = GenericBuckets & {
|
||||
alertsCount?: {
|
||||
value?: number | null; // Elasticsearch returns `null` when a sub-aggregation cannot be computed
|
||||
};
|
||||
severitiesSubAggregation?: {
|
||||
buckets?: GenericBuckets[];
|
||||
};
|
||||
countSeveritySubAggregation?: {
|
||||
value?: number | null; // Elasticsearch returns `null` when a sub-aggregation cannot be computed
|
||||
};
|
||||
usersCountAggregation?: {
|
||||
value?: number | null; // Elasticsearch returns `null` when a sub-aggregation cannot be computed
|
||||
};
|
||||
hostsCountAggregation?: {
|
||||
value?: number | null; // Elasticsearch returns `null` when a sub-aggregation cannot be computed
|
||||
};
|
||||
rulesCountAggregation?: {
|
||||
value?: number | null; // Elasticsearch returns `null` when a sub-aggregation cannot be computed
|
||||
};
|
||||
ruleTags?: {
|
||||
doc_count_error_upper_bound?: number;
|
||||
sum_other_doc_count?: number;
|
||||
buckets?: GenericBuckets[];
|
||||
};
|
||||
stackByMultipleFields1?: {
|
||||
buckets?: GenericBuckets[];
|
||||
doc_count_error_upper_bound?: number;
|
||||
sum_other_doc_count?: number;
|
||||
};
|
||||
};
|
||||
export type RawBucket<T> = GenericBuckets & T;
|
||||
|
||||
/** Defines the shape of the aggregation returned by Elasticsearch */
|
||||
export interface GroupingAggregation {
|
||||
export interface GroupingAggregation<T> {
|
||||
stackByMultipleFields0?: {
|
||||
buckets?: RawBucket[];
|
||||
buckets?: Array<RawBucket<T>>;
|
||||
};
|
||||
groupsCount0?: {
|
||||
value?: number | null;
|
||||
|
@ -60,11 +31,3 @@ export type GroupingFieldTotalAggregation = Record<
|
|||
string,
|
||||
{ value?: number | null; buckets?: Array<{ doc_count?: number | null }> }
|
||||
>;
|
||||
|
||||
export type FlattenedBucket = Pick<
|
||||
RawBucket,
|
||||
'doc_count' | 'key' | 'key_as_string' | 'alertsCount'
|
||||
> & {
|
||||
stackByMultipleFields1Key?: string;
|
||||
stackByMultipleFields1DocCount?: number;
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ import type {
|
|||
RawBucket,
|
||||
} from '@kbn/securitysolution-grouping';
|
||||
import { getGrouping, isNoneGroup } from '@kbn/securitysolution-grouping';
|
||||
import type { AlertsGroupingAggregation } from './grouping_settings/types';
|
||||
import { useGetGroupSelector } from '../../../common/containers/grouping/hooks/use_get_group_selector';
|
||||
import type { Status } from '../../../../common/detection_engine/schemas/common';
|
||||
import { defaultGroup } from '../../../common/store/grouping/defaults';
|
||||
|
@ -183,7 +184,10 @@ export const GroupedAlertsTableComponent: React.FC<AlertsTableComponentProps> =
|
|||
request,
|
||||
response,
|
||||
setQuery: setAlertsQuery,
|
||||
} = useQueryAlerts<{}, GroupingAggregation & GroupingFieldTotalAggregation>({
|
||||
} = useQueryAlerts<
|
||||
{},
|
||||
GroupingAggregation<AlertsGroupingAggregation> & GroupingFieldTotalAggregation
|
||||
>({
|
||||
query: queryGroups,
|
||||
indexName: signalIndexName,
|
||||
queryName: ALERTS_QUERY_NAMES.ALERTS_GROUPING,
|
||||
|
@ -236,12 +240,12 @@ export const GroupedAlertsTableComponent: React.FC<AlertsTableComponentProps> =
|
|||
isNoneGroup(selectedGroup)
|
||||
? renderChildComponent([])
|
||||
: getGrouping({
|
||||
badgeMetricStats: (fieldBucket: RawBucket) =>
|
||||
badgeMetricStats: (fieldBucket: RawBucket<AlertsGroupingAggregation>) =>
|
||||
getSelectedGroupBadgeMetrics(selectedGroup, fieldBucket),
|
||||
customMetricStats: (fieldBucket: RawBucket) =>
|
||||
customMetricStats: (fieldBucket: RawBucket<AlertsGroupingAggregation>) =>
|
||||
getSelectedGroupCustomMetrics(selectedGroup, fieldBucket),
|
||||
data: alertsGroupsData?.aggregations,
|
||||
groupPanelRenderer: (fieldBucket: RawBucket) =>
|
||||
groupPanelRenderer: (fieldBucket: RawBucket<AlertsGroupingAggregation>) =>
|
||||
getSelectedGroupButtonContent(selectedGroup, fieldBucket),
|
||||
groupsSelector,
|
||||
inspectButton: inspect,
|
||||
|
|
|
@ -19,12 +19,16 @@ import { euiThemeVars } from '@kbn/ui-theme';
|
|||
import { isArray } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
import type { RawBucket } from '@kbn/securitysolution-grouping';
|
||||
import type { AlertsGroupingAggregation } from './types';
|
||||
import { firstNonNullValue } from '../../../../../common/endpoint/models/ecs_safety_helpers';
|
||||
import type { GenericBuckets } from '../../../../../common/search_strategy';
|
||||
import { PopoverItems } from '../../../../common/components/popover_items';
|
||||
import { COLUMN_TAGS } from '../../../pages/detection_engine/rules/translations';
|
||||
|
||||
export const getSelectedGroupButtonContent = (selectedGroup: string, bucket: RawBucket) => {
|
||||
export const getSelectedGroupButtonContent = (
|
||||
selectedGroup: string,
|
||||
bucket: RawBucket<AlertsGroupingAggregation>
|
||||
) => {
|
||||
switch (selectedGroup) {
|
||||
case 'kibana.alert.rule.name':
|
||||
return isArray(bucket.key) ? (
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { EuiIcon } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import type { RawBucket } from '@kbn/securitysolution-grouping';
|
||||
import type { AlertsGroupingAggregation } from './types';
|
||||
import * as i18n from '../translations';
|
||||
|
||||
const getSingleGroupSeverity = (severity?: string) => {
|
||||
|
@ -63,7 +64,10 @@ const multiSeverity = (
|
|||
</>
|
||||
);
|
||||
|
||||
export const getSelectedGroupBadgeMetrics = (selectedGroup: string, bucket: RawBucket) => {
|
||||
export const getSelectedGroupBadgeMetrics = (
|
||||
selectedGroup: string,
|
||||
bucket: RawBucket<AlertsGroupingAggregation>
|
||||
) => {
|
||||
const defaultBadges = [
|
||||
{
|
||||
title: i18n.STATS_GROUP_ALERTS,
|
||||
|
@ -131,7 +135,10 @@ export const getSelectedGroupBadgeMetrics = (selectedGroup: string, bucket: RawB
|
|||
];
|
||||
};
|
||||
|
||||
export const getSelectedGroupCustomMetrics = (selectedGroup: string, bucket: RawBucket) => {
|
||||
export const getSelectedGroupCustomMetrics = (
|
||||
selectedGroup: string,
|
||||
bucket: RawBucket<AlertsGroupingAggregation>
|
||||
) => {
|
||||
const singleSeverityComponent =
|
||||
bucket.severitiesSubAggregation?.buckets && bucket.severitiesSubAggregation?.buckets?.length
|
||||
? getSingleGroupSeverity(bucket.severitiesSubAggregation?.buckets[0].key.toString())
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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 type { GenericBuckets } from '@kbn/securitysolution-grouping/src';
|
||||
// Elasticsearch returns `null` when a sub-aggregation cannot be computed
|
||||
type NumberOrNull = number | null;
|
||||
export interface AlertsGroupingAggregation {
|
||||
alertsCount?: {
|
||||
value?: NumberOrNull;
|
||||
};
|
||||
severitiesSubAggregation?: {
|
||||
buckets?: GenericBuckets[];
|
||||
};
|
||||
countSeveritySubAggregation?: {
|
||||
value?: NumberOrNull;
|
||||
};
|
||||
usersCountAggregation?: {
|
||||
value?: NumberOrNull;
|
||||
};
|
||||
hostsCountAggregation?: {
|
||||
value?: NumberOrNull;
|
||||
};
|
||||
rulesCountAggregation?: {
|
||||
value?: NumberOrNull;
|
||||
};
|
||||
ruleTags?: {
|
||||
doc_count_error_upper_bound?: number;
|
||||
sum_other_doc_count?: number;
|
||||
buckets?: GenericBuckets[];
|
||||
};
|
||||
stackByMultipleFields1?: {
|
||||
buckets?: GenericBuckets[];
|
||||
doc_count_error_upper_bound?: number;
|
||||
sum_other_doc_count?: number;
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue