Skip to content

Commit 3434841

Browse files
kibanamachineJoseLuisGJelasticmachine
authored
[8.19] [Search][Query Rules UI] Test in console and delete ruleset from details page (#221350) (#221726)
# Backport This will backport the following commits from `main` to `8.19`: - [[Search][Query Rules UI] Test in console and delete ruleset from details page (#221350)](#221350) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"José Luis González","email":"[email protected]"},"sourceCommit":{"committedDate":"2025-05-28T07:20:31Z","message":"[Search][Query Rules UI] Test in console and delete ruleset from details page (#221350)\n\n## Summary\n\nAdding Test in Console and Delete from the Ruleset details page:\n\n\n\nhttps://github.com/user-attachments/assets/e9db3752-a2e9-4a28-adac-0716809884d6\n\n\nTest in Console example populating data from the ruleset selected:\n\n![CleanShot 2025-05-23 at 12 18\n35@2x](https://github.com/user-attachments/assets/fc94c0e0-f412-4193-9662-9a23703fbfcd)\n\n\n\n### Checklist\n\nCheck the PR satisfies following conditions. \n\nReviewers should verify this PR satisfies this list as well.\n\n- [ ] Any text added follows [EUI's writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\nsentence case text and includes [i18n\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\n- [ ]\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\nwas added for features that require explanation or tutorials\n- [ ] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios\n- [ ] If a plugin configuration key changed, check if it needs to be\nallowlisted in the cloud and added to the [docker\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\n- [ ] This was checked for breaking HTTP API changes, and any breaking\nchanges have been approved by the breaking-change committee. The\n`release_note:breaking` label should be applied in these situations.\n- [ ] [Flaky Test\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was\nused on any tests changed\n- [ ] The PR description includes the appropriate Release Notes section,\nand the correct `release_note:*` label is applied per the\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\n\n### Identify risks\n\nDoes this PR introduce any risks? For example, consider risks like hard\nto test bugs, performance regression, potential of data loss.\n\nDescribe the risk, its severity, and mitigation for each identified\nrisk. Invite stakeholders and evaluate how to proceed before merging.\n\n- [ ] [See some risk\nexamples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)\n- [ ] ...\n\n---------\n\nCo-authored-by: Elastic Machine <[email protected]>\nCo-authored-by: kibanamachine <[email protected]>","sha":"661b96f71904783e02ec13c155062150e92f4139","branchLabelMapping":{"^v9.1.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:Search","backport:version","v9.1.0","v8.19.0"],"title":"[Search][Query Rules UI] Test in console and delete ruleset from details page","number":221350,"url":"https://github.com/elastic/kibana/pull/221350","mergeCommit":{"message":"[Search][Query Rules UI] Test in console and delete ruleset from details page (#221350)\n\n## Summary\n\nAdding Test in Console and Delete from the Ruleset details page:\n\n\n\nhttps://github.com/user-attachments/assets/e9db3752-a2e9-4a28-adac-0716809884d6\n\n\nTest in Console example populating data from the ruleset selected:\n\n![CleanShot 2025-05-23 at 12 18\n35@2x](https://github.com/user-attachments/assets/fc94c0e0-f412-4193-9662-9a23703fbfcd)\n\n\n\n### Checklist\n\nCheck the PR satisfies following conditions. \n\nReviewers should verify this PR satisfies this list as well.\n\n- [ ] Any text added follows [EUI's writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\nsentence case text and includes [i18n\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\n- [ ]\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\nwas added for features that require explanation or tutorials\n- [ ] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios\n- [ ] If a plugin configuration key changed, check if it needs to be\nallowlisted in the cloud and added to the [docker\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\n- [ ] This was checked for breaking HTTP API changes, and any breaking\nchanges have been approved by the breaking-change committee. The\n`release_note:breaking` label should be applied in these situations.\n- [ ] [Flaky Test\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was\nused on any tests changed\n- [ ] The PR description includes the appropriate Release Notes section,\nand the correct `release_note:*` label is applied per the\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\n\n### Identify risks\n\nDoes this PR introduce any risks? For example, consider risks like hard\nto test bugs, performance regression, potential of data loss.\n\nDescribe the risk, its severity, and mitigation for each identified\nrisk. Invite stakeholders and evaluate how to proceed before merging.\n\n- [ ] [See some risk\nexamples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)\n- [ ] ...\n\n---------\n\nCo-authored-by: Elastic Machine <[email protected]>\nCo-authored-by: kibanamachine <[email protected]>","sha":"661b96f71904783e02ec13c155062150e92f4139"}},"sourceBranch":"main","suggestedTargetBranches":["8.19"],"targetPullRequestStates":[{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/221350","number":221350,"mergeCommit":{"message":"[Search][Query Rules UI] Test in console and delete ruleset from details page (#221350)\n\n## Summary\n\nAdding Test in Console and Delete from the Ruleset details page:\n\n\n\nhttps://github.com/user-attachments/assets/e9db3752-a2e9-4a28-adac-0716809884d6\n\n\nTest in Console example populating data from the ruleset selected:\n\n![CleanShot 2025-05-23 at 12 18\n35@2x](https://github.com/user-attachments/assets/fc94c0e0-f412-4193-9662-9a23703fbfcd)\n\n\n\n### Checklist\n\nCheck the PR satisfies following conditions. \n\nReviewers should verify this PR satisfies this list as well.\n\n- [ ] Any text added follows [EUI's writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\nsentence case text and includes [i18n\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\n- [ ]\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\nwas added for features that require explanation or tutorials\n- [ ] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios\n- [ ] If a plugin configuration key changed, check if it needs to be\nallowlisted in the cloud and added to the [docker\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\n- [ ] This was checked for breaking HTTP API changes, and any breaking\nchanges have been approved by the breaking-change committee. The\n`release_note:breaking` label should be applied in these situations.\n- [ ] [Flaky Test\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was\nused on any tests changed\n- [ ] The PR description includes the appropriate Release Notes section,\nand the correct `release_note:*` label is applied per the\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\n\n### Identify risks\n\nDoes this PR introduce any risks? For example, consider risks like hard\nto test bugs, performance regression, potential of data loss.\n\nDescribe the risk, its severity, and mitigation for each identified\nrisk. Invite stakeholders and evaluate how to proceed before merging.\n\n- [ ] [See some risk\nexamples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)\n- [ ] ...\n\n---------\n\nCo-authored-by: Elastic Machine <[email protected]>\nCo-authored-by: kibanamachine <[email protected]>","sha":"661b96f71904783e02ec13c155062150e92f4139"}},{"branch":"8.19","label":"v8.19.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: José Luis González <[email protected]> Co-authored-by: Elastic Machine <[email protected]>
1 parent e686f31 commit 3434841

File tree

5 files changed

+273
-58
lines changed

5 files changed

+273
-58
lines changed

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,25 @@ import { useDeleteRuleset } from '../../hooks/use_delete_query_rules_ruleset';
2020
export interface DeleteRulesetModalProps {
2121
rulesetId: string;
2222
closeDeleteModal: () => void;
23+
onSuccess?: () => void;
2324
}
2425

25-
export const DeleteRulesetModal = ({ closeDeleteModal, rulesetId }: DeleteRulesetModalProps) => {
26+
export const DeleteRulesetModal = ({
27+
closeDeleteModal,
28+
rulesetId,
29+
onSuccess: onSuccessProp,
30+
}: DeleteRulesetModalProps) => {
2631
const [error, setError] = useState<string | null>(null);
2732
const [isLoading, setIsLoading] = useState(false);
33+
2834
const onSuccess = () => {
2935
setIsLoading(false);
3036
closeDeleteModal();
37+
if (onSuccessProp) {
38+
onSuccessProp();
39+
}
3140
};
41+
3242
const confirmCheckboxId = useGeneratedHtmlId({
3343
prefix: 'confirmCheckboxId',
3444
});

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ describe('Query rule detail', () => {
3030
const TEST_IDS = {
3131
DetailPage: 'queryRulesetDetailPage',
3232
DetailPageHeader: 'queryRulesetDetailHeader',
33-
HeaderDataButton: 'queryRulesetDetailHeaderDataButton',
3433
HeaderSaveButton: 'queryRulesetDetailHeaderSaveButton',
3534
AddRuleButton: 'queryRulesetDetailAddRuleButton',
3635
DraggableItem: 'searchQueryRulesDraggableItem',
@@ -49,7 +48,6 @@ describe('Query rule detail', () => {
4948

5049
const header = screen.getByTestId(TEST_IDS.DetailPageHeader);
5150
expect(within(header).getByText('my-ruleset')).toBeInTheDocument();
52-
expect(within(header).getByTestId(TEST_IDS.HeaderDataButton)).toBeInTheDocument();
5351
expect(within(header).getByTestId(TEST_IDS.HeaderSaveButton)).toBeInTheDocument();
5452
expect(screen.getByTestId(TEST_IDS.AddRuleButton)).toBeInTheDocument();
5553
expect(screen.getAllByTestId(TEST_IDS.DraggableItem)).toHaveLength(

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

Lines changed: 95 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,17 @@
88
import React, { useEffect, useState } from 'react';
99

1010
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
11-
import { EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui';
11+
import {
12+
EuiButton,
13+
EuiButtonIcon,
14+
EuiContextMenuItem,
15+
EuiContextMenuPanel,
16+
EuiFlexGroup,
17+
EuiFlexItem,
18+
EuiIcon,
19+
EuiPopover,
20+
useGeneratedHtmlId,
21+
} from '@elastic/eui';
1222
import { FormattedMessage } from '@kbn/i18n-react';
1323
import { useParams } from 'react-router-dom';
1424
import { QueryRulesQueryRule } from '@elastic/elasticsearch/lib/api/types';
@@ -20,6 +30,8 @@ import { ErrorPrompt } from '../error_prompt/error_prompt';
2030
import { isNotFoundError, isPermissionError } from '../../utils/query_rules_utils';
2131
import { QueryRulesPageTemplate } from '../../layout/query_rules_page_template';
2232
import { QueryRuleDetailPanel } from './query_rule_detail_panel';
33+
import { UseRunQueryRuleset } from '../../hooks/use_run_query_ruleset';
34+
import { DeleteRulesetModal } from '../query_rules_sets/delete_ruleset_modal';
2335

2436
export const QueryRulesetDetail: React.FC = () => {
2537
const {
@@ -38,6 +50,31 @@ export const QueryRulesetDetail: React.FC = () => {
3850

3951
const [rules, setRules] = useState<QueryRulesQueryRule[]>(queryRulesetData?.rules ?? []);
4052

53+
const [isPopoverOpen, setPopover] = useState(false);
54+
const splitButtonPopoverId = useGeneratedHtmlId({
55+
prefix: 'splitButtonPopover',
56+
});
57+
58+
const onButtonClick = () => {
59+
setPopover(!isPopoverOpen);
60+
};
61+
62+
const closePopover = () => {
63+
setPopover(false);
64+
};
65+
const items = [
66+
<EuiContextMenuItem
67+
color="danger"
68+
key="delete"
69+
icon="trash"
70+
onClick={() => setRulesetToDelete(rulesetId)}
71+
>
72+
Delete ruleset
73+
</EuiContextMenuItem>,
74+
];
75+
76+
const [rulesetToDelete, setRulesetToDelete] = useState<string | null>(null);
77+
4178
useEffect(() => {
4279
if (queryRulesetData?.rules) {
4380
setRules(queryRulesetData.rules);
@@ -72,40 +109,68 @@ export const QueryRulesetDetail: React.FC = () => {
72109
rightSideItems={[
73110
<EuiFlexGroup alignItems="center" key="queryRulesetDetailHeaderButtons">
74111
<EuiFlexItem grow={false}>
75-
<EuiButtonEmpty
76-
iconType="database"
77-
color="primary"
78-
data-test-subj="queryRulesetDetailHeaderDataButton"
79-
onClick={() => {
80-
// Logic to handle data button click
81-
}}
82-
>
83-
<FormattedMessage
84-
id="xpack.queryRules.queryRulesetDetail.dataButton"
85-
defaultMessage="Data"
86-
/>
87-
</EuiButtonEmpty>
88-
</EuiFlexItem>
89-
<EuiFlexItem grow={false}>
90-
<EuiButton
91-
iconType="save"
92-
fill
93-
color="primary"
94-
data-test-subj="queryRulesetDetailHeaderSaveButton"
95-
onClick={() => {
96-
// Logic to save the query ruleset
97-
}}
98-
>
99-
<FormattedMessage
100-
id="xpack.queryRules.queryRulesetDetail.saveButton"
101-
defaultMessage="Save"
102-
/>
103-
</EuiButton>
112+
<UseRunQueryRuleset
113+
rulesetId={rulesetId}
114+
type="contextMenuItem"
115+
content={i18n.translate('xpack.queryRules.queryRulesetDetail.testButton', {
116+
defaultMessage: 'Test in Console',
117+
})}
118+
/>
104119
</EuiFlexItem>
120+
<EuiFlexGroup responsive={false} gutterSize="xs" alignItems="center">
121+
<EuiFlexItem grow={false}>
122+
<EuiButton
123+
iconType="save"
124+
fill
125+
color="primary"
126+
data-test-subj="queryRulesetDetailHeaderSaveButton"
127+
onClick={() => {
128+
// Logic to save the query ruleset
129+
}}
130+
>
131+
<FormattedMessage
132+
id="xpack.queryRules.queryRulesetDetail.saveButton"
133+
defaultMessage="Save"
134+
/>
135+
</EuiButton>
136+
</EuiFlexItem>
137+
<EuiFlexItem grow={false}>
138+
<EuiPopover
139+
id={splitButtonPopoverId}
140+
button={
141+
<EuiButtonIcon
142+
data-test-subj="searchQueryRulesQueryRulesetDetailButton"
143+
display="fill"
144+
size="m"
145+
iconType="boxesVertical"
146+
aria-label="More"
147+
onClick={onButtonClick}
148+
/>
149+
}
150+
isOpen={isPopoverOpen}
151+
closePopover={closePopover}
152+
panelPaddingSize="none"
153+
anchorPosition="downLeft"
154+
>
155+
<EuiContextMenuPanel size="s" items={items} />
156+
</EuiPopover>
157+
</EuiFlexItem>
158+
</EuiFlexGroup>
105159
</EuiFlexGroup>,
106160
]}
107161
/>
108162
)}
163+
{rulesetToDelete && (
164+
<DeleteRulesetModal
165+
rulesetId={rulesetToDelete}
166+
closeDeleteModal={() => {
167+
setRulesetToDelete(null);
168+
}}
169+
onSuccess={() => {
170+
application.navigateToUrl(http.basePath.prepend(`${PLUGIN_ROUTE_ROOT}`));
171+
}}
172+
/>
173+
)}
109174
{!isError && <QueryRuleDetailPanel rules={rules} setRules={setRules} />}
110175
{isError && (
111176
<ErrorPrompt

x-pack/solutions/search/plugins/search_query_rules/public/hooks/use_run_query_ruleset.test.tsx

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ describe('UseRunQueryRuleset', () => {
4141
console: mockConsole,
4242
},
4343
});
44+
45+
// Default mock for ruleset data
4446
(useFetchQueryRuleset as jest.Mock).mockReturnValue({
4547
data: {
4648
rules: [
@@ -53,6 +55,13 @@ describe('UseRunQueryRuleset', () => {
5355
},
5456
],
5557
},
58+
criteria: [
59+
{
60+
metadata: 'user_query',
61+
values: ['test-query'],
62+
type: 'exact',
63+
},
64+
],
5665
},
5766
],
5867
},
@@ -76,9 +85,15 @@ describe('UseRunQueryRuleset', () => {
7685
expect.anything()
7786
);
7887

79-
// Verify that the request contains the index from the fetched data
8088
const buttonProps = (TryInConsoleButton as jest.Mock).mock.calls[0][0];
89+
// Verify that the request contains the index from the fetched data
8190
expect(buttonProps.request).toContain('test-index');
91+
// Verify the request contains retriever structure
92+
expect(buttonProps.request).toContain('retriever');
93+
// Verify the request contains the expected query
94+
expect(buttonProps.request).toContain('"query": "pugs"');
95+
// Verify ruleset_ids are included
96+
expect(buttonProps.request).toContain('test-ruleset');
8297
});
8398

8499
it('renders with custom type and content', () => {
@@ -107,11 +122,86 @@ describe('UseRunQueryRuleset', () => {
107122
expect(buttonProps.request).toContain('my_index');
108123
});
109124

110-
it('creates correct query with ruleset ID in the request', () => {
111-
const rulesetId = 'special-test-ruleset';
112-
render(<UseRunQueryRuleset rulesetId={rulesetId} />);
125+
it('handles multiple indices from ruleset data', () => {
126+
(useFetchQueryRuleset as jest.Mock).mockReturnValue({
127+
data: {
128+
rules: [
129+
{
130+
actions: {
131+
docs: [
132+
{ _index: 'index1', _id: 'id1' },
133+
{ _index: 'index2', _id: 'id2' },
134+
],
135+
},
136+
},
137+
],
138+
},
139+
isInitialLoading: false,
140+
isError: false,
141+
});
142+
143+
render(<UseRunQueryRuleset rulesetId="test-ruleset" />);
144+
145+
const buttonProps = (TryInConsoleButton as jest.Mock).mock.calls[0][0];
146+
expect(buttonProps.request).toContain('index1,index2');
147+
});
148+
149+
it('creates match criteria from ruleset data', () => {
150+
(useFetchQueryRuleset as jest.Mock).mockReturnValue({
151+
data: {
152+
rules: [
153+
{
154+
criteria: [
155+
{
156+
metadata: 'user_query',
157+
values: 'search term',
158+
type: 'exact',
159+
},
160+
{
161+
metadata: 'user_location',
162+
values: ['US', 'UK'],
163+
type: 'exact',
164+
},
165+
],
166+
},
167+
],
168+
},
169+
isInitialLoading: false,
170+
isError: false,
171+
});
172+
173+
render(<UseRunQueryRuleset rulesetId="test-ruleset" />);
174+
175+
const buttonProps = (TryInConsoleButton as jest.Mock).mock.calls[0][0];
176+
expect(buttonProps.request).toContain('"user_query": "search term"');
177+
expect(buttonProps.request).toMatch(/"user_location":\s*\[\s*"US",\s*"UK"\s*\]/);
178+
});
179+
180+
it('handles complex nested criteria values', () => {
181+
(useFetchQueryRuleset as jest.Mock).mockReturnValue({
182+
data: {
183+
rules: [
184+
{
185+
criteria: [
186+
{
187+
values: {
188+
nested_field: 'nested value',
189+
another_field: ['array', 'of', 'values'],
190+
},
191+
type: 'exact',
192+
},
193+
],
194+
},
195+
],
196+
},
197+
isInitialLoading: false,
198+
isError: false,
199+
});
200+
201+
render(<UseRunQueryRuleset rulesetId="test-ruleset" />);
113202

114203
const buttonProps = (TryInConsoleButton as jest.Mock).mock.calls[0][0];
115-
expect(buttonProps.request).toContain(`"ruleset_ids": [ "${rulesetId}" ]`);
204+
expect(buttonProps.request).toContain('"nested_field": "nested value"');
205+
expect(buttonProps.request).toMatch(/"another_field":\s*\[\s*"array",\s*"of",\s*"values"\s*\]/);
116206
});
117207
});

0 commit comments

Comments
 (0)