diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts index b62663bd787503..546116f82696b7 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { HttpStart } from 'kibana/public'; +import { Dispatch } from 'redux'; +import { CoreStart, HttpStart } from 'kibana/public'; import { EndpointAction, HostInfo, @@ -13,6 +14,7 @@ import { HostIsolationResponse, HostResultList, Immutable, + ImmutableObject, } from '../../../../../common/endpoint/types'; import { GetPolicyListResponse } from '../../policy/types'; import { ImmutableMiddlewareAPI, ImmutableMiddlewareFactory } from '../../../../common/store'; @@ -54,6 +56,7 @@ import { import { isolateHost, unIsolateHost } from '../../../../common/lib/host_isolation'; import { AppAction } from '../../../../common/store/actions'; import { resolvePathVariables } from '../../../../common/utils/resolve_path_variables'; +import { ServerReturnedEndpointPackageInfo } from './action'; type EndpointPageStore = ImmutableMiddlewareAPI; @@ -78,26 +81,14 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory, + dispatch: Dispatch, + coreStart: CoreStart +) { + if (endpointPackageInfo(state)) return; + + try { + const packageInfo = await sendGetEndpointSecurityPackage(coreStart.http); + dispatch({ + type: 'serverReturnedEndpointPackageInfo', + payload: packageInfo, + }); + } catch (error) { + // Ignore Errors, since this should not hinder the user's ability to use the UI + // eslint-disable-next-line no-console + console.error(error); + } +} diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx index 1407e5f64f156a..d01ccea5ba1f4a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx @@ -63,7 +63,7 @@ describe('Policy Details', () => { describe('when displayed with invalid id', () => { let releaseApiFailure: () => void; beforeEach(() => { - http.get.mockImplementationOnce(async () => { + http.get.mockImplementation(async () => { await new Promise((_, reject) => { releaseApiFailure = reject.bind(null, new Error('policy not found')); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx index 691601b69e3cd7..adc9438f27d743 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx @@ -633,18 +633,21 @@ describe('When on the Trusted Apps Page', () => { }); }); - it('should close the flyout', async () => { + it('should close the flyout', () => { expect(renderResult.queryByTestId('addTrustedAppFlyout')).toBeNull(); }); - it('should show success toast notification', async () => { + it('should show success toast notification', () => { expect(coreStart.notifications.toasts.addSuccess.mock.calls[0][0]).toEqual( '"one app" has been added to the Trusted Applications list.' ); }); - it('should trigger the List to reload', async () => { - expect(coreStart.http.get.mock.calls[0][0]).toEqual(TRUSTED_APPS_LIST_API); + it('should trigger the List to reload', () => { + const isCalled = coreStart.http.get.mock.calls.some( + (call) => call[0].toString() === TRUSTED_APPS_LIST_API + ); + expect(isCalled).toEqual(true); }); }); @@ -666,18 +669,18 @@ describe('When on the Trusted Apps Page', () => { }); }); - it('should continue to show the flyout', async () => { + it('should continue to show the flyout', () => { expect(renderResult.getByTestId('addTrustedAppFlyout')).not.toBeNull(); }); - it('should enable the Cancel Button', async () => { + it('should enable the Cancel Button', () => { expect( (renderResult.getByTestId('addTrustedAppFlyout-cancelButton') as HTMLButtonElement) .disabled ).toBe(false); }); - it('should show the dialog close button', async () => { + it('should show the dialog close button', () => { expect(renderResult.getByTestId('euiFlyoutCloseButton')).not.toBeNull(); }); @@ -688,7 +691,7 @@ describe('When on the Trusted Apps Page', () => { ).toBe(false); }); - it('should show API errors in the form', async () => { + it('should show API errors in the form', () => { expect(renderResult.container.querySelector('.euiForm__errors')).not.toBeNull(); }); }); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx index a6b545045a6272..f0198092ec1be8 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx @@ -9,11 +9,15 @@ import React from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; import { OverviewEmpty } from '.'; import { useIngestEnabledCheck } from '../../../common/hooks/endpoint/ingest_enabled'; + +const endpointPackageVersion = '0.19.1'; + jest.mock('../../../common/lib/kibana'); jest.mock('../../../management/pages/endpoint_hosts/view/hooks', () => ({ useIngestUrl: jest .fn() .mockReturnValue({ appId: 'ingestAppId', appPath: 'ingestPath', url: 'ingestUrl' }), + useEndpointSelector: jest.fn().mockReturnValue({ endpointPackageVersion }), })); jest.mock('../../../common/hooks/endpoint/ingest_enabled', () => ({ @@ -57,7 +61,7 @@ describe('OverviewEmpty', () => { fill: false, label: 'Add Endpoint Security', onClick: undefined, - url: '/app/home#/tutorial_directory/security', + url: `#/integrations/endpoint-${endpointPackageVersion}/add-integration`, }, }); }); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx index 6a77271e987715..028871d7be19d8 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx @@ -7,6 +7,7 @@ import React, { useMemo } from 'react'; import { omit } from 'lodash/fp'; +import { createStructuredSelector } from 'reselect'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiLink } from '@elastic/eui'; @@ -14,18 +15,33 @@ import * as i18nCommon from '../../../common/translations'; import { EmptyPage, EmptyPageActionsProps } from '../../../common/components/empty_page'; import { useKibana } from '../../../common/lib/kibana'; import { ADD_DATA_PATH } from '../../../../common/constants'; -import { useIngestUrl } from '../../../management/pages/endpoint_hosts/view/hooks'; +import { + useEndpointSelector, + useIngestUrl, +} from '../../../management/pages/endpoint_hosts/view/hooks'; import { useNavigateToAppEventHandler } from '../../../common/hooks/endpoint/use_navigate_to_app_event_handler'; import { useIngestEnabledCheck } from '../../../common/hooks/endpoint/ingest_enabled'; +import { CreateStructuredSelector } from '../../../common/store'; +import { endpointPackageVersion as useEndpointPackageVersion } from '../../../management/pages/endpoint_hosts/store/selectors'; const OverviewEmptyComponent: React.FC = () => { const { http, docLinks } = useKibana().services; const basePath = http.basePath.get(); - const { appId: ingestAppId, appPath: ingestPath, url: ingestUrl } = useIngestUrl( - 'integrations?category=security' - ); - const handleOnClick = useNavigateToAppEventHandler(ingestAppId, { path: ingestPath }); + const selector = (createStructuredSelector as CreateStructuredSelector)({ + endpointPackageVersion: useEndpointPackageVersion, + }); + const { endpointPackageVersion } = useEndpointSelector(selector); + const { url: ingestUrl } = useIngestUrl(''); + + const endpointIntegrationUrlPath = endpointPackageVersion + ? `/endpoint-${endpointPackageVersion}/add-integration` + : ''; + const endpointIntegrationUrl = `#/integrations${endpointIntegrationUrlPath}`; + const handleEndpointClick = useNavigateToAppEventHandler('fleet', { + path: endpointIntegrationUrl, + }); const { allEnabled: isIngestEnabled } = useIngestEnabledCheck(); + const emptyPageActions: EmptyPageActionsProps = useMemo( () => ({ elasticAgent: { @@ -42,13 +58,13 @@ const OverviewEmptyComponent: React.FC = () => { }, endpoint: { label: i18nCommon.EMPTY_ACTION_ENDPOINT, - url: `${basePath}${ADD_DATA_PATH}`, + url: endpointIntegrationUrl, description: i18nCommon.EMPTY_ACTION_ENDPOINT_DESCRIPTION, - onClick: handleOnClick, + onClick: handleEndpointClick, fill: false, }, }), - [basePath, ingestUrl, handleOnClick] + [basePath, ingestUrl, endpointIntegrationUrl, handleEndpointClick] ); const emptyPageIngestDisabledActions = useMemo(