Skip to content

Commit b459328

Browse files
committed
[Query Rules] Query rules telemetry (#225146)
## Summary Adds telemetry to query rules UIs ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md) - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations. - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> (cherry picked from commit cae3861) # Conflicts: # x-pack/solutions/search/plugins/search_query_rules/public/components/query_ruleset_detail/query_rule_flyout/use_query_rule_flyout_state.ts
1 parent 10aa64e commit b459328

14 files changed

Lines changed: 177 additions & 24 deletions

File tree

x-pack/solutions/search/plugins/search_query_rules/kibana.jsonc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"console",
2020
"searchNavigation",
2121
"share",
22+
"usageCollection",
2223
],
2324
"requiredBundles": [
2425
"kibanaReact",
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
export enum AnalyticsEvents {
9+
// Empty promp actions
10+
gettingStartedButtonClicked = 'getting_started_button_clicked',
11+
createInConsoleClicked = 'create_in_console_clicked',
12+
emptyPromptLoaded = 'empty_prompt_loaded',
13+
14+
// Error prompt loaded
15+
genericErrorPromptLoaded = 'generic_error_prompt_loaded',
16+
notFoundErrorPromptLoaded = 'not_found_error_prompt_loaded',
17+
missingPermissionsErrorPromptLoaded = 'missing_permissions_error_prompt_loaded',
18+
19+
rulesetCreateClicked = 'ruleset_create_clicked',
20+
rulesetUpdateClicked = 'ruleset_update_clicked',
21+
22+
// ruleset detail page actions
23+
rulesetDetailPageLoaded = 'ruleset_details_page_loaded',
24+
addRuleClicked = 'add_rule_clicked',
25+
editRuleClicked = 'edit_rule_clicked',
26+
deleteRuleClicked = 'delete_rule_clicked',
27+
testInConsoleClicked = 'test_in_console_clicked',
28+
deleteRulesetFromHeaderClicked = 'delete_ruleset_from_header_clicked',
29+
backToRulesetListClicked = 'back_to_ruleset_list_clicked',
30+
31+
rulesReordered = 'rules_reordered',
32+
ruleFlyoutDocumentsReordered = 'rule_flyout_documents_reordered',
33+
34+
// ruleset list page actions
35+
editRulesetInlineNameClicked = 'edit_ruleset_inline_name_clicked',
36+
editRulesetInlineDropdownClicked = 'edit_ruleset_inline_dropdown_clicked',
37+
rulesetListPageLoaded = 'ruleset_list_page_loaded',
38+
deleteRulesetInlineDropdownClicked = 'delete_ruleset_inline_dropdown_clicked',
39+
testRulesetInlineDropdownClicked = 'test_ruleset_inline_dropdown_clicked',
40+
rulesetSearched = 'ruleset_searched',
41+
addRulesetClicked = 'add_ruleset_clicked',
42+
}

x-pack/solutions/search/plugins/search_query_rules/public/components/empty_prompt/empty_prompt.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* 2.0.
66
*/
77

8-
import React from 'react';
8+
import React, { useEffect } from 'react';
99

1010
import {
1111
EuiButton,
@@ -35,14 +35,22 @@ import { useKibana } from '../../hooks/use_kibana';
3535
import queryRulesImg from '../../assets/query-rules-context-alt.svg';
3636
import queryRulesDarkImg from '../../assets/query-rules-context-alt-dark.svg';
3737
import backgroundPanelImg from '../../assets/query-rule-panel-background.svg';
38+
import { AnalyticsEvents } from '../../analytics/constants';
39+
import { useUsageTracker } from '../../hooks/use_usage_tracker';
3840
import backgroundPaneDarklImg from '../../assets/query-rule-panel-background-dark.svg';
3941

4042
interface EmptyPromptProps {
4143
getStartedAction: () => void;
4244
}
4345
export const EmptyPrompt: React.FC<EmptyPromptProps> = ({ getStartedAction }) => {
46+
const usageTracker = useUsageTracker();
4447
const { application, share, console: consolePlugin } = useKibana().services;
4548
const { euiTheme, colorMode } = useEuiTheme();
49+
50+
useEffect(() => {
51+
usageTracker?.load(AnalyticsEvents.emptyPromptLoaded);
52+
}, [usageTracker]);
53+
4654
const gradientOverlay = css({
4755
background: `linear-gradient(180deg, ${transparentize(
4856
euiTheme.colors.backgroundBasePlain,
@@ -101,7 +109,10 @@ export const EmptyPrompt: React.FC<EmptyPromptProps> = ({ getStartedAction }) =>
101109
data-test-subj="searchQueryRulesEmptyPromptGetStartedButton"
102110
color="primary"
103111
fill
104-
onClick={getStartedAction}
112+
onClick={() => {
113+
usageTracker?.click(AnalyticsEvents.gettingStartedButtonClicked);
114+
getStartedAction();
115+
}}
105116
>
106117
<FormattedMessage
107118
id="xpack.queryRules.emptyPrompt.getStartedButton"
@@ -240,6 +251,7 @@ export const EmptyPrompt: React.FC<EmptyPromptProps> = ({ getStartedAction }) =>
240251
defaultMessage: 'Create in Console',
241252
})}
242253
showIcon
254+
data-test-subj={AnalyticsEvents.createInConsoleClicked}
243255
/>
244256
</EuiFlexItem>
245257
</EuiHideFor>

x-pack/solutions/search/plugins/search_query_rules/public/components/error_prompt/error_prompt.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
* 2.0.
66
*/
77

8-
import React from 'react';
8+
import React, { useEffect } from 'react';
99

1010
import { EuiEmptyPrompt } from '@elastic/eui';
1111
import { FormattedMessage } from '@kbn/i18n-react';
12+
import { useUsageTracker } from '../../hooks/use_usage_tracker';
13+
import { AnalyticsEvents } from '../../analytics/constants';
1214

1315
const ERROR_MESSAGES = {
1416
notFound: {
@@ -19,6 +21,7 @@ const ERROR_MESSAGES = {
1921
defaultMessage="Requested resource was not found. Check if the URL is correct."
2022
/>
2123
),
24+
analyticsEvent: AnalyticsEvents.notFoundErrorPromptLoaded,
2225
},
2326
generic: {
2427
title: <FormattedMessage id="xpack.queryRules.errorTitle" defaultMessage="An error occurred" />,
@@ -28,6 +31,7 @@ const ERROR_MESSAGES = {
2831
defaultMessage="An error occurred while fetching query rules. Check Kibana logs for more information."
2932
/>
3033
),
34+
analyticsEvent: AnalyticsEvents.genericErrorPromptLoaded,
3135
},
3236
missingPermissions: {
3337
title: (
@@ -42,12 +46,17 @@ const ERROR_MESSAGES = {
4246
defaultMessage="You do not have the necessary permissions to manage query rules. Contact your system administrator."
4347
/>
4448
),
49+
analyticsEvent: AnalyticsEvents.missingPermissionsErrorPromptLoaded,
4550
},
4651
};
4752

4853
export const ErrorPrompt: React.FC<{
4954
errorType: 'missingPermissions' | 'generic' | 'notFound';
5055
}> = ({ errorType }) => {
56+
const useTracker = useUsageTracker();
57+
useEffect(() => {
58+
useTracker?.load?.(ERROR_MESSAGES[errorType].analyticsEvent);
59+
}, [errorType, useTracker]);
5160
return (
5261
<EuiEmptyPrompt
5362
iconType="logoElasticsearch"

x-pack/solutions/search/plugins/search_query_rules/public/components/overview/overview.tsx

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* 2.0.
66
*/
77

8-
import React, { useState, useEffect } from 'react';
8+
import React, { useState } from 'react';
99

1010
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
1111
import {
@@ -31,24 +31,16 @@ import { QueryRulesSets } from '../query_rules_sets/query_rules_sets';
3131
import { CreateRulesetModal } from './create_ruleset_modal';
3232

3333
import { QueryRulesPageTemplate } from '../../layout/query_rules_page_template';
34+
import { useUsageTracker } from '../../hooks/use_usage_tracker';
35+
import { AnalyticsEvents } from '../../analytics/constants';
3436

3537
export const QueryRulesOverview = () => {
38+
const usageTracker = useUsageTracker();
3639
const { colorMode } = useEuiTheme();
37-
const {
38-
data: queryRulesData,
39-
isInitialLoading,
40-
isError,
41-
error,
42-
refetch,
43-
} = useFetchQueryRulesSets();
4440

45-
useEffect(() => {
46-
const interval = setInterval(() => {
47-
refetch();
48-
}, 1000);
49-
return () => clearInterval(interval);
50-
}, [refetch]);
41+
const { data: queryRulesData, isInitialLoading, isError, error } = useFetchQueryRulesSets();
5142
const [isCreateModalVisible, setIsCreateModalVisible] = useState(false);
43+
5244
const backgroundProps = css({
5345
backgroundImage: `url(${
5446
colorMode === 'DARK' ? queryRulesBackgroundDark : queryRulesBackground
@@ -61,6 +53,7 @@ export const QueryRulesOverview = () => {
6153
alignContent: 'center',
6254
backgroundPosition: 'center center',
6355
});
56+
6457
return (
6558
<QueryRulesPageTemplate restrictWidth={false}>
6659
{!isInitialLoading && !isError && queryRulesData?._meta.totalItemCount !== 0 && (
@@ -95,6 +88,7 @@ export const QueryRulesOverview = () => {
9588
fill
9689
iconType="plusInCircle"
9790
onClick={() => {
91+
usageTracker?.click(AnalyticsEvents.addRulesetClicked);
9892
setIsCreateModalVisible(true);
9993
}}
10094
>
@@ -143,6 +137,7 @@ export const QueryRulesOverview = () => {
143137
<EuiFlexItem>
144138
<EmptyPrompt
145139
getStartedAction={() => {
140+
usageTracker?.click(AnalyticsEvents.gettingStartedButtonClicked);
146141
setIsCreateModalVisible(true);
147142
}}
148143
/>

x-pack/solutions/search/plugins/search_query_rules/public/components/query_rules_sets/query_rules_sets.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* 2.0.
66
*/
77

8-
import React, { useState } from 'react';
8+
import React, { useEffect, useState } from 'react';
99

1010
import { QueryRulesListRulesetsQueryRulesetListItem } from '@elastic/elasticsearch/lib/api/types';
1111
import {
@@ -24,8 +24,11 @@ import { useQueryRulesSetsTableData } from '../../hooks/use_query_rules_sets_tab
2424
import { QueryRulesSetsSearch } from './query_rules_sets_search';
2525
import { DeleteRulesetModal } from './delete_ruleset_modal';
2626
import { UseRunQueryRuleset } from '../../hooks/use_run_query_ruleset';
27+
import { useUsageTracker } from '../../hooks/use_usage_tracker';
28+
import { AnalyticsEvents } from '../../analytics/constants';
2729

2830
export const QueryRulesSets = () => {
31+
const useTracker = useUsageTracker();
2932
const {
3033
services: { application, http },
3134
} = useKibana();
@@ -43,6 +46,10 @@ export const QueryRulesSets = () => {
4346
pageSize
4447
);
4548

49+
useEffect(() => {
50+
useTracker?.load?.(AnalyticsEvents.rulesetListPageLoaded);
51+
}, [useTracker]);
52+
4653
if (!queryRulesData) {
4754
return null;
4855
}
@@ -57,6 +64,7 @@ export const QueryRulesSets = () => {
5764
<EuiLink
5865
data-test-subj="queryRuleSetName"
5966
onClick={() => {
67+
useTracker?.click?.(AnalyticsEvents.editRulesetInlineNameClicked);
6068
application.navigateToUrl(
6169
http.basePath.prepend(`${PLUGIN_ROUTE_ROOT}/ruleset/${name}`)
6270
);
@@ -86,6 +94,9 @@ export const QueryRulesSets = () => {
8694
content={i18n.translate('xpack.queryRules.queryRulesSetTable.actions.run', {
8795
defaultMessage: 'Test in Console',
8896
})}
97+
onClick={() => {
98+
useTracker?.click?.(AnalyticsEvents.testRulesetInlineDropdownClicked);
99+
}}
89100
/>
90101
);
91102
},
@@ -102,10 +113,12 @@ export const QueryRulesSets = () => {
102113
icon: 'pencil',
103114
color: 'text',
104115
type: 'icon',
105-
onClick: (ruleset: QueryRulesListRulesetsQueryRulesetListItem) =>
116+
onClick: (ruleset: QueryRulesListRulesetsQueryRulesetListItem) => {
117+
useTracker?.click?.(AnalyticsEvents.editRulesetInlineDropdownClicked);
106118
application.navigateToUrl(
107119
http.basePath.prepend(`${PLUGIN_ROUTE_ROOT}/ruleset/${ruleset.ruleset_id}`)
108-
),
120+
);
121+
},
109122
},
110123
{
111124
name: i18n.translate('xpack.queryRules.queryRulesSetTable.actions.delete', {
@@ -121,6 +134,7 @@ export const QueryRulesSets = () => {
121134
type: 'icon',
122135
isPrimary: true,
123136
onClick: (ruleset: QueryRulesListRulesetsQueryRulesetListItem) => {
137+
useTracker?.click?.(AnalyticsEvents.deleteRulesetInlineDropdownClicked);
124138
setRulesetToDelete(ruleset.ruleset_id);
125139
},
126140
},

x-pack/solutions/search/plugins/search_query_rules/public/components/query_rules_sets/query_rules_sets_search.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
import { EuiFieldSearch } from '@elastic/eui';
99
import React, { useCallback } from 'react';
10+
import { useUsageTracker } from '../../hooks/use_usage_tracker';
11+
import { AnalyticsEvents } from '../../analytics/constants';
1012

1113
interface QueryRulesSetsSearchProps {
1214
searchKey: string;
@@ -17,12 +19,15 @@ export const QueryRulesSetsSearch: React.FC<QueryRulesSetsSearchProps> = ({
1719
searchKey,
1820
setSearchKey,
1921
}) => {
22+
const useTracker = useUsageTracker();
2023
const onSearch = useCallback(
2124
(newSearch: string) => {
25+
useTracker?.load?.(AnalyticsEvents.rulesetSearched);
26+
2227
const trimSearch = newSearch.trim();
2328
setSearchKey(trimSearch);
2429
},
25-
[setSearchKey]
30+
[setSearchKey, useTracker]
2631
);
2732

2833
return (

x-pack/solutions/search/plugins/search_query_rules/public/components/query_ruleset_detail/query_rule_detail_panel.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import { QueryRuleFlyout } from './query_rule_flyout/query_rule_flyout';
1616
import { useGenerateRuleId } from '../../hooks/use_generate_rule_id';
1717
import { SearchQueryRulesQueryRule } from '../../types';
1818
import { RulesetDetailEmptyPrompt } from '../empty_prompt/ruleset_detail_empty_prompt';
19+
import { useUsageTracker } from '../../hooks/use_usage_tracker';
20+
import { AnalyticsEvents } from '../../analytics/constants';
1921

2022
interface QueryRuleDetailPanelProps {
2123
rules: SearchQueryRulesQueryRule[];
@@ -46,6 +48,8 @@ export const QueryRuleDetailPanel: React.FC<QueryRuleDetailPanelProps> = ({
4648
const [ruleIdToEdit, setRuleIdToEdit] = React.useState<string | null>(null);
4749
const [flyoutMode, setFlyoutMode] = React.useState<'create' | 'edit'>('edit');
4850

51+
const useTracker = useUsageTracker();
52+
4953
const { mutate: generateRuleId } = useGenerateRuleId(rulesetId);
5054
useEffect(() => {
5155
if (createMode && rules.length === 0) {
@@ -94,6 +98,7 @@ export const QueryRuleDetailPanel: React.FC<QueryRuleDetailPanelProps> = ({
9498
color="primary"
9599
data-test-subj="queryRulesetDetailAddRuleButton"
96100
onClick={() => {
101+
useTracker?.click(AnalyticsEvents.addRuleClicked);
97102
generateRuleId(undefined, {
98103
onSuccess: (newRuleId) => {
99104
setFlyoutMode('create');

x-pack/solutions/search/plugins/search_query_rules/public/components/query_ruleset_detail/query_rule_draggable_list/query_rule_draggable_list.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ import { css } from '@emotion/react';
2727
import { QueryRulesQueryRule } from '@elastic/elasticsearch/lib/api/types';
2828
import { FormattedMessage } from '@kbn/i18n-react';
2929
import { i18n } from '@kbn/i18n';
30+
import { useUsageTracker } from '../../../hooks/use_usage_tracker';
3031
import { SearchQueryRulesQueryRule } from '../../../../common/types';
3132
import { DroppableContainer } from '../styles';
3233
import { QueryRuleDraggableListHeader } from './query_rule_draggable_list_header';
3334
import { QueryRuleDraggableListItemActionTypeBadge } from './query_rule_draggable_item_action_type_badge';
3435
import { QueryRuleDraggableItemCriteriaDisplay } from './query_rule_draggable_item_criteria_display';
3536
import { DeleteRulesetRuleModal } from './delete_ruleset_rule_modal';
37+
import { AnalyticsEvents } from '../../../analytics/constants';
3638

3739
export interface QueryRuleDraggableListItemProps {
3840
rules: SearchQueryRulesQueryRule[];
@@ -60,6 +62,7 @@ export const QueryRuleDraggableListItem: React.FC<QueryRuleDraggableListItemProp
6062
isLastItem = false,
6163
}) => {
6264
const { euiTheme } = useEuiTheme();
65+
const useTracker = useUsageTracker();
6366
const [isPopoverOpen, setIsPopoverOpen] = React.useState(false);
6467
const localTourTargetRef = useRef<HTMLDivElement>(null);
6568
const effectiveRef = tourInfo?.tourTargetRef || localTourTargetRef;
@@ -174,6 +177,7 @@ export const QueryRuleDraggableListItem: React.FC<QueryRuleDraggableListItemProp
174177
icon="pencil"
175178
data-test-subj="searchQueryRulesQueryRulesetDetailEditButton"
176179
onClick={() => {
180+
useTracker?.click(AnalyticsEvents.editRuleClicked);
177181
onEditRuleFlyoutOpen(queryRule.rule_id);
178182
closePopover();
179183
}}
@@ -204,6 +208,7 @@ export const QueryRuleDraggableListItem: React.FC<QueryRuleDraggableListItemProp
204208
`}
205209
data-test-subj="searchQueryRulesQueryRulesetDetailDeleteButton"
206210
onClick={() => {
211+
useTracker?.click(AnalyticsEvents.deleteRuleClicked);
207212
setRuleToDelete(queryRule.rule_id);
208213
closePopover();
209214
}}
@@ -263,11 +268,13 @@ export const QueryRuleDraggableList: React.FC<QueryRuleDraggableListProps> = ({
263268
tourInfo,
264269
}) => {
265270
const { euiTheme } = useEuiTheme();
271+
const useTracker = useUsageTracker();
266272

267273
return (
268274
<EuiDragDropContext
269275
onDragEnd={({ source, destination }) => {
270276
if (source && destination) {
277+
useTracker?.click(AnalyticsEvents.rulesReordered);
271278
const items = euiDragDropReorder(rules, source.index, destination.index);
272279
onReorder(items);
273280
}

0 commit comments

Comments
 (0)