Skip to content

Commit

Permalink
Reduce the _review rule upgrade endpoint response size
Browse files Browse the repository at this point in the history
  • Loading branch information
xcrzx committed Feb 14, 2025
1 parent 526ff05 commit 004b8c1
Show file tree
Hide file tree
Showing 10 changed files with 234 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,68 @@
* 2.0.
*/

import type { RuleObjectId, RuleSignatureId, RuleTagArray } from '../../model';
import { z } from '@kbn/zod';
import { SortOrder, type RuleObjectId, type RuleSignatureId, type RuleTagArray } from '../../model';
import type { PartialRuleDiff } from '../model';
import type { RuleResponse } from '../../model/rule_schema';
import type { RuleResponse, RuleVersion } from '../../model/rule_schema';
import { FindRulesSortField } from '../../rule_management';

export type ReviewRuleUpgradeRequestBody = z.infer<typeof ReviewRuleUpgradeRequestBody>;
export const ReviewRuleUpgradeRequestBody = z
.object({
/**
* Rule IDs to return upgrade info for
*/
rule_ids: z.array(z.string()).optional(),
/**
* Search query
*/
filter: z.string().optional(),
/**
* Field to sort by
*/
sort_field: FindRulesSortField.optional(),
/**
* Sort order
*/
sort_order: SortOrder.optional(),
/**
* Page number
*/
page: z.coerce.number().int().min(1).optional().default(1),
/**
* Rules per page
*/
per_page: z.coerce.number().int().min(0).optional().default(20),
})
.nullable();

export interface ReviewRuleUpgradeResponseBody {
/** Aggregated info about all rules available for upgrade */
stats: RuleUpgradeStatsForReview;

/** Info about individual rules: one object per each rule available for upgrade */
rules: RuleUpgradeInfoForReview[];

/** The requested page number */
page: number;

/** The requested number of items per page */
per_page: number;
}

export interface RuleUpgradeStatsForReview {
/** Number of installed prebuilt rules available for upgrade (stock + customized) */
num_rules_to_upgrade_total: number;

/** Number of installed prebuilt rules with upgrade conflicts (SOLVABLE or NON_SOLVABLE) */
/**
* @deprecated Always 0
*/
num_rules_with_conflicts: number;

/** Number of installed prebuilt rules with NON_SOLVABLE upgrade conflicts */
/**
* @deprecated Always 0
*/
num_rules_with_non_solvable_conflicts: number;

/** A union of all tags of all rules available for upgrade */
Expand All @@ -34,6 +76,7 @@ export interface RuleUpgradeStatsForReview {
export interface RuleUpgradeInfoForReview {
id: RuleObjectId;
rule_id: RuleSignatureId;
version: RuleVersion;
current_rule: RuleResponse;
target_rule: RuleResponse;
diff: PartialRuleDiff;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import type {
GetPrebuiltRulesStatusResponseBody,
ReviewRuleUpgradeResponseBody,
ReviewRuleInstallationResponseBody,
ReviewRuleUpgradeRequestBody,
} from '../../../../common/api/detection_engine/prebuilt_rules';
import type {
BulkDuplicateRules,
Expand Down Expand Up @@ -637,13 +638,16 @@ export const getPrebuiltRulesStatus = async ({
*/
export const reviewRuleUpgrade = async ({
signal,
request,
}: {
signal: AbortSignal | undefined;
request: ReviewRuleUpgradeRequestBody;
}): Promise<ReviewRuleUpgradeResponseBody> =>
KibanaServices.get().http.fetch(REVIEW_RULE_UPGRADE_URL, {
method: 'POST',
version: '1',
signal,
body: JSON.stringify(request),
});

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,24 @@ import type { UseQueryOptions } from '@tanstack/react-query';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { reviewRuleUpgrade } from '../../api';
import { REVIEW_RULE_UPGRADE_URL } from '../../../../../../common/api/detection_engine/prebuilt_rules/urls';
import type { ReviewRuleUpgradeResponseBody } from '../../../../../../common/api/detection_engine/prebuilt_rules';
import type {
ReviewRuleUpgradeRequestBody,
ReviewRuleUpgradeResponseBody,
} from '../../../../../../common/api/detection_engine/prebuilt_rules';
import { DEFAULT_QUERY_OPTIONS } from '../constants';
import { retryOnRateLimitedError } from './retry_on_rate_limited_error';
import { cappedExponentialBackoff } from './capped_exponential_backoff';

export const REVIEW_RULE_UPGRADE_QUERY_KEY = ['POST', REVIEW_RULE_UPGRADE_URL];

export const useFetchPrebuiltRulesUpgradeReviewQuery = (
request: ReviewRuleUpgradeRequestBody,
options?: UseQueryOptions<ReviewRuleUpgradeResponseBody>
) => {
return useQuery<ReviewRuleUpgradeResponseBody>(
REVIEW_RULE_UPGRADE_QUERY_KEY,
[...REVIEW_RULE_UPGRADE_QUERY_KEY, request],
async ({ signal }) => {
const response = await reviewRuleUpgrade({ signal });
const response = await reviewRuleUpgrade({ signal, request });
return response;
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
* 2.0.
*/
import type { UseQueryOptions } from '@tanstack/react-query';
import type { ReviewRuleUpgradeResponseBody } from '../../../../../common/api/detection_engine/prebuilt_rules';
import type {
ReviewRuleUpgradeRequestBody,
ReviewRuleUpgradeResponseBody,
} from '../../../../../common/api/detection_engine/prebuilt_rules';
import { useAppToasts } from '../../../../common/hooks/use_app_toasts';

import * as i18n from '../translations';
Expand All @@ -18,11 +21,12 @@ import { useFetchPrebuiltRulesUpgradeReviewQuery } from '../../api/hooks/prebuil
* @returns useQuery result
*/
export const usePrebuiltRulesUpgradeReview = (
request: ReviewRuleUpgradeRequestBody,
options?: UseQueryOptions<ReviewRuleUpgradeResponseBody>
) => {
const { addError } = useAppToasts();

return useFetchPrebuiltRulesUpgradeReviewQuery({
return useFetchPrebuiltRulesUpgradeReviewQuery(request, {
onError: (error) => addError(error, { title: i18n.RULE_AND_TIMELINE_FETCH_FAILURE }),
...options,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
EuiEmptyPrompt,
EuiFlexGroup,
EuiFlexItem,
EuiInMemoryTable,
EuiBasicTable,
EuiProgress,
EuiSkeletonLoading,
EuiSkeletonText,
Expand All @@ -19,7 +19,7 @@ import {
import React, { useCallback, useState } from 'react';
import type { RuleUpgradeState } from '../../../../rule_management/model/prebuilt_rule_upgrade';
import * as i18n from '../../../../../detections/pages/detection_engine/rules/translations';
import { RULES_TABLE_INITIAL_PAGE_SIZE, RULES_TABLE_PAGE_SIZE_OPTIONS } from '../constants';
import { RULES_TABLE_PAGE_SIZE_OPTIONS } from '../constants';
import { RulesChangelogLink } from '../rules_changelog_link';
import { UpgradePrebuiltRulesTableButtons } from './upgrade_prebuilt_rules_table_buttons';
import { useUpgradePrebuiltRulesTableContext } from './upgrade_prebuilt_rules_table_context';
Expand All @@ -46,18 +46,22 @@ export const UpgradePrebuiltRulesTable = React.memo(() => {
isLoading,
isRefetching,
isUpgradingSecurityPackages,
pagination,
},
actions: { setPagination },
} = useUpgradePrebuiltRulesTableContext();
const [selected, setSelected] = useState<RuleUpgradeState[]>([]);

const rulesColumns = useUpgradePrebuiltRulesTableColumns();
const shouldShowProgress = isUpgradingSecurityPackages || isRefetching;
const [pageIndex, setPageIndex] = useState(0);
const handleTableChange = useCallback(
({ page: { index } }: CriteriaWithPagination<RuleUpgradeState>) => {
setPageIndex(index);
({ page: { index, size } }: CriteriaWithPagination<RuleUpgradeState>) => {
setPagination({
page: index + 1,
perPage: size,
});
},
[setPageIndex]
[setPagination]
);

return (
Expand Down Expand Up @@ -104,13 +108,13 @@ export const UpgradePrebuiltRulesTable = React.memo(() => {
</EuiFlexItem>
</EuiFlexGroup>

<EuiInMemoryTable
<EuiBasicTable
items={ruleUpgradeStates}
sorting
pagination={{
initialPageSize: RULES_TABLE_INITIAL_PAGE_SIZE,
totalItemCount: pagination.total,
pageSizeOptions: RULES_TABLE_PAGE_SIZE_OPTIONS,
pageIndex,
pageIndex: pagination.page - 1,
pageSize: pagination.perPage,
}}
selection={{
selectable: () => true,
Expand All @@ -120,7 +124,7 @@ export const UpgradePrebuiltRulesTable = React.memo(() => {
itemId="rule_id"
data-test-subj="rules-upgrades-table"
columns={rulesColumns}
onTableChange={handleTableChange}
onChange={handleTableChange}
/>
</>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import { UpgradeFlyoutSubHeader } from './upgrade_flyout_subheader';
import * as ruleDetailsI18n from '../../../../rule_management/components/rule_details/translations';
import * as i18n from './translations';
import { CustomizationDisabledCallout } from './customization_disabled_callout';
import { RULES_TABLE_INITIAL_PAGE_SIZE } from '../constants';
import type { PaginationOptions } from '../../../../rule_management/logic';

const REVIEW_PREBUILT_RULES_UPGRADE_REFRESH_INTERVAL = 5 * 60 * 1000;

Expand Down Expand Up @@ -84,6 +86,10 @@ export interface UpgradePrebuiltRulesTableState {
* The timestamp for when the rules were successfully fetched
*/
lastUpdated: number;
/**
* Current pagination state
*/
pagination: PaginationOptions;
}

export const PREBUILT_RULE_UPDATE_FLYOUT_ANCHOR = 'updatePrebuiltRulePreview';
Expand All @@ -93,6 +99,7 @@ export interface UpgradePrebuiltRulesTableActions {
upgradeRules: (ruleIds: RuleSignatureId[]) => void;
upgradeAllRules: () => void;
setFilterOptions: Dispatch<SetStateAction<UpgradePrebuiltRulesTableFilterOptions>>;
setPagination: Dispatch<SetStateAction<{ page: number; perPage: number }>>;
openRulePreview: (ruleId: string) => void;
}

Expand Down Expand Up @@ -128,21 +135,33 @@ export const UpgradePrebuiltRulesTableContextProvider = ({
ruleSource: [],
});
const isUpgradingSecurityPackages = useIsUpgradingSecurityPackages();
const [pagination, setPagination] = useState({
page: 1,
perPage: RULES_TABLE_INITIAL_PAGE_SIZE,
});

const {
data: { rules: ruleUpgradeInfos, stats: { tags } } = {
rules: [],
stats: { tags: [] },
},
data: upgradeReviewData,
refetch,
dataUpdatedAt,
isFetched,
isLoading,
isRefetching,
} = usePrebuiltRulesUpgradeReview({
refetchInterval: REVIEW_PREBUILT_RULES_UPGRADE_REFRESH_INTERVAL,
keepPreviousData: true, // Use this option so that the state doesn't jump between "success" and "loading" on page change
});
} = usePrebuiltRulesUpgradeReview(
{
page: pagination.page,
per_page: pagination.perPage,
},
{
refetchInterval: REVIEW_PREBUILT_RULES_UPGRADE_REFRESH_INTERVAL,
keepPreviousData: true, // Use this option so that the state doesn't jump between "success" and "loading" on page change
}
);

const ruleUpgradeInfos = useMemo(() => upgradeReviewData?.rules ?? [], [upgradeReviewData]);
const tags = useMemo(() => upgradeReviewData?.stats.tags ?? [], [upgradeReviewData]);
const totalRulesToUpgrade = upgradeReviewData?.stats.num_rules_to_upgrade_total ?? 0;

const { rulesUpgradeState, setRuleFieldResolvedValue } =
usePrebuiltRulesUpgradeState(ruleUpgradeInfos);
const ruleUpgradeStates = useMemo(() => Object.values(rulesUpgradeState), [rulesUpgradeState]);
Expand Down Expand Up @@ -399,6 +418,7 @@ export const UpgradePrebuiltRulesTableContextProvider = ({
upgradeAllRules,
setFilterOptions,
openRulePreview,
setPagination,
}),
[refetch, upgradeRules, upgradeAllRules, openRulePreview]
);
Expand All @@ -416,21 +436,27 @@ export const UpgradePrebuiltRulesTableContextProvider = ({
isUpgradingSecurityPackages,
loadingRules,
lastUpdated: dataUpdatedAt,
pagination: {
...pagination,
total: totalRulesToUpgrade,
},
},
actions,
}),
[
ruleUpgradeInfos.length,
filteredRuleUpgradeStates,
isFetched,
ruleUpgradeInfos.length,
filterOptions,
tags,
isFetched,
isLoading,
areMlJobsLoading,
isRefetching,
isUpgradingSecurityPackages,
loadingRules,
dataUpdatedAt,
pagination,
totalRulesToUpgrade,
actions,
]
);
Expand Down
Loading

0 comments on commit 004b8c1

Please sign in to comment.