[8.6] [Security Solution] Update cache invalidation logic to handle error responses (#146271) (#146380)

# Backport

This will backport the following commits from `main` to `8.6`:
- [[Security Solution] Update cache invalidation logic to handle error
responses (#146271)](https://github.com/elastic/kibana/pull/146271)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Dmitrii
Shevchenko","email":"dmitrii.shevchenko@elastic.co"},"sourceCommit":{"committedDate":"2022-11-28T11:50:40Z","message":"[Security
Solution] Update cache invalidation logic to handle error responses
(#146271)\n\n**Resolves:
https://github.com/elastic/kibana/issues/146277**\r\n\r\n##
Summary\r\n\r\nPreviously, we invalidated the rules table cache only
after successful\r\nserver-side state mutations. So when an action like
bulk edit was\r\nsuccessfully updating some rules and failing for
others, the table\r\ncontinued showing outdated results.\r\n\r\nThis PR
moves the cache invalidation from the `onSuccess` handlers to\r\nthe
`onSettled` handlers to prevent showing partially stale data
after\r\nfailed
updates.","sha":"e9bc60355858c82fe39676622004f8e9cdfa61a2","branchLabelMapping":{"^v8.7.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["bug","release_note:fix","impact:low","Team:Detections
and Resp","Team: SecuritySolution","auto-backport","Feature:Rule
Management","Team:Detection
Rules","v8.6.0","v8.7.0"],"number":146271,"url":"https://github.com/elastic/kibana/pull/146271","mergeCommit":{"message":"[Security
Solution] Update cache invalidation logic to handle error responses
(#146271)\n\n**Resolves:
https://github.com/elastic/kibana/issues/146277**\r\n\r\n##
Summary\r\n\r\nPreviously, we invalidated the rules table cache only
after successful\r\nserver-side state mutations. So when an action like
bulk edit was\r\nsuccessfully updating some rules and failing for
others, the table\r\ncontinued showing outdated results.\r\n\r\nThis PR
moves the cache invalidation from the `onSuccess` handlers to\r\nthe
`onSettled` handlers to prevent showing partially stale data
after\r\nfailed
updates.","sha":"e9bc60355858c82fe39676622004f8e9cdfa61a2"}},"sourceBranch":"main","suggestedTargetBranches":["8.6"],"targetPullRequestStates":[{"branch":"8.6","label":"v8.6.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.7.0","labelRegex":"^v8.7.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/146271","number":146271,"mergeCommit":{"message":"[Security
Solution] Update cache invalidation logic to handle error responses
(#146271)\n\n**Resolves:
https://github.com/elastic/kibana/issues/146277**\r\n\r\n##
Summary\r\n\r\nPreviously, we invalidated the rules table cache only
after successful\r\nserver-side state mutations. So when an action like
bulk edit was\r\nsuccessfully updating some rules and failing for
others, the table\r\ncontinued showing outdated results.\r\n\r\nThis PR
moves the cache invalidation from the `onSuccess` handlers to\r\nthe
`onSettled` handlers to prevent showing partially stale data
after\r\nfailed
updates.","sha":"e9bc60355858c82fe39676622004f8e9cdfa61a2"}}]}]
BACKPORT-->

Co-authored-by: Dmitrii Shevchenko <dmitrii.shevchenko@elastic.co>
This commit is contained in:
Kibana Machine 2022-11-28 08:18:03 -05:00 committed by GitHub
parent 2499d5589a
commit 8320b80002
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 87 additions and 57 deletions

View file

@ -206,14 +206,22 @@ export interface BulkActionAggregatedError {
rules: Array<{ id: string; name?: string }>;
}
export interface BulkActionAttributes {
summary: BulkActionSummary;
results: BulkActionResult;
errors?: BulkActionAggregatedError[];
}
export interface BulkActionResponse {
success?: boolean;
rules_count?: number;
attributes: {
summary: BulkActionSummary;
results: BulkActionResult;
errors?: BulkActionAggregatedError[];
};
attributes: BulkActionAttributes;
}
export interface BulkActionErrorResponse {
message: string;
status_code: number;
attributes?: BulkActionAttributes;
}
export type QueryOrIds = { query: string; ids?: undefined } | { query?: undefined; ids: string[] };

View file

@ -6,8 +6,9 @@
*/
import type { UseMutationOptions } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import type { IHttpFetchError } from '@kbn/core/public';
import { BulkActionType } from '../../../../../common/detection_engine/rule_management/api/rules/bulk_actions/request_schema';
import type { BulkActionResponse, PerformBulkActionProps } from '../api';
import type { BulkActionErrorResponse, BulkActionResponse, PerformBulkActionProps } from '../api';
import { performBulkAction } from '../api';
import { useInvalidateFetchPrebuiltRulesStatusQuery } from './use_fetch_prebuilt_rules_status_query';
import { useInvalidateFindRulesQuery, useUpdateRulesCache } from './use_find_rules_query';
@ -18,7 +19,11 @@ import { DETECTION_ENGINE_RULES_BULK_ACTION } from '../../../../../common/consta
export const BULK_ACTION_MUTATION_KEY = ['POST', DETECTION_ENGINE_RULES_BULK_ACTION];
export const useBulkActionMutation = (
options?: UseMutationOptions<BulkActionResponse, Error, PerformBulkActionProps>
options?: UseMutationOptions<
BulkActionResponse,
IHttpFetchError<BulkActionErrorResponse>,
PerformBulkActionProps
>
) => {
const invalidateFindRulesQuery = useInvalidateFindRulesQuery();
const invalidateFetchRuleByIdQuery = useInvalidateFetchRuleByIdQuery();
@ -26,47 +31,64 @@ export const useBulkActionMutation = (
const invalidateFetchPrebuiltRulesStatusQuery = useInvalidateFetchPrebuiltRulesStatusQuery();
const updateRulesCache = useUpdateRulesCache();
return useMutation<BulkActionResponse, Error, PerformBulkActionProps>(
(bulkActionProps: PerformBulkActionProps) => performBulkAction(bulkActionProps),
{
...options,
mutationKey: BULK_ACTION_MUTATION_KEY,
onSuccess: (...args) => {
const [
res,
{
bulkAction: { type: actionType },
},
] = args;
switch (actionType) {
case BulkActionType.enable:
case BulkActionType.disable: {
invalidateFetchRuleByIdQuery();
// This action doesn't affect rule content, no need for invalidation
updateRulesCache(res?.attributes?.results?.updated ?? []);
break;
}
case BulkActionType.delete:
invalidateFindRulesQuery();
invalidateFetchRuleByIdQuery();
invalidateFetchTagsQuery();
invalidateFetchPrebuiltRulesStatusQuery();
break;
case BulkActionType.duplicate:
invalidateFindRulesQuery();
invalidateFetchPrebuiltRulesStatusQuery();
break;
case BulkActionType.edit:
updateRulesCache(res?.attributes?.results?.updated ?? []);
invalidateFetchRuleByIdQuery();
invalidateFetchTagsQuery();
break;
}
return useMutation<
BulkActionResponse,
IHttpFetchError<BulkActionErrorResponse>,
PerformBulkActionProps
>((bulkActionProps: PerformBulkActionProps) => performBulkAction(bulkActionProps), {
...options,
mutationKey: BULK_ACTION_MUTATION_KEY,
onSettled: (...args) => {
const [
response,
error,
{
bulkAction: { type: actionType },
},
] = args;
if (options?.onSuccess) {
options.onSuccess(...args);
const updatedRules =
response?.attributes?.results?.updated ?? error?.body?.attributes?.results?.updated;
switch (actionType) {
case BulkActionType.enable:
case BulkActionType.disable: {
invalidateFetchRuleByIdQuery();
if (updatedRules) {
// We have a list of updated rules, no need to invalidate all
updateRulesCache(updatedRules);
} else {
// We failed to receive the list of update rules, invalidate all
invalidateFindRulesQuery();
}
break;
}
},
}
);
case BulkActionType.delete:
invalidateFindRulesQuery();
invalidateFetchRuleByIdQuery();
invalidateFetchTagsQuery();
invalidateFetchPrebuiltRulesStatusQuery();
break;
case BulkActionType.duplicate:
invalidateFindRulesQuery();
invalidateFetchPrebuiltRulesStatusQuery();
break;
case BulkActionType.edit:
if (updatedRules) {
// We have a list of updated rules, no need to invalidate all
updateRulesCache(updatedRules);
} else {
// We failed to receive the list of update rules, invalidate all
invalidateFindRulesQuery();
}
invalidateFetchRuleByIdQuery();
invalidateFetchTagsQuery();
break;
}
if (options?.onSettled) {
options.onSettled(...args);
}
},
});
};

View file

@ -25,15 +25,15 @@ export const useCreatePrebuiltRulesMutation = (
return useMutation(() => createPrepackagedRules(), {
...options,
mutationKey: CREATE_PREBUILT_RULES_MUTATION_KEY,
onSuccess: (...args) => {
onSettled: (...args) => {
// Always invalidate all rules and the prepackaged rules status cache as
// the number of rules might change after the installation
invalidatePrePackagedRulesStatus();
invalidateFindRulesQuery();
invalidateFetchTagsQuery();
if (options?.onSuccess) {
options.onSuccess(...args);
if (options?.onSettled) {
options.onSettled(...args);
}
},
});

View file

@ -31,13 +31,13 @@ export const useCreateRuleMutation = (
{
...options,
mutationKey: CREATE_RULE_MUTATION_KEY,
onSuccess: (...args) => {
onSettled: (...args) => {
invalidateFetchPrePackagedRulesStatusQuery();
invalidateFindRulesQuery();
invalidateFetchTagsQuery();
if (options?.onSuccess) {
options.onSuccess(...args);
if (options?.onSettled) {
options.onSettled(...args);
}
},
}

View file

@ -31,13 +31,13 @@ export const useUpdateRuleMutation = (
{
...options,
mutationKey: UPDATE_RULE_MUTATION_KEY,
onSuccess: (...args) => {
onSettled: (...args) => {
invalidateFindRulesQuery();
invalidateFetchRuleByIdQuery();
invalidateFetchTagsQuery();
if (options?.onSuccess) {
options.onSuccess(...args);
if (options?.onSettled) {
options.onSettled(...args);
}
},
}