[Security Solution] Invalidate prebuilt rules status after package upgrade or installation (#150292)

**Resolves: https://github.com/elastic/kibana/issues/150306**

## Summary

Fixes the Load Prebuilt rules button not visible when users visit the
rules management page for the first time (no prebuilt detection rules
package installed).

## Steps to test

1. Ensure that the detection engine package is not installed: <img
src="https://user-images.githubusercontent.com/1938181/216949857-a49731f3-535e-4813-95d3-62b2995500bb.png"
width="600" />
2. Navigate to the rules management page.

### Previously

The "Load Elastic Prebuilt Rules" button is not visible, and users
cannot install prebuilt rules.

<img
src="https://user-images.githubusercontent.com/1938181/216950430-4c57a3ad-3146-40a0-82c2-08c4fc2f65c3.png"
width="600" />

### With the fix

Users now see loading animation, indicating that the package
installation happens in the background. Once the package installation
finishes, users see the Load Prebuilt rules button appear.


https://user-images.githubusercontent.com/1938181/217585144-879fe288-0ede-4e01-b585-6aced1d89379.mov
This commit is contained in:
Dmitrii Shevchenko 2023-02-13 16:00:20 +01:00 committed by GitHub
parent 2749e95fe0
commit 4f1f2a84fd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 304 additions and 186 deletions

View file

@ -18,7 +18,6 @@ import {
getScopeFromPath,
useSourcererDataView,
} from '../../common/containers/sourcerer';
import { useUpgradeSecurityPackages } from '../../common/hooks/use_upgrade_security_packages';
import { GlobalHeader } from './global_header';
import { ConsoleManager } from '../../management/components/console/components/console_manager';
@ -26,6 +25,7 @@ import { TourContextProvider } from '../../common/components/guided_onboarding_t
import { useUrlState } from '../../common/hooks/use_url_state';
import { useUpdateBrowserTitle } from '../../common/hooks/use_update_browser_title';
import { useUpgradeSecurityPackages } from '../../detection_engine/rule_management/logic/use_upgrade_security_packages';
interface HomePageProps {
children: React.ReactNode;

View file

@ -1,113 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { useEffect } from 'react';
import type { HttpFetchOptions, HttpStart } from '@kbn/core/public';
import type { BulkInstallPackagesResponse } from '@kbn/fleet-plugin/common';
import { epmRouteService } from '@kbn/fleet-plugin/common';
import type { InstallPackageResponse } from '@kbn/fleet-plugin/common/types';
import { KibanaServices, useKibana } from '../lib/kibana';
import { useUserPrivileges } from '../components/user_privileges';
import { PREBUILT_RULES_PACKAGE_NAME } from '../../../common/detection_engine/constants';
/**
* Requests that the endpoint and security_detection_engine package be upgraded to the latest version
*
* @param http an http client for sending the request
* @param options an object containing options for the request
* @param prebuiltRulesPackageVersion specific version of the prebuilt rules package to install
*/
const sendUpgradeSecurityPackages = async (
http: HttpStart,
options: HttpFetchOptions = {},
prebuiltRulesPackageVersion?: string
): Promise<void> => {
const packages = ['endpoint', PREBUILT_RULES_PACKAGE_NAME];
const requests: Array<Promise<InstallPackageResponse | BulkInstallPackagesResponse>> = [];
// If `prebuiltRulesPackageVersion` is provided, try to install that version
// Must be done as two separate requests as bulk API doesn't support versions
if (prebuiltRulesPackageVersion != null) {
packages.splice(packages.indexOf(PREBUILT_RULES_PACKAGE_NAME), 1);
requests.push(
http.post<InstallPackageResponse>(
epmRouteService.getInstallPath(PREBUILT_RULES_PACKAGE_NAME, prebuiltRulesPackageVersion),
{
...options,
body: JSON.stringify({
force: true,
}),
}
)
);
}
// Note: if `prerelease:true` option is provided, endpoint package will also be installed as prerelease
requests.push(
http.post<BulkInstallPackagesResponse>(epmRouteService.getBulkInstallPath(), {
...options,
body: JSON.stringify({
packages,
}),
})
);
await Promise.allSettled(requests);
};
export const useUpgradeSecurityPackages = () => {
const context = useKibana();
const canAccessFleet = useUserPrivileges().endpointPrivileges.canAccessFleet;
useEffect(() => {
const abortController = new AbortController();
// cancel any ongoing requests
const abortRequests = () => {
abortController.abort();
};
if (canAccessFleet) {
const signal = abortController.signal;
(async () => {
try {
// Make sure fleet is initialized first
await context.services.fleet?.isInitialized();
// Always install the latest package if in dev env or snapshot build
const isPrerelease =
KibanaServices.getKibanaVersion().includes('-SNAPSHOT') ||
KibanaServices.getKibanaBranch() === 'main';
// ignore the response for now since we aren't notifying the user
// Note: response would be Promise.allSettled, so must iterate all responses for errors and throw manually
await sendUpgradeSecurityPackages(
context.services.http,
{
query: {
prerelease: isPrerelease,
},
signal,
},
KibanaServices.getPrebuiltRulesPackageVersion()
);
} catch (error) {
// Ignore Errors, since this should not hinder the user's ability to use the UI
// log to console, except if the error occurred due to aborting a request
if (!abortController.signal.aborted) {
// eslint-disable-next-line no-console
console.error(error);
}
}
})();
return abortRequests;
}
}, [canAccessFleet, context.services.fleet, context.services.http]);
};

View file

@ -10,6 +10,9 @@ import type {
ExceptionListItemSchema,
} from '@kbn/securitysolution-io-ts-list-types';
import type { BulkInstallPackagesResponse } from '@kbn/fleet-plugin/common';
import { epmRouteService } from '@kbn/fleet-plugin/common';
import type { InstallPackageResponse } from '@kbn/fleet-plugin/common/types';
import type { RuleManagementFiltersResponse } from '../../../../common/detection_engine/rule_management/api/rules/filters/response_schema';
import { RULE_MANAGEMENT_FILTERS_URL } from '../../../../common/detection_engine/rule_management/api/urls';
import type { BulkActionsDryRunErrCode } from '../../../../common/constants';
@ -481,3 +484,61 @@ export const addRuleExceptions = async ({
signal,
}
);
export interface InstallFleetPackageProps {
packageName: string;
packageVersion: string;
prerelease?: boolean;
force?: boolean;
}
/**
* Install a Fleet package from the registry
*
* @param packageName Name of the package to install
* @param packageVersion Version of the package to install
* @param prerelease Whether to install a prerelease version of the package
* @param force Whether to force install the package. If false, the package will only be installed if it is not already installed
*
* @returns The response from the Fleet API
*/
export const installFleetPackage = ({
packageName,
packageVersion,
prerelease = false,
force = true,
}: InstallFleetPackageProps): Promise<InstallPackageResponse> => {
return KibanaServices.get().http.post<InstallPackageResponse>(
epmRouteService.getInstallPath(packageName, packageVersion),
{
query: { prerelease },
body: JSON.stringify({ force }),
}
);
};
export interface BulkInstallFleetPackagesProps {
packages: string[];
prerelease?: boolean;
}
/**
* Install multiple Fleet packages from the registry
*
* @param packages Array of package names to install
* @param prerelease Whether to install prerelease versions of the packages
*
* @returns The response from the Fleet API
*/
export const bulkInstallFleetPackages = ({
packages,
prerelease = false,
}: BulkInstallFleetPackagesProps): Promise<BulkInstallPackagesResponse> => {
return KibanaServices.get().http.post<BulkInstallPackagesResponse>(
epmRouteService.getBulkInstallPath(),
{
query: { prerelease },
body: JSON.stringify({ packages }),
}
);
};

View file

@ -0,0 +1,44 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { EPM_API_ROUTES } from '@kbn/fleet-plugin/common';
import type { BulkInstallPackagesResponse } from '@kbn/fleet-plugin/common/types';
import type { UseMutationOptions } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import { PREBUILT_RULES_PACKAGE_NAME } from '../../../../../common/detection_engine/constants';
import type { BulkInstallFleetPackagesProps } from '../api';
import { bulkInstallFleetPackages } from '../api';
import { useInvalidateFetchPrebuiltRulesStatusQuery } from './use_fetch_prebuilt_rules_status_query';
export const BULK_INSTALL_FLEET_PACKAGES_MUTATION_KEY = [
'POST',
EPM_API_ROUTES.BULK_INSTALL_PATTERN,
];
export const useBulkInstallFleetPackagesMutation = (
options?: UseMutationOptions<BulkInstallPackagesResponse, Error, BulkInstallFleetPackagesProps>
) => {
const invalidatePrePackagedRulesStatus = useInvalidateFetchPrebuiltRulesStatusQuery();
return useMutation((props: BulkInstallFleetPackagesProps) => bulkInstallFleetPackages(props), {
...options,
mutationKey: BULK_INSTALL_FLEET_PACKAGES_MUTATION_KEY,
onSettled: (...args) => {
const response = args[0];
const rulesPackage = response?.items.find(
(item) => item.name === PREBUILT_RULES_PACKAGE_NAME
);
if (rulesPackage && 'result' in rulesPackage && rulesPackage.result.status === 'installed') {
// The rules package was installed/updated, so invalidate the pre-packaged rules status query
invalidatePrePackagedRulesStatus();
}
if (options?.onSettled) {
options.onSettled(...args);
}
},
});
};

View file

@ -0,0 +1,41 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { EPM_API_ROUTES } from '@kbn/fleet-plugin/common';
import type { InstallPackageResponse } from '@kbn/fleet-plugin/common/types';
import type { UseMutationOptions } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import { PREBUILT_RULES_PACKAGE_NAME } from '../../../../../common/detection_engine/constants';
import type { InstallFleetPackageProps } from '../api';
import { installFleetPackage } from '../api';
import { useInvalidateFetchPrebuiltRulesStatusQuery } from './use_fetch_prebuilt_rules_status_query';
export const INSTALL_FLEET_PACKAGE_MUTATION_KEY = [
'POST',
EPM_API_ROUTES.INSTALL_FROM_REGISTRY_PATTERN,
];
export const useInstallFleetPackageMutation = (
options?: UseMutationOptions<InstallPackageResponse, Error, InstallFleetPackageProps>
) => {
const invalidatePrePackagedRulesStatus = useInvalidateFetchPrebuiltRulesStatusQuery();
return useMutation((props: InstallFleetPackageProps) => installFleetPackage(props), {
...options,
mutationKey: INSTALL_FLEET_PACKAGE_MUTATION_KEY,
onSettled: (...args) => {
const { packageName } = args[2];
if (packageName === PREBUILT_RULES_PACKAGE_NAME) {
// Invalidate the pre-packaged rules status query as there might be new rules to install
invalidatePrePackagedRulesStatus();
}
if (options?.onSettled) {
options.onSettled(...args);
}
},
});
};

View file

@ -4,8 +4,12 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { useIsMutating } from '@tanstack/react-query';
import { useAppToasts } from '../../../common/hooks/use_app_toasts';
import { useCreatePrebuiltRulesMutation } from '../api/hooks/use_create_prebuilt_rules_mutation';
import {
CREATE_PREBUILT_RULES_MUTATION_KEY,
useCreatePrebuiltRulesMutation,
} from '../api/hooks/use_create_prebuilt_rules_mutation';
import * as i18n from './translations';
export const useInstallPrePackagedRules = () => {
@ -21,6 +25,11 @@ export const useInstallPrePackagedRules = () => {
});
};
export const useIsInstallingPrePackagedRules = () => {
const mutationsCount = useIsMutating(CREATE_PREBUILT_RULES_MUTATION_KEY);
return mutationsCount > 0;
};
const getSuccessToastMessage = (result: {
rules_installed: number;
rules_updated: number;

View file

@ -5,14 +5,13 @@
* 2.0.
*/
import React, { memo } from 'react';
import { KibanaServices, useKibana } from '../lib/kibana';
import type { RenderHookResult } from '@testing-library/react-hooks';
import { renderHook as _renderHook } from '@testing-library/react-hooks';
import { useUpgradeSecurityPackages } from './use_upgrade_security_packages';
import { epmRouteService } from '@kbn/fleet-plugin/common';
import { renderHook } from '@testing-library/react-hooks';
import { useKibana, KibanaServices } from '../../../common/lib/kibana';
import { TestProviders } from '../../../common/mock';
import { useUpgradeSecurityPackages } from './use_upgrade_security_packages';
jest.mock('../components/user_privileges', () => {
jest.mock('../../../common/components/user_privileges', () => {
return {
useUserPrivileges: jest.fn().mockReturnValue({
endpointPrivileges: {
@ -21,48 +20,30 @@ jest.mock('../components/user_privileges', () => {
}),
};
});
jest.mock('../lib/kibana');
jest.mock('../../../common/lib/kibana');
const mockGetPrebuiltRulesPackageVersion =
KibanaServices.getPrebuiltRulesPackageVersion as jest.Mock;
const mockGetKibanaVersion = KibanaServices.getKibanaVersion as jest.Mock;
const mockGetKibanaBranch = KibanaServices.getKibanaBranch as jest.Mock;
const useKibanaMock = useKibana as jest.MockedFunction<typeof useKibana>;
describe('When using the `useUpgradeSecurityPackages()` hook', () => {
const mockGetPrebuiltRulesPackageVersion =
KibanaServices.getPrebuiltRulesPackageVersion as jest.Mock;
const mockGetKibanaVersion = KibanaServices.getKibanaVersion as jest.Mock;
const mockGetKibanaBranch = KibanaServices.getKibanaBranch as jest.Mock;
let renderResult: RenderHookResult<object, void>;
let renderHook: () => RenderHookResult<object, void>;
let kibana: ReturnType<typeof useKibana>;
// eslint-disable-next-line react/display-name
const Wrapper = memo(({ children }) => {
kibana = useKibana();
return <>{children}</>;
});
beforeEach(() => {
renderHook = () => {
renderResult = _renderHook(() => useUpgradeSecurityPackages(), { wrapper: Wrapper });
return renderResult;
};
});
afterEach(() => {
jest.clearAllMocks();
if (renderResult) {
renderResult.unmount();
}
});
it('should call fleet setup first via `isInitialized()` and then send upgrade request', async () => {
renderHook();
const { waitFor } = renderHook(() => useUpgradeSecurityPackages(), {
wrapper: TestProviders,
});
expect(kibana.services.fleet?.isInitialized).toHaveBeenCalled();
expect(kibana.services.http.post).not.toHaveBeenCalled();
expect(useKibanaMock().services.fleet?.isInitialized).toHaveBeenCalled();
expect(useKibanaMock().services.http.post).not.toHaveBeenCalled();
await renderResult.waitFor(
() => (kibana.services.http.post as jest.Mock).mock.calls.length > 0
);
await waitFor(() => (useKibanaMock().services.http.post as jest.Mock).mock.calls.length > 0);
expect(kibana.services.http.post).toHaveBeenCalledWith(
expect(useKibanaMock().services.http.post).toHaveBeenCalledWith(
`${epmRouteService.getBulkInstallPath()}`,
expect.objectContaining({
body: '{"packages":["endpoint","security_detection_engine"]}',
@ -74,13 +55,13 @@ describe('When using the `useUpgradeSecurityPackages()` hook', () => {
mockGetKibanaVersion.mockReturnValue('8.0.0');
mockGetKibanaBranch.mockReturnValue('release');
renderHook();
const { waitFor } = renderHook(() => useUpgradeSecurityPackages(), {
wrapper: TestProviders,
});
await renderResult.waitFor(
() => (kibana.services.http.post as jest.Mock).mock.calls.length > 0
);
await waitFor(() => (useKibanaMock().services.http.post as jest.Mock).mock.calls.length > 0);
expect(kibana.services.http.post).toHaveBeenCalledWith(
expect(useKibanaMock().services.http.post).toHaveBeenCalledWith(
`${epmRouteService.getBulkInstallPath()}`,
expect.objectContaining({
body: '{"packages":["endpoint","security_detection_engine"]}',
@ -93,13 +74,13 @@ describe('When using the `useUpgradeSecurityPackages()` hook', () => {
mockGetKibanaVersion.mockReturnValue('8.0.0-SNAPSHOT');
mockGetKibanaBranch.mockReturnValue('main');
renderHook();
const { waitFor } = renderHook(() => useUpgradeSecurityPackages(), {
wrapper: TestProviders,
});
await renderResult.waitFor(
() => (kibana.services.http.post as jest.Mock).mock.calls.length > 0
);
await waitFor(() => (useKibanaMock().services.http.post as jest.Mock).mock.calls.length > 0);
expect(kibana.services.http.post).toHaveBeenCalledWith(
expect(useKibanaMock().services.http.post).toHaveBeenCalledWith(
`${epmRouteService.getBulkInstallPath()}`,
expect.objectContaining({
body: '{"packages":["endpoint","security_detection_engine"]}',
@ -112,13 +93,13 @@ describe('When using the `useUpgradeSecurityPackages()` hook', () => {
mockGetKibanaVersion.mockReturnValue('8.0.0-SNAPSHOT');
mockGetKibanaBranch.mockReturnValue('release');
renderHook();
const { waitFor } = renderHook(() => useUpgradeSecurityPackages(), {
wrapper: TestProviders,
});
await renderResult.waitFor(
() => (kibana.services.http.post as jest.Mock).mock.calls.length > 0
);
await waitFor(() => (useKibanaMock().services.http.post as jest.Mock).mock.calls.length > 0);
expect(kibana.services.http.post).toHaveBeenCalledWith(
expect(useKibanaMock().services.http.post).toHaveBeenCalledWith(
`${epmRouteService.getBulkInstallPath()}`,
expect.objectContaining({
body: '{"packages":["endpoint","security_detection_engine"]}',
@ -131,13 +112,13 @@ describe('When using the `useUpgradeSecurityPackages()` hook', () => {
mockGetKibanaVersion.mockReturnValue('8.0.0');
mockGetKibanaBranch.mockReturnValue('main');
renderHook();
const { waitFor } = renderHook(() => useUpgradeSecurityPackages(), {
wrapper: TestProviders,
});
await renderResult.waitFor(
() => (kibana.services.http.post as jest.Mock).mock.calls.length > 0
);
await waitFor(() => (useKibanaMock().services.http.post as jest.Mock).mock.calls.length > 0);
expect(kibana.services.http.post).toHaveBeenCalledWith(
expect(useKibanaMock().services.http.post).toHaveBeenCalledWith(
`${epmRouteService.getBulkInstallPath()}`,
expect.objectContaining({
body: '{"packages":["endpoint","security_detection_engine"]}',
@ -149,18 +130,18 @@ describe('When using the `useUpgradeSecurityPackages()` hook', () => {
it('should send separate upgrade requests if prebuiltRulesPackageVersion is provided', async () => {
mockGetPrebuiltRulesPackageVersion.mockReturnValue('8.2.1');
renderHook();
const { waitFor } = renderHook(() => useUpgradeSecurityPackages(), {
wrapper: TestProviders,
});
await renderResult.waitFor(
() => (kibana.services.http.post as jest.Mock).mock.calls.length > 0
);
await waitFor(() => (useKibanaMock().services.http.post as jest.Mock).mock.calls.length > 0);
expect(kibana.services.http.post).toHaveBeenNthCalledWith(
expect(useKibanaMock().services.http.post).toHaveBeenNthCalledWith(
1,
`${epmRouteService.getInstallPath('security_detection_engine', '8.2.1')}`,
expect.objectContaining({ query: { prerelease: true } })
);
expect(kibana.services.http.post).toHaveBeenNthCalledWith(
expect(useKibanaMock().services.http.post).toHaveBeenNthCalledWith(
2,
`${epmRouteService.getBulkInstallPath()}`,
expect.objectContaining({

View file

@ -0,0 +1,95 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { useIsMutating } from '@tanstack/react-query';
import { useEffect } from 'react';
import { PREBUILT_RULES_PACKAGE_NAME } from '../../../../common/detection_engine/constants';
import { useUserPrivileges } from '../../../common/components/user_privileges';
import { KibanaServices, useKibana } from '../../../common/lib/kibana';
import type { BulkInstallFleetPackagesProps, InstallFleetPackageProps } from '../api/api';
import {
BULK_INSTALL_FLEET_PACKAGES_MUTATION_KEY,
useBulkInstallFleetPackagesMutation,
} from '../api/hooks/use_bulk_install_fleet_packages_mutation';
import {
INSTALL_FLEET_PACKAGE_MUTATION_KEY,
useInstallFleetPackageMutation,
} from '../api/hooks/use_install_fleet_package_mutation';
/**
* Install or upgrade the security packages (endpoint and prebuilt rules)
*/
export const useUpgradeSecurityPackages = () => {
const context = useKibana();
const canAccessFleet = useUserPrivileges().endpointPrivileges.canAccessFleet;
const { mutate: bulkInstallFleetPackages } = useBulkInstallFleetPackagesMutation();
const { mutate: installFleetPackage } = useInstallFleetPackageMutation();
useEffect(() => {
if (!canAccessFleet) {
return;
}
(async () => {
// Make sure fleet is initialized first
await context.services.fleet?.isInitialized();
// Always install the latest package if in dev env or snapshot build
const prerelease =
KibanaServices.getKibanaVersion().includes('-SNAPSHOT') ||
KibanaServices.getKibanaBranch() === 'main';
const prebuiltRulesPackageVersion = KibanaServices.getPrebuiltRulesPackageVersion();
// ignore the response for now since we aren't notifying the user
const packages = ['endpoint', PREBUILT_RULES_PACKAGE_NAME];
// If `prebuiltRulesPackageVersion` is provided, try to install that version
// Must be done as two separate requests as bulk API doesn't support versions
if (prebuiltRulesPackageVersion != null) {
installFleetPackage({
packageName: PREBUILT_RULES_PACKAGE_NAME,
packageVersion: prebuiltRulesPackageVersion,
prerelease,
force: true,
});
packages.splice(packages.indexOf(PREBUILT_RULES_PACKAGE_NAME), 1);
}
// Note: if `prerelease:true` option is provided, endpoint package will also be installed as prerelease
bulkInstallFleetPackages({
packages,
prerelease,
});
})();
}, [bulkInstallFleetPackages, canAccessFleet, context.services.fleet, installFleetPackage]);
};
/**
* @returns true if the security packages are being installed or upgraded
*/
export const useIsUpgradingSecurityPackages = () => {
const isInstallingPackages = useIsMutating({
predicate: ({ options }) => {
const { mutationKey, variables } = options;
// The mutation is bulk Fleet packages installation. Check if the packages include the prebuilt rules package
if (mutationKey === BULK_INSTALL_FLEET_PACKAGES_MUTATION_KEY) {
return (variables as BulkInstallFleetPackagesProps).packages.includes(
PREBUILT_RULES_PACKAGE_NAME
);
}
// The mutation is single Fleet package installation. Check if the package is the prebuilt rules package
if (mutationKey === INSTALL_FLEET_PACKAGE_MUTATION_KEY) {
return (variables as InstallFleetPackageProps).packageName === PREBUILT_RULES_PACKAGE_NAME;
}
return false;
},
});
return isInstallingPackages > 0;
};

View file

@ -40,6 +40,7 @@ import { useStartMlJobs } from '../../../rule_management/logic/use_start_ml_jobs
import { RULES_TABLE_PAGE_SIZE_OPTIONS } from './constants';
import { useRuleManagementFilters } from '../../../rule_management/logic/use_rule_management_filters';
import type { FindRulesSortField } from '../../../../../common/detection_engine/rule_management';
import { useIsUpgradingSecurityPackages } from '../../../rule_management/logic/use_upgrade_security_packages';
const INITIAL_SORT_FIELD = 'enabled';
@ -63,6 +64,7 @@ const NO_ITEMS_MESSAGE = (
export const RulesTables = React.memo<RulesTableProps>(({ selectedTab }) => {
const [{ canUserCRUD }] = useUserData();
const hasPermissions = hasUserCRUDPermission(canUserCRUD);
const isUpgradingSecurityPackages = useIsUpgradingSecurityPackages();
const tableRef = useRef<EuiBasicTable>(null);
const rulesTableContext = useRulesTableContext();
@ -227,7 +229,7 @@ export const RulesTables = React.memo<RulesTableProps>(({ selectedTab }) => {
}
: { 'data-test-subj': 'monitoring-table', columns: monitoringColumns };
const shouldShowLinearProgress = isFetched && isRefetching;
const shouldShowLinearProgress = (isFetched && isRefetching) || isUpgradingSecurityPackages;
const shouldShowLoadingOverlay = (!isFetched && isRefetching) || isPreflightInProgress;
return (

View file

@ -11,6 +11,7 @@ import { useBoolState } from '../../../../common/hooks/use_bool_state';
import { RULES_TABLE_ACTIONS } from '../../../../common/lib/apm/user_actions';
import { useStartTransaction } from '../../../../common/lib/apm/use_start_transaction';
import { useCreatePrePackagedRules } from '../../../../detection_engine/rule_management/logic/use_create_pre_packaged_rules';
import { useIsInstallingPrePackagedRules } from '../../../../detection_engine/rule_management/logic/use_install_pre_packaged_rules';
import { usePrePackagedRulesStatus } from '../../../../detection_engine/rule_management/logic/use_pre_packaged_rules_status';
import { affectedJobIds } from '../../callouts/ml_job_compatibility_callout/affected_job_ids';
import { MlJobUpgradeModal } from '../../modals/ml_job_upgrade_modal';
@ -27,11 +28,8 @@ interface LoadPrePackagedRulesProps {
export const LoadPrePackagedRules = ({ children }: LoadPrePackagedRulesProps) => {
const { isFetching: isFetchingPrepackagedStatus } = usePrePackagedRulesStatus();
const {
createPrePackagedRules,
canCreatePrePackagedRules,
isLoading: loadingCreatePrePackagedRules,
} = useCreatePrePackagedRules();
const isInstallingPrebuiltRules = useIsInstallingPrePackagedRules();
const { createPrePackagedRules, canCreatePrePackagedRules } = useCreatePrePackagedRules();
const { startTransaction } = useStartTransaction();
const handleCreatePrePackagedRules = useCallback(async () => {
@ -63,7 +61,7 @@ export const LoadPrePackagedRules = ({ children }: LoadPrePackagedRulesProps) =>
return (
<>
{children({
isLoading: loadingCreatePrePackagedRules,
isLoading: isInstallingPrebuiltRules,
isDisabled,
onClick: handleInstallPrePackagedRules,
})}