[Rules] Adding actions to the rules details action menu (Part 1)#219635
[Rules] Adding actions to the rules details action menu (Part 1)#219635baileycash-elastic merged 5 commits intoelastic:mainfrom
Conversation
|
Pinging @elastic/response-ops (Team:ResponseOps) |
|
Pinging @elastic/obs-ux-management-team (Team:obs-ux-management) |
justinkambic
left a comment
There was a problem hiding this comment.
Functionality LGTM.
I tested this against a custom threshold rule and everything seemed to be working well! I have a bunch of opinionated comments that aren't required for merging but could make the code simpler/more testable.
| onRuleChanged={async () => { | ||
| refetch(); | ||
| }} |
There was a problem hiding this comment.
| onRuleChanged={async () => { | |
| refetch(); | |
| }} | |
| onRuleChanged={refetch} |
| export function useDisableRule() { | ||
| const { | ||
| http, | ||
| notifications: { toasts }, | ||
| } = useKibana().services; | ||
|
|
||
| const queryClient = useQueryClient(); | ||
|
|
||
| const disableRule = useMutation<string, string, { id: string; untrack: boolean }>( | ||
| ['disableRule'], | ||
| ({ id, untrack }) => { | ||
| const body = JSON.stringify({ | ||
| ...(untrack ? { untrack } : {}), | ||
| }); | ||
| try { | ||
| return http.post(`/api/alerting/rule/${id}/_disable`, { body }); | ||
| } catch (e) { | ||
| throw new Error(`Unable to parse id: ${e}`); | ||
| } | ||
| }, | ||
| { | ||
| onError: (_err) => { | ||
| toasts.addDanger( | ||
| i18n.translate( | ||
| 'xpack.observability.rules.disableErrorModal.errorNotification.descriptionText', | ||
| { | ||
| defaultMessage: 'Failed to disable rule', | ||
| } | ||
| ) | ||
| ); | ||
| }, | ||
|
|
||
| onSuccess: (_, variables) => { | ||
| queryClient.invalidateQueries({ queryKey: ['fetchRule', variables.id], exact: false }); | ||
| toasts.addSuccess( | ||
| i18n.translate( | ||
| 'xpack.observability.rules.disableConfirmationModal.successNotification.descriptionText', | ||
| { | ||
| defaultMessage: 'Disabled rule', | ||
| } | ||
| ) | ||
| ); | ||
| }, | ||
| } | ||
| ); | ||
|
|
||
| return disableRule; | ||
| } |
There was a problem hiding this comment.
This is an opinionated comment, but I think we have a good amount of repetition between use_disable_rule and use_enable_rule that we can abstract away. If we were to combine these two files and extract the unique values, we'd end up with a hook that requires fewer tests and allows enable/disable procedures to share common code.
Here's an example re-implementation I made that works fine with your patch:
// common code between the two hooks
function useRuleMutation({
mutationKey,
apiEndpoint,
successMessage,
errorMessage,
}: {
mutationKey: string;
apiEndpoint: (id: string) => string;
successMessage: string;
errorMessage: string;
}) {
const {
http,
notifications: { toasts },
} = useKibana().services;
const queryClient = useQueryClient();
return useMutation<string, string, { id: string; untrack?: boolean }>(
[mutationKey],
({ id, untrack }) => {
const body = untrack ? JSON.stringify({ untrack }) : undefined;
try {
return http.post(apiEndpoint(id), { body });
} catch (e) {
throw new Error(`Unable to parse id: ${e}`);
}
},
{
onError: () => {
toasts.addDanger(errorMessage);
},
onSuccess: (_, variables) => {
queryClient.invalidateQueries({ queryKey: ['fetchRule', variables.id], exact: false });
toasts.addSuccess(successMessage);
},
}
);
}
// implementation details provided for enable/disable, core functionality is shared
export function useEnableRule() {
return {
enableRule: useRuleMutation({
mutationKey: 'enableRule',
apiEndpoint: (id) => `/api/alerting/rule/${id}/_enable`,
successMessage: i18n.translate(
'xpack.observability.rules.enableConfirmationModal.successNotification.descriptionText',
{
defaultMessage: 'Enabled rule',
}
),
errorMessage: i18n.translate(
'xpack.observability.rules.enableErrorModal.errorNotification.descriptionText',
{
defaultMessage: 'Failed to enable rule',
}
),
}),
disableRule: useRuleMutation({
mutationKey: 'disableRule',
apiEndpoint: (id) => `/api/alerting/rule/${id}/_disable`,
successMessage: i18n.translate(
'xpack.observability.rules.disableConfirmationModal.successNotification.descriptionText',
{
defaultMessage: 'Disabled rule',
}
),
errorMessage: i18n.translate(
'xpack.observability.rules.disableErrorModal.errorNotification.descriptionText',
{
defaultMessage: 'Failed to disable rule',
}
),
}),
};
}This increases the parameter footprint for the internal hook, but makes it so that the functionality of "request query and issue appropriate toast on response" is separated from the actual enable/disable details. Then the implementation in the component side looks like:
const {
enableRule: { mutateAsync: enableRule },
disableRule: { mutateAsync: disableRule },
} = useEnableRule();We could also simply return the mutateAsync and rename it to simplify the destructuring in the component.
I wouldn't say this is required for merging but it follows DRY principles a bit more.
| getUntrackModal: (props: UntrackAlertsModalProps) => { | ||
| return getUntrackModalLazy(props); | ||
| }, |
There was a problem hiding this comment.
| getUntrackModal: (props: UntrackAlertsModalProps) => { | |
| return getUntrackModalLazy(props); | |
| }, | |
| getUntrackModal: UntrackAlertsModal, |
I don't know why they have this pattern here, perhaps someone from the maintaining team can respond, but this works for me locally. Probably best to just keep it this way as they have others around it that follow the same pattern.
There was a problem hiding this comment.
Yeah this is a long followed practice of this plugin, mostly to allow dynamic import for this components- load them when they are rendered for the first time.
| const onDisableModalClose = () => { | ||
| setIsUntrackAlertsModalOpen(false); | ||
| }; | ||
|
|
||
| const onDisableModalOpen = () => { | ||
| setIsUntrackAlertsModalOpen(true); | ||
| }; |
There was a problem hiding this comment.
| const onDisableModalClose = () => { | |
| setIsUntrackAlertsModalOpen(false); | |
| }; | |
| const onDisableModalOpen = () => { | |
| setIsUntrackAlertsModalOpen(true); | |
| }; |
Another opinionated commented; I don't think declaring these functions is necessary.
| const handleEnableRule = () => { | ||
| setIsRuleEditPopoverOpen(false); | ||
| enableRule({ | ||
| id: ruleId, | ||
| }); | ||
| }; | ||
|
|
||
| const handleDisableRule = (untrack: boolean) => { | ||
| setIsRuleEditPopoverOpen(false); | ||
| onDisableModalClose(); | ||
| disableRule({ | ||
| id: ruleId, | ||
| untrack, | ||
| }); | ||
| }; |
There was a problem hiding this comment.
Another opinionated comment.
| const handleEnableRule = () => { | |
| setIsRuleEditPopoverOpen(false); | |
| enableRule({ | |
| id: ruleId, | |
| }); | |
| }; | |
| const handleDisableRule = (untrack: boolean) => { | |
| setIsRuleEditPopoverOpen(false); | |
| onDisableModalClose(); | |
| disableRule({ | |
| id: ruleId, | |
| untrack, | |
| }); | |
| }; |
Regarding handleEditRule, handleRemoveRule, handleEnableRule, handleDisableRule, these functions are only referenced once, further down where they're supplied as handlers to some components. Why not just put them there and leave the functions anonymous? The code will be closer to where it's called and will remove the mental load of the reader to go back up and figure out what each handle function is doing.
I realize that the first two handle functions were already there but I think this will simplify the component.
js-jankisalvi
left a comment
There was a problem hiding this comment.
These actions are added only to Observability > Rule details page. Response ops changes lgtm 👍
| getUntrackModal: (props: UntrackAlertsModalProps) => { | ||
| return getUntrackModalLazy(props); | ||
| }, |
There was a problem hiding this comment.
Yeah this is a long followed practice of this plugin, mostly to allow dynamic import for this components- load them when they are rendered for the first time.
🎉 Snyk checks have passed. No issues have been found so far.✅ security/snyk check is complete. No issues have been found. (View Details) ✅ license/snyk check is complete. No issues have been found. (View Details) |
d8d06e6 to
c914968
Compare
|
Starting backport for target branches: 8.19 https://github.com/elastic/kibana/actions/runs/14776534047 |
…stic#219635) ## Summary Partially implements elastic#199421 This PR adds snooze and enable/disable options to the action menu of the rule details header action menu. Note: A second PR with the other 2 required actions and a slight redesign is [here](elastic#219790)   (cherry picked from commit 1ee2f62)
💚 All backports created successfully
Note: Successful backport PRs will be merged automatically after passing CI. Questions ?Please refer to the Backport tool documentation |
💔 Build Failed
Failed CI StepsTest Failures
Metrics [docs]Module Count
Public APIs missing comments
Async chunks
Public APIs missing exports
Page load bundle
History
|
…1) (#219635) (#219848) # Backport This will backport the following commits from `main` to `8.19`: - [[Rules] Adding actions to the rules details action menu (Part 1) (#219635)](#219635) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Bailey Cash","email":"bailey.cash@elastic.co"},"sourceCommit":{"committedDate":"2025-05-01T13:51:33Z","message":"[Rules] Adding actions to the rules details action menu (Part 1) (#219635)\n\n## Summary\n\nPartially implements #199421\n\nThis PR adds snooze and enable/disable options to the action menu of the\nrule details header action menu.\n\nNote: A second PR with the other 2 required actions and a slight\nredesign is [here](https://github.com/elastic/kibana/pull/219790)\n\n\n\n","sha":"1ee2f62791b413e656aae31bb5d55f62630583d9","branchLabelMapping":{"^v9.1.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:ResponseOps","Team:obs-ux-management","backport:version","v9.1.0","v8.19.0"],"title":"[Rules] Adding actions to the rules details action menu (Part 1)","number":219635,"url":"https://github.com/elastic/kibana/pull/219635","mergeCommit":{"message":"[Rules] Adding actions to the rules details action menu (Part 1) (#219635)\n\n## Summary\n\nPartially implements #199421\n\nThis PR adds snooze and enable/disable options to the action menu of the\nrule details header action menu.\n\nNote: A second PR with the other 2 required actions and a slight\nredesign is [here](https://github.com/elastic/kibana/pull/219790)\n\n\n\n","sha":"1ee2f62791b413e656aae31bb5d55f62630583d9"}},"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/219635","number":219635,"mergeCommit":{"message":"[Rules] Adding actions to the rules details action menu (Part 1) (#219635)\n\n## Summary\n\nPartially implements #199421\n\nThis PR adds snooze and enable/disable options to the action menu of the\nrule details header action menu.\n\nNote: A second PR with the other 2 required actions and a slight\nredesign is [here](https://github.com/elastic/kibana/pull/219790)\n\n\n\n","sha":"1ee2f62791b413e656aae31bb5d55f62630583d9"}},{"branch":"8.19","label":"v8.19.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Bailey Cash <bailey.cash@elastic.co>
…stic#219635) ## Summary Partially implements elastic#199421 This PR adds snooze and enable/disable options to the action menu of the rule details header action menu. Note: A second PR with the other 2 required actions and a slight redesign is [here](elastic#219790)  
Summary
Partially implements #199421
This PR adds snooze and enable/disable options to the action menu of the rule details header action menu.
Note: A second PR with the other 2 required actions and a slight redesign is here