Skip to content

Commit

Permalink
Merge branch 'main' into data-quality-base-path-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine committed May 4, 2023
2 parents 8919574 + 0e9d84d commit 0fcffc7
Show file tree
Hide file tree
Showing 116 changed files with 498 additions and 137 deletions.
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

export const createGroupFilter = (selectedGroup: string, query?: string) =>
export const createGroupFilter = (selectedGroup: string, query?: string | null) =>
query && selectedGroup
? [
{
Expand Down
Expand Up @@ -8,19 +8,21 @@

import { fireEvent, render } from '@testing-library/react';
import { GroupPanel } from '.';
import { createGroupFilter } from './helpers';
import { createGroupFilter, getNullGroupFilter } from './helpers';
import React from 'react';

const onToggleGroup = jest.fn();
const renderChildComponent = jest.fn();
const ruleName = 'Rule name';
const ruleDesc = 'Rule description';
const selectedGroup = 'kibana.alert.rule.name';

const testProps = {
isLoading: false,
isNullGroup: false,
groupBucket: {
key: [ruleName, ruleDesc],
key_as_string: `${ruleName}|${ruleDesc}`,
selectedGroup,
key: [ruleName, ruleName],
key_as_string: `${ruleName}|${ruleName}`,
doc_count: 98,
hostsCountAggregation: {
value: 5,
Expand Down Expand Up @@ -54,7 +56,7 @@ const testProps = {
},
},
renderChildComponent,
selectedGroup: 'kibana.alert.rule.name',
selectedGroup,
onGroupClose: () => {},
};

Expand All @@ -69,7 +71,12 @@ describe('grouping accordion panel', () => {
createGroupFilter(testProps.selectedGroup, ruleName)
);
});
it('does not create query without a valid groupFieldValue', () => {
it('creates the query for the selectedGroup attribute when the group is null', () => {
const { getByTestId } = render(<GroupPanel {...testProps} isNullGroup />);
expect(getByTestId('grouping-accordion')).toBeInTheDocument();
expect(renderChildComponent).toHaveBeenCalledWith(getNullGroupFilter(testProps.selectedGroup));
});
it('does not render accordion or create query without a valid groupFieldValue', () => {
const { queryByTestId } = render(
<GroupPanel
{...testProps}
Expand All @@ -83,6 +90,11 @@ describe('grouping accordion panel', () => {
expect(queryByTestId('grouping-accordion')).not.toBeInTheDocument();
expect(renderChildComponent).not.toHaveBeenCalled();
});
it('Does not render accordion or create query when groupBucket.selectedGroup !== selectedGroup', () => {
const { queryByTestId } = render(<GroupPanel {...testProps} selectedGroup="source.ip" />);
expect(queryByTestId('grouping-accordion')).not.toBeInTheDocument();
expect(testProps.renderChildComponent).not.toHaveBeenCalled();
});
it('When onToggleGroup not defined, does nothing on toggle', () => {
const { container } = render(<GroupPanel {...testProps} />);
fireEvent.click(container.querySelector('[data-test-subj="grouping-accordion"] button')!);
Expand Down
Expand Up @@ -10,22 +10,22 @@ import { EuiAccordion, EuiFlexGroup, EuiFlexItem, EuiTitle, EuiIconTip } from '@
import type { Filter } from '@kbn/es-query';
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import { firstNonNullValue } from '../../helpers';
import type { RawBucket } from '../types';
import type { GroupingBucket } from '../types';
import { createGroupFilter, getNullGroupFilter } from './helpers';

interface GroupPanelProps<T> {
customAccordionButtonClassName?: string;
customAccordionClassName?: string;
extraAction?: React.ReactNode;
forceState?: 'open' | 'closed';
groupBucket: RawBucket<T>;
groupBucket: GroupingBucket<T>;
groupPanelRenderer?: JSX.Element;
groupingLevel?: number;
isLoading: boolean;
isNullGroup?: boolean;
nullGroupMessage?: string;
onGroupClose: () => void;
onToggleGroup?: (isOpen: boolean, groupBucket: RawBucket<T>) => void;
onToggleGroup?: (isOpen: boolean, groupBucket: GroupingBucket<T>) => void;
renderChildComponent: (groupFilter: Filter[]) => React.ReactElement;
selectedGroup: string;
}
Expand Down Expand Up @@ -81,8 +81,10 @@ const GroupPanelComponent = <T,>({
lastForceState.current = 'open';
}
}, [onGroupClose, forceState, selectedGroup]);

const groupFieldValue = useMemo(() => firstNonNullValue(groupBucket.key), [groupBucket.key]);
const groupFieldValue = useMemo(
() => (groupBucket.selectedGroup === selectedGroup ? firstNonNullValue(groupBucket.key) : null),
[groupBucket.key, groupBucket.selectedGroup, selectedGroup]
);

const groupFilters = useMemo(
() =>
Expand Down
Expand Up @@ -24,6 +24,7 @@ export const mockGroupingProps = {
{
key: [host1Name],
key_as_string: `${host1Name}`,
selectedGroup: 'host.name',
doc_count: 1,
hostsCountAggregation: {
value: 1,
Expand Down Expand Up @@ -56,6 +57,7 @@ export const mockGroupingProps = {
{
key: [host2Name],
key_as_string: `${host2Name}`,
selectedGroup: 'host.name',
doc_count: 1,
hostsCountAggregation: {
value: 1,
Expand Down Expand Up @@ -88,6 +90,7 @@ export const mockGroupingProps = {
{
key: ['-'],
key_as_string: `-`,
selectedGroup: 'host.name',
isNullGroup: true,
doc_count: 11,
hostsCountAggregation: {
Expand Down
Expand Up @@ -16,15 +16,16 @@ export const NONE_GROUP_KEY = 'none';

export type RawBucket<T> = GenericBuckets & T;

export interface GroupingBucket {
export type GroupingBucket<T> = RawBucket<T> & {
selectedGroup: string;
isNullGroup?: boolean;
}
};

/** Defines the shape of the aggregation returned by Elasticsearch */
// TODO: write developer docs for these fields
export interface RootAggregation<T> {
groupByFields?: {
buckets?: Array<RawBucket<T> & GroupingBucket>;
buckets?: Array<GroupingBucket<T>>;
};
groupsCount?: {
value?: number | null;
Expand Down
Expand Up @@ -161,16 +161,19 @@ describe('group selector', () => {
{
key: ['20.80.64.28', '20.80.64.28'],
key_as_string: '20.80.64.28|20.80.64.28',
selectedGroup: 'source.ip',
doc_count: 75,
},
{
key: ['0.0.0.0', '0.0.0.0'],
key_as_string: '0.0.0.0|0.0.0.0',
selectedGroup: 'source.ip',
doc_count: 75,
},
{
key: ['0.0.0.0', '::'],
key_as_string: '0.0.0.0|::',
selectedGroup: 'source.ip',
doc_count: 75,
},
],
Expand All @@ -186,23 +189,26 @@ describe('group selector', () => {
},
};
it('parseGroupingQuery finds and flags the null group', () => {
const result = parseGroupingQuery(groupingAggs);
const result = parseGroupingQuery('source.ip', groupingAggs);
expect(result).toEqual({
groupByFields: {
buckets: [
{
key: ['20.80.64.28'],
key_as_string: '20.80.64.28',
selectedGroup: 'source.ip',
doc_count: 75,
},
{
key: ['0.0.0.0'],
key_as_string: '0.0.0.0',
selectedGroup: 'source.ip',
doc_count: 75,
},
{
key: [getEmptyValue()],
key_as_string: getEmptyValue(),
selectedGroup: 'source.ip',
isNullGroup: true,
doc_count: 75,
},
Expand All @@ -220,7 +226,7 @@ describe('group selector', () => {
});
});
it('parseGroupingQuery adjust group count when null field group is present', () => {
const result: GroupingAggregation<{}> = parseGroupingQuery({
const result: GroupingAggregation<{}> = parseGroupingQuery('source.ip', {
...groupingAggs,
unitsCountWithoutNull: { value: 99 },
});
Expand Down
Expand Up @@ -119,9 +119,11 @@ export const getGroupingQuery = ({
/**
* Parses the grouping query response to add the isNullGroup
* flag to the buckets and to format the bucket keys
* @param buckets buckets returned from the grouping query
* @param selectedGroup from the grouping query
* @param aggs aggs returned from the grouping query
*/
export const parseGroupingQuery = <T>(
selectedGroup: string,
aggs?: GroupingAggregation<T>
): GroupingAggregation<T> | {} => {
if (!aggs) {
Expand All @@ -138,11 +140,13 @@ export const parseGroupingQuery = <T>(
? {
...group,
key: [group.key[0]],
selectedGroup,
key_as_string: group.key[0],
}
: {
...group,
key: [emptyValue],
selectedGroup,
key_as_string: emptyValue,
isNullGroup: true,
};
Expand Down
Expand Up @@ -136,6 +136,7 @@ export const applicationUsageSchema = {
enterpriseSearch: commonSchema,
enterpriseSearchContent: commonSchema,
enterpriseSearchAnalytics: commonSchema,
enterpriseSearchApplications: commonSchema,
elasticsearch: commonSchema,
appSearch: commonSchema,
workplaceSearch: commonSchema,
Expand Down
131 changes: 131 additions & 0 deletions src/plugins/telemetry/schema/oss_plugins.json
Expand Up @@ -2229,6 +2229,137 @@
}
}
},
"enterpriseSearchApplications": {
"properties": {
"appId": {
"type": "keyword",
"_meta": {
"description": "The application being tracked"
}
},
"viewId": {
"type": "keyword",
"_meta": {
"description": "Always `main`"
}
},
"clicks_total": {
"type": "long",
"_meta": {
"description": "General number of clicks in the application since we started counting them"
}
},
"clicks_7_days": {
"type": "long",
"_meta": {
"description": "General number of clicks in the application over the last 7 days"
}
},
"clicks_30_days": {
"type": "long",
"_meta": {
"description": "General number of clicks in the application over the last 30 days"
}
},
"clicks_90_days": {
"type": "long",
"_meta": {
"description": "General number of clicks in the application over the last 90 days"
}
},
"minutes_on_screen_total": {
"type": "float",
"_meta": {
"description": "Minutes the application is active and on-screen since we started counting them."
}
},
"minutes_on_screen_7_days": {
"type": "float",
"_meta": {
"description": "Minutes the application is active and on-screen over the last 7 days"
}
},
"minutes_on_screen_30_days": {
"type": "float",
"_meta": {
"description": "Minutes the application is active and on-screen over the last 30 days"
}
},
"minutes_on_screen_90_days": {
"type": "float",
"_meta": {
"description": "Minutes the application is active and on-screen over the last 90 days"
}
},
"views": {
"type": "array",
"items": {
"properties": {
"appId": {
"type": "keyword",
"_meta": {
"description": "The application being tracked"
}
},
"viewId": {
"type": "keyword",
"_meta": {
"description": "The application view being tracked"
}
},
"clicks_total": {
"type": "long",
"_meta": {
"description": "General number of clicks in the application sub view since we started counting them"
}
},
"clicks_7_days": {
"type": "long",
"_meta": {
"description": "General number of clicks in the active application sub view over the last 7 days"
}
},
"clicks_30_days": {
"type": "long",
"_meta": {
"description": "General number of clicks in the active application sub view over the last 30 days"
}
},
"clicks_90_days": {
"type": "long",
"_meta": {
"description": "General number of clicks in the active application sub view over the last 90 days"
}
},
"minutes_on_screen_total": {
"type": "float",
"_meta": {
"description": "Minutes the application sub view is active and on-screen since we started counting them."
}
},
"minutes_on_screen_7_days": {
"type": "float",
"_meta": {
"description": "Minutes the application is active and on-screen active application sub view over the last 7 days"
}
},
"minutes_on_screen_30_days": {
"type": "float",
"_meta": {
"description": "Minutes the application is active and on-screen active application sub view over the last 30 days"
}
},
"minutes_on_screen_90_days": {
"type": "float",
"_meta": {
"description": "Minutes the application is active and on-screen active application sub view over the last 90 days"
}
}
}
}
}
}
},
"elasticsearch": {
"properties": {
"appId": {
Expand Down
Expand Up @@ -53,6 +53,8 @@ const LensMarkDownRendererComponent: React.FC<LensMarkDownRendererProps> = ({
executionContext={{
type: 'cases',
}}
syncTooltips={false}
syncCursor={false}
/>
<LensChartTooltipFix />
</Container>
Expand Down

0 comments on commit 0fcffc7

Please sign in to comment.