mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
Create upselling package and implement EntityAnalytics serverless upselling (#164136)
UX: https://github.com/elastic/security-team/issues/7310 ## Summary * It creates an Upselling package to share the service and components between ESS and Serverless plugins * It implements upselling for entity analytics on serverless by replicating the ESS approach ESS <img width="1520" alt="Screenshot 2023-08-17 at 13 34 59" src="95c2c94e
-7ab3-4d9f-aa24-b3e9c00eb3ed"> Serverless <img width="1523" alt="Screenshot 2023-08-17 at 13 39 25" src="618ce9dc
-ef4e-469d-884a-dfb09834d0b0"> We are not displaying the upgrade button because we still don't know how to link to the cloud settings page. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
1610e32972
commit
75644797c3
52 changed files with 319 additions and 236 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -604,6 +604,7 @@ x-pack/plugins/security_solution @elastic/security-solution
|
|||
x-pack/plugins/security_solution_serverless @elastic/security-solution
|
||||
x-pack/packages/security-solution/side_nav @elastic/security-threat-hunting-explore
|
||||
x-pack/packages/security-solution/storybook/config @elastic/security-threat-hunting-explore
|
||||
x-pack/packages/security-solution/upselling @elastic/security-threat-hunting-explore
|
||||
x-pack/test/security_functional/plugins/test_endpoints @elastic/kibana-security
|
||||
packages/kbn-securitysolution-autocomplete @elastic/security-detection-engine
|
||||
x-pack/packages/security-solution/data_table @elastic/security-threat-hunting-investigations
|
||||
|
|
|
@ -608,6 +608,7 @@
|
|||
"@kbn/security-solution-serverless": "link:x-pack/plugins/security_solution_serverless",
|
||||
"@kbn/security-solution-side-nav": "link:x-pack/packages/security-solution/side_nav",
|
||||
"@kbn/security-solution-storybook-config": "link:x-pack/packages/security-solution/storybook/config",
|
||||
"@kbn/security-solution-upselling": "link:x-pack/packages/security-solution/upselling",
|
||||
"@kbn/security-test-endpoints-plugin": "link:x-pack/test/security_functional/plugins/test_endpoints",
|
||||
"@kbn/securitysolution-autocomplete": "link:packages/kbn-securitysolution-autocomplete",
|
||||
"@kbn/securitysolution-data-table": "link:x-pack/packages/security-solution/data_table",
|
||||
|
|
|
@ -1202,6 +1202,8 @@
|
|||
"@kbn/security-solution-side-nav/*": ["x-pack/packages/security-solution/side_nav/*"],
|
||||
"@kbn/security-solution-storybook-config": ["x-pack/packages/security-solution/storybook/config"],
|
||||
"@kbn/security-solution-storybook-config/*": ["x-pack/packages/security-solution/storybook/config/*"],
|
||||
"@kbn/security-solution-upselling": ["x-pack/packages/security-solution/upselling"],
|
||||
"@kbn/security-solution-upselling/*": ["x-pack/packages/security-solution/upselling/*"],
|
||||
"@kbn/security-test-endpoints-plugin": ["x-pack/test/security_functional/plugins/test_endpoints"],
|
||||
"@kbn/security-test-endpoints-plugin/*": ["x-pack/test/security_functional/plugins/test_endpoints/*"],
|
||||
"@kbn/securitysolution-autocomplete": ["packages/kbn-securitysolution-autocomplete"],
|
||||
|
|
3
x-pack/packages/security-solution/upselling/README.mdx
Normal file
3
x-pack/packages/security-solution/upselling/README.mdx
Normal file
|
@ -0,0 +1,3 @@
|
|||
## Security Solution Upselling
|
||||
|
||||
This package contains the upselling service that registers pages/component/messages and shared upselling components for ESS and Serverless plugins.
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 2.2 MiB |
12
x-pack/packages/security-solution/upselling/jest.config.js
Normal file
12
x-pack/packages/security-solution/upselling/jest.config.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../../..',
|
||||
roots: ['<rootDir>/x-pack/packages/security-solution/upselling'],
|
||||
};
|
5
x-pack/packages/security-solution/upselling/kibana.jsonc
Normal file
5
x-pack/packages/security-solution/upselling/kibana.jsonc
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"id": "@kbn/security-solution-upselling",
|
||||
"owner": "@elastic/security-threat-hunting-explore"
|
||||
}
|
|
@ -8,7 +8,7 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const UPGRADE_INVESTIGATION_GUIDE = (requiredLicense: string) =>
|
||||
i18n.translate('xpack.securitySolutionEss.markdown.insight.upsell', {
|
||||
i18n.translate('securitySolutionPackages.markdown.insight.upsell', {
|
||||
defaultMessage: 'Upgrade to {requiredLicense} to make use of insights in investigation guides',
|
||||
values: {
|
||||
requiredLicense,
|
6
x-pack/packages/security-solution/upselling/package.json
Normal file
6
x-pack/packages/security-solution/upselling/package.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "@kbn/security-solution-upselling",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "Elastic License 2.0"
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import EntityAnalyticsUpsellingComponent from './entity_analytics';
|
||||
|
||||
jest.mock('@kbn/security-solution-navigation', () => {
|
||||
const original = jest.requireActual('@kbn/security-solution-navigation');
|
||||
return {
|
||||
...original,
|
||||
useNavigation: () => ({
|
||||
navigateTo: jest.fn(),
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('EntityAnalyticsUpselling', () => {
|
||||
it('should render', () => {
|
||||
const { getByTestId } = render(
|
||||
<EntityAnalyticsUpsellingComponent requiredLicense="TEST LICENSE" />
|
||||
);
|
||||
expect(getByTestId('paywallCardDescription')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should throw exception when requiredLicense and requiredProduct are not provided', () => {
|
||||
expect(() => render(<EntityAnalyticsUpsellingComponent />)).toThrow();
|
||||
});
|
||||
|
||||
it('should show product message when requiredProduct is provided', () => {
|
||||
const { getByTestId } = render(
|
||||
<EntityAnalyticsUpsellingComponent
|
||||
requiredProduct="TEST PRODUCT"
|
||||
requiredLicense="TEST LICENSE"
|
||||
/>
|
||||
);
|
||||
|
||||
expect(getByTestId('paywallCardDescription')).toHaveTextContent(
|
||||
'Entity risk scoring capability is available in our TEST PRODUCT license tier'
|
||||
);
|
||||
});
|
||||
|
||||
it('should show product badge when requiredProduct is provided', () => {
|
||||
const { getByText } = render(
|
||||
<EntityAnalyticsUpsellingComponent
|
||||
requiredProduct="TEST PRODUCT"
|
||||
requiredLicense="TEST LICENSE"
|
||||
/>
|
||||
);
|
||||
|
||||
expect(getByText('TEST PRODUCT')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show license message when requiredLicense is provided', () => {
|
||||
const { getByTestId } = render(
|
||||
<EntityAnalyticsUpsellingComponent requiredLicense="TEST LICENSE" />
|
||||
);
|
||||
|
||||
expect(getByTestId('paywallCardDescription')).toHaveTextContent(
|
||||
'This feature is available with TEST LICENSE or higher subscription'
|
||||
);
|
||||
});
|
||||
|
||||
it('should show license badge when requiredLicense is provided', () => {
|
||||
const { getByText } = render(
|
||||
<EntityAnalyticsUpsellingComponent requiredLicense="TEST LICENSE" />
|
||||
);
|
||||
|
||||
expect(getByText('TEST LICENSE')).toBeInTheDocument();
|
||||
});
|
||||
});
|
|
@ -23,7 +23,7 @@ import styled from '@emotion/styled';
|
|||
import { useNavigation } from '@kbn/security-solution-navigation';
|
||||
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
|
||||
import * as i18n from './translations';
|
||||
import paywallPng from '../../common/images/entity_paywall.png';
|
||||
import paywallPng from '../images/entity_paywall.png';
|
||||
|
||||
const PaywallDiv = styled.div`
|
||||
max-width: 75%;
|
||||
|
@ -33,7 +33,7 @@ const PaywallDiv = styled.div`
|
|||
width: auto;
|
||||
}
|
||||
}
|
||||
.platinumCardDescription {
|
||||
.paywallCardDescription {
|
||||
padding: 0 15%;
|
||||
}
|
||||
`;
|
||||
|
@ -45,15 +45,31 @@ const StyledEuiCard = styled(EuiCard)`
|
|||
}
|
||||
`;
|
||||
|
||||
const EntityAnalyticsUpsellingComponent = () => {
|
||||
const { getAppUrl, navigateTo } = useNavigation();
|
||||
const subscriptionUrl = getAppUrl({
|
||||
appId: 'management',
|
||||
path: 'stack/license_management',
|
||||
});
|
||||
const EntityAnalyticsUpsellingComponent = ({
|
||||
requiredLicense,
|
||||
requiredProduct,
|
||||
subscriptionUrl,
|
||||
}: {
|
||||
requiredLicense?: string;
|
||||
requiredProduct?: string;
|
||||
subscriptionUrl?: string;
|
||||
}) => {
|
||||
const { navigateTo } = useNavigation();
|
||||
|
||||
const goToSubscription = useCallback(() => {
|
||||
navigateTo({ url: subscriptionUrl });
|
||||
}, [navigateTo, subscriptionUrl]);
|
||||
|
||||
if (!requiredProduct && !requiredLicense) {
|
||||
throw new Error('requiredProduct or requiredLicense must be defined');
|
||||
}
|
||||
|
||||
const upgradeMessage = requiredProduct
|
||||
? i18n.UPGRADE_PRODUCT_MESSAGE(requiredProduct)
|
||||
: i18n.UPGRADE_LICENSE_MESSAGE(requiredLicense ?? '');
|
||||
|
||||
const requiredProductOrLicense = requiredProduct ?? requiredLicense ?? '';
|
||||
|
||||
return (
|
||||
<KibanaPageTemplate restrictWidth={false} contentBorder={false} grow={true}>
|
||||
<KibanaPageTemplate.Section>
|
||||
|
@ -61,8 +77,7 @@ const EntityAnalyticsUpsellingComponent = () => {
|
|||
<EuiSpacer size="xl" />
|
||||
<PaywallDiv>
|
||||
<StyledEuiCard
|
||||
data-test-subj="platinumCard"
|
||||
betaBadgeProps={{ label: i18n.PLATINUM }}
|
||||
betaBadgeProps={{ label: requiredProductOrLicense }}
|
||||
icon={<EuiIcon size="xl" type="lock" />}
|
||||
display="subdued"
|
||||
title={
|
||||
|
@ -73,26 +88,33 @@ const EntityAnalyticsUpsellingComponent = () => {
|
|||
description={false}
|
||||
paddingSize="xl"
|
||||
>
|
||||
<EuiFlexGroup className="platinumCardDescription" direction="column" gutterSize="none">
|
||||
<EuiFlexGroup
|
||||
data-test-subj="paywallCardDescription"
|
||||
className="paywallCardDescription"
|
||||
direction="column"
|
||||
gutterSize="none"
|
||||
>
|
||||
<EuiText>
|
||||
<EuiFlexItem>
|
||||
<p>
|
||||
<EuiTextColor color="subdued">{i18n.UPGRADE_MESSAGE}</EuiTextColor>
|
||||
<EuiTextColor color="subdued">{upgradeMessage}</EuiTextColor>
|
||||
</p>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<div>
|
||||
<EuiButton onClick={goToSubscription} fill>
|
||||
{i18n.UPGRADE_BUTTON}
|
||||
</EuiButton>
|
||||
</div>
|
||||
{subscriptionUrl && (
|
||||
<div>
|
||||
<EuiButton onClick={goToSubscription} fill>
|
||||
{i18n.UPGRADE_BUTTON(requiredProductOrLicense)}
|
||||
</EuiButton>
|
||||
</div>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiText>
|
||||
</EuiFlexGroup>
|
||||
</StyledEuiCard>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiImage alt={i18n.UPGRADE_MESSAGE} src={paywallPng} size="fullWidth" />
|
||||
<EuiImage alt={upgradeMessage} src={paywallPng} size="fullWidth" />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</PaywallDiv>
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
|
||||
export const UPGRADE_LICENSE_MESSAGE = (requiredLicense: string) =>
|
||||
i18n.translate('securitySolutionPackages.entityAnalytics.paywall.upgradeLicenseMessage', {
|
||||
defaultMessage: 'This feature is available with {requiredLicense} or higher subscription',
|
||||
values: {
|
||||
requiredLicense,
|
||||
},
|
||||
});
|
||||
|
||||
export const UPGRADE_PRODUCT_MESSAGE = (requiredProduct: string) =>
|
||||
i18n.translate('securitySolutionPackages.entityAnalytics.paywall.upgradeProductMessage', {
|
||||
defaultMessage:
|
||||
'Entity risk scoring capability is available in our {requiredProduct} license tier',
|
||||
values: {
|
||||
requiredProduct,
|
||||
},
|
||||
});
|
||||
|
||||
export const UPGRADE_BUTTON = (requiredLicenseOrProduct: string) =>
|
||||
i18n.translate('securitySolutionPackages.entityAnalytics.paywall.upgradeButton', {
|
||||
defaultMessage: 'Upgrade to {requiredLicenseOrProduct}',
|
||||
values: {
|
||||
requiredLicenseOrProduct,
|
||||
},
|
||||
});
|
||||
|
||||
export const ENTITY_ANALYTICS_LICENSE_DESC = i18n.translate(
|
||||
'securitySolutionPackages.entityAnalytics.pageDesc',
|
||||
{
|
||||
defaultMessage: 'Detect threats from users and hosts within your network with Entity Analytics',
|
||||
}
|
||||
);
|
||||
|
||||
export const ENTITY_ANALYTICS_TITLE = i18n.translate(
|
||||
'securitySolutionPackages.entityAnalytics.navigation',
|
||||
{
|
||||
defaultMessage: 'Entity Analytics',
|
||||
}
|
||||
);
|
|
@ -5,4 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
export { UpsellingService } from './upselling_service';
|
||||
export type { PageUpsellings, SectionUpsellings, UpsellingSectionId } from './types';
|
||||
export type {
|
||||
PageUpsellings,
|
||||
SectionUpsellings,
|
||||
UpsellingSectionId,
|
||||
UpsellingMessageId,
|
||||
} from './types';
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { SecurityPageName } from '../../../../common';
|
||||
import type { SecurityPageName } from '@kbn/security-solution-navigation';
|
||||
|
||||
export type PageUpsellings = Partial<Record<SecurityPageName, React.ComponentType>>;
|
||||
export type MessageUpsellings = Partial<Record<UpsellingMessageId, string>>;
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { SecurityPageName } from '../../../../common';
|
||||
import { SecurityPageName } from '@kbn/security-solution-navigation';
|
||||
import { UpsellingService } from './upselling_service';
|
||||
|
||||
const TestComponent = () => <div>{'TEST component'}</div>;
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import type { Observable } from 'rxjs';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import type { SecurityPageName } from '../../../../common';
|
||||
import type { SecurityPageName } from '@kbn/security-solution-navigation';
|
||||
import type {
|
||||
SectionUpsellings,
|
||||
PageUpsellings,
|
27
x-pack/packages/security-solution/upselling/tsconfig.json
Normal file
27
x-pack/packages/security-solution/upselling/tsconfig.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"extends": "../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"react",
|
||||
"@emotion/react/types/css-prop",
|
||||
"@testing-library/jest-dom",
|
||||
"@testing-library/react",
|
||||
"@kbn/ambient-ui-types"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/i18n",
|
||||
"@kbn/security-solution-navigation",
|
||||
"@kbn/shared-ux-page-kibana-template",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
]
|
||||
}
|
|
@ -11,7 +11,7 @@ import type { AppLinkItems } from '../../links';
|
|||
import { updateAppLinks } from '../../links';
|
||||
import { mockGlobalState } from '../../mock';
|
||||
import type { Capabilities } from '@kbn/core-capabilities-common';
|
||||
import { UpsellingService } from '../../lib/upsellings';
|
||||
import { UpsellingService } from '@kbn/security-solution-upselling/service';
|
||||
|
||||
const defaultAppLinks: AppLinkItems = [
|
||||
{
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import React, { memo, useContext } from 'react';
|
||||
import type { UpsellingService } from '../../..';
|
||||
import type { UpsellingService } from '@kbn/security-solution-upselling/service';
|
||||
|
||||
export const UpsellingProviderContext = React.createContext<UpsellingService | null>(null);
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import React from 'react';
|
||||
import { SecurityPageName } from '../../../common';
|
||||
import { UpsellingService } from '../lib/upsellings';
|
||||
import { UpsellingService } from '@kbn/security-solution-upselling/service';
|
||||
import { useUpsellingComponent, useUpsellingMessage, useUpsellingPage } from './use_upselling';
|
||||
import { UpsellingProvider } from '../components/upselling_provider';
|
||||
|
||||
|
|
|
@ -8,10 +8,12 @@
|
|||
import { useMemo } from 'react';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import type React from 'react';
|
||||
import type {
|
||||
UpsellingSectionId,
|
||||
UpsellingMessageId,
|
||||
} from '@kbn/security-solution-upselling/service';
|
||||
import { useUpsellingService } from '../components/upselling_provider';
|
||||
import type { UpsellingSectionId } from '../lib/upsellings';
|
||||
import type { SecurityPageName } from '../../../common';
|
||||
import type { UpsellingMessageId } from '../lib/upsellings/types';
|
||||
|
||||
export const useUpsellingComponent = (id: UpsellingSectionId): React.ComponentType | null => {
|
||||
const upselling = useUpsellingService();
|
||||
|
|
|
@ -48,7 +48,7 @@ import { cloudExperimentsMock } from '@kbn/cloud-experiments-plugin/common/mocks
|
|||
import { guidedOnboardingMock } from '@kbn/guided-onboarding-plugin/public/mocks';
|
||||
import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks';
|
||||
import { of } from 'rxjs';
|
||||
import { UpsellingService } from '../upsellings';
|
||||
import { UpsellingService } from '@kbn/security-solution-upselling/service';
|
||||
import { cloudMock } from '@kbn/cloud-plugin/public/mocks';
|
||||
import { NavigationProvider } from '@kbn/security-solution-navigation';
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import {
|
|||
} from './links';
|
||||
import { createCapabilities } from './test_utils';
|
||||
import { hasCapabilities } from '../lib/capabilities';
|
||||
import { UpsellingService } from '../lib/upsellings';
|
||||
import { UpsellingService } from '@kbn/security-solution-upselling/service';
|
||||
import React from 'react';
|
||||
|
||||
const defaultAppLinks: AppLinkItems = [
|
||||
|
|
|
@ -14,8 +14,8 @@ import type {
|
|||
LinkCategory as GenericLinkCategory,
|
||||
LinkCategories as GenericLinkCategories,
|
||||
} from '@kbn/security-solution-navigation';
|
||||
import type { UpsellingService } from '@kbn/security-solution-upselling/service';
|
||||
import type { ExperimentalFeatures } from '../../../common/experimental_features';
|
||||
import type { UpsellingService } from '../lib/upsellings';
|
||||
import type { RequiredCapabilities } from '../lib/capabilities';
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { renderHook, act } from '@testing-library/react-hooks';
|
||||
import { allowedExperimentalValues } from '../../../../common/experimental_features';
|
||||
import { UpsellingService } from '../../lib/upsellings';
|
||||
import { UpsellingService } from '@kbn/security-solution-upselling/service';
|
||||
import { updateAppLinks } from '../../links';
|
||||
import { links } from '../../links/app_links';
|
||||
import { useShowTimeline } from './use_show_timeline';
|
||||
|
|
|
@ -87,14 +87,13 @@ const RiskDetailsTabBodyComponent: React.FC<
|
|||
[entityName, riskEntity]
|
||||
);
|
||||
|
||||
const { data, loading, refetch, inspect, isDeprecated, isModuleEnabled, isAuthorized } =
|
||||
useRiskScore({
|
||||
filterQuery,
|
||||
onlyLatest: false,
|
||||
riskEntity,
|
||||
skip: !overTimeToggleStatus && !contributorsToggleStatus,
|
||||
timerange,
|
||||
});
|
||||
const { data, loading, refetch, inspect, isDeprecated, isModuleEnabled } = useRiskScore({
|
||||
filterQuery,
|
||||
onlyLatest: false,
|
||||
riskEntity,
|
||||
skip: !overTimeToggleStatus && !contributorsToggleStatus,
|
||||
timerange,
|
||||
});
|
||||
|
||||
const { data: riskScoreEngineStatus } = useRiskEngineStatus();
|
||||
|
||||
|
@ -136,10 +135,6 @@ const RiskDetailsTabBodyComponent: React.FC<
|
|||
isDeprecated: isDeprecated && !loading,
|
||||
};
|
||||
|
||||
if (!isAuthorized) {
|
||||
return <>{'TODO: Add RiskScore Upsell'}</>;
|
||||
}
|
||||
|
||||
if (riskScoreEngineStatus?.isUpdateAvailable) {
|
||||
return <RiskScoreUpdatePanel />;
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ import { LandingPageComponent } from '../../../../common/components/landing_page
|
|||
import { AlertCountByRuleByStatus } from '../../../../common/components/alert_count_by_status';
|
||||
import { useLicense } from '../../../../common/hooks/use_license';
|
||||
import { ResponderActionButton } from '../../../../detections/components/endpoint_responder/responder_action_button';
|
||||
import { useHasSecurityCapability } from '../../../../helper_hooks';
|
||||
|
||||
const ES_HOST_FIELD = 'host.name';
|
||||
const HostOverviewManage = manageQuery(HostOverview);
|
||||
|
@ -152,7 +153,7 @@ const HostDetailsComponent: React.FC<HostDetailsProps> = ({ detailName, hostDeta
|
|||
dispatch(setHostDetailsTablesActivePageToZero());
|
||||
}, [dispatch, detailName]);
|
||||
|
||||
const isPlatinumOrTrialLicense = useMlCapabilities().isPlatinumOrTrialLicense;
|
||||
const hasEntityAnalyticsCapability = useHasSecurityCapability('entity-analytics');
|
||||
|
||||
const { hasKibanaREAD, hasIndexRead } = useAlertsPrivileges();
|
||||
const canReadAlerts = hasKibanaREAD && hasIndexRead;
|
||||
|
@ -252,7 +253,7 @@ const HostDetailsComponent: React.FC<HostDetailsProps> = ({ detailName, hostDeta
|
|||
<TabNavigation
|
||||
navTabs={navTabsHostDetails({
|
||||
hasMlUserPermissions: hasMlUserPermissions(capabilities),
|
||||
isRiskyHostsEnabled: isPlatinumOrTrialLicense,
|
||||
isRiskyHostsEnabled: hasEntityAnalyticsCapability,
|
||||
hostName: detailName,
|
||||
isEnterprise: isEnterprisePlus,
|
||||
})}
|
||||
|
|
|
@ -56,6 +56,7 @@ import { ID } from '../containers/hosts';
|
|||
import { LandingPageComponent } from '../../../common/components/landing_page';
|
||||
import { fieldNameExistsFilter } from '../../../common/components/visualization_actions/utils';
|
||||
import { useLicense } from '../../../common/hooks/use_license';
|
||||
import { useHasSecurityCapability } from '../../../helper_hooks';
|
||||
|
||||
/**
|
||||
* Need a 100% height here to account for the graph/analyze tool, which sets no explicit height parameters, but fills the available space.
|
||||
|
@ -156,6 +157,7 @@ const HostsComponent = () => {
|
|||
});
|
||||
|
||||
const isEnterprisePlus = useLicense().isEnterprise();
|
||||
const hasEntityAnalyticsCapability = useHasSecurityCapability('entity-analytics');
|
||||
|
||||
const onSkipFocusBeforeEventsTable = useCallback(() => {
|
||||
containerElement.current
|
||||
|
@ -215,7 +217,7 @@ const HostsComponent = () => {
|
|||
<TabNavigation
|
||||
navTabs={navTabsHosts({
|
||||
hasMlUserPermissions: hasMlUserPermissions(capabilities),
|
||||
isRiskyHostsEnabled: capabilities.isPlatinumOrTrialLicense,
|
||||
isRiskyHostsEnabled: hasEntityAnalyticsCapability,
|
||||
isEnterprise: isEnterprisePlus,
|
||||
})}
|
||||
/>
|
||||
|
|
|
@ -74,7 +74,6 @@ export const HostRiskScoreQueryTabBody = ({
|
|||
isModuleEnabled,
|
||||
loading,
|
||||
refetch,
|
||||
isAuthorized,
|
||||
totalCount,
|
||||
} = useRiskScore({
|
||||
filterQuery,
|
||||
|
@ -96,10 +95,6 @@ export const HostRiskScoreQueryTabBody = ({
|
|||
isDeprecated: isDeprecated && !loading,
|
||||
};
|
||||
|
||||
if (!isAuthorized) {
|
||||
return <>{'TODO: Add RiskScore Upsell'}</>;
|
||||
}
|
||||
|
||||
if (riskScoreEngineStatus?.isUpdateAvailable) {
|
||||
return <RiskScoreUpdatePanel />;
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ import { UsersType } from '../../store/model';
|
|||
import { hasMlUserPermissions } from '../../../../../common/machine_learning/has_ml_user_permissions';
|
||||
import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities';
|
||||
import { LandingPageComponent } from '../../../../common/components/landing_page';
|
||||
import { useHasSecurityCapability } from '../../../../helper_hooks';
|
||||
|
||||
const QUERY_ID = 'UsersDetailsQueryId';
|
||||
const ES_USER_FIELD = 'user.name';
|
||||
|
@ -73,7 +74,7 @@ const UsersDetailsComponent: React.FC<UsersDetailsProps> = ({
|
|||
usersDetailsPagePath,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const isPlatinumOrTrialLicense = useMlCapabilities().isPlatinumOrTrialLicense;
|
||||
const hasEntityAnalyticsCapability = useHasSecurityCapability('entity-analytics');
|
||||
const getTable = useMemo(() => dataTableSelectors.getTableByIdSelector(), []);
|
||||
const graphEventId = useShallowEqualSelector(
|
||||
(state) => (getTable(state, TableId.hostsPageEvents) ?? timelineDefaults).graphEventId
|
||||
|
@ -241,7 +242,7 @@ const UsersDetailsComponent: React.FC<UsersDetailsProps> = ({
|
|||
navTabs={navTabsUsersDetails(
|
||||
detailName,
|
||||
hasMlUserPermissions(capabilities),
|
||||
isPlatinumOrTrialLicense
|
||||
hasEntityAnalyticsCapability
|
||||
)}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
|
|
|
@ -75,7 +75,6 @@ export const UserRiskScoreQueryTabBody = ({
|
|||
loading,
|
||||
refetch,
|
||||
totalCount,
|
||||
isAuthorized,
|
||||
} = useRiskScore({
|
||||
filterQuery,
|
||||
pagination,
|
||||
|
@ -96,10 +95,6 @@ export const UserRiskScoreQueryTabBody = ({
|
|||
isDeprecated: isDeprecated && !loading,
|
||||
};
|
||||
|
||||
if (!isAuthorized) {
|
||||
return <>{'TODO: Add RiskScore Upsell'}</>;
|
||||
}
|
||||
|
||||
if (riskScoreEngineStatus?.isUpdateAvailable) {
|
||||
return <RiskScoreUpdatePanel />;
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ import { hasMlUserPermissions } from '../../../../common/machine_learning/has_ml
|
|||
import { useMlCapabilities } from '../../../common/components/ml/hooks/use_ml_capabilities';
|
||||
import { LandingPageComponent } from '../../../common/components/landing_page';
|
||||
import { userNameExistsFilter } from './details/helpers';
|
||||
import { useHasSecurityCapability } from '../../../helper_hooks';
|
||||
|
||||
const ID = 'UsersQueryId';
|
||||
|
||||
|
@ -175,10 +176,10 @@ const UsersComponent = () => {
|
|||
);
|
||||
|
||||
const capabilities = useMlCapabilities();
|
||||
const isPlatinumOrTrialLicense = useMlCapabilities().isPlatinumOrTrialLicense;
|
||||
const hasEntityAnalyticsCapability = useHasSecurityCapability('entity-analytics');
|
||||
const navTabs = useMemo(
|
||||
() => navTabsUsers(hasMlUserPermissions(capabilities), isPlatinumOrTrialLicense),
|
||||
[capabilities, isPlatinumOrTrialLicense]
|
||||
() => navTabsUsers(hasMlUserPermissions(capabilities), hasEntityAnalyticsCapability),
|
||||
[capabilities, hasEntityAnalyticsCapability]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -12,13 +12,6 @@ import type { PluginSetup, PluginStart } from './types';
|
|||
export type { TimelineModel } from './timelines/store/timeline/model';
|
||||
export type { LinkItem } from './common/links';
|
||||
|
||||
export type {
|
||||
UpsellingService,
|
||||
PageUpsellings,
|
||||
SectionUpsellings,
|
||||
UpsellingSectionId,
|
||||
} from './common/lib/upsellings';
|
||||
|
||||
export const plugin = (context: PluginInitializerContext): Plugin => new Plugin(context);
|
||||
|
||||
export type { PluginSetup, PluginStart };
|
||||
|
|
|
@ -10,6 +10,7 @@ import React, { memo } from 'react';
|
|||
import { Provider as ReduxStoreProvider } from 'react-redux';
|
||||
import type { Store } from 'redux';
|
||||
import { NavigationProvider } from '@kbn/security-solution-navigation';
|
||||
import type { UpsellingService } from '@kbn/security-solution-upselling/service';
|
||||
import { UpsellingProvider } from '../../../../../../../common/components/upselling_provider';
|
||||
import { UserPrivilegesProvider } from '../../../../../../../common/components/user_privileges/user_privileges_context';
|
||||
import type { SecuritySolutionQueryClient } from '../../../../../../../common/containers/query_client/query_client_provider';
|
||||
|
@ -18,7 +19,6 @@ import { SecuritySolutionStartDependenciesContext } from '../../../../../../../c
|
|||
import { CurrentLicense } from '../../../../../../../common/components/current_license';
|
||||
import type { StartPlugins } from '../../../../../../../types';
|
||||
import { useKibana } from '../../../../../../../common/lib/kibana';
|
||||
import type { UpsellingService } from '../../../../../../..';
|
||||
|
||||
export type RenderContextProvidersProps = PropsWithChildren<{
|
||||
store: Store;
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
*/
|
||||
|
||||
import type { CoreStart } from '@kbn/core-lifecycle-browser';
|
||||
import type { UpsellingService } from '@kbn/security-solution-upselling/service';
|
||||
import type { StartPlugins } from '../../../../../types';
|
||||
import type { UpsellingService } from '../../../../..';
|
||||
|
||||
export interface FleetUiExtensionGetterOptions {
|
||||
coreStart: CoreStart;
|
||||
|
|
|
@ -12,7 +12,7 @@ import { createAppRootMockRenderer } from '../../../../../common/mock/endpoint';
|
|||
import type { PolicySettingsFormProps } from './policy_settings_form';
|
||||
import { PolicySettingsForm } from './policy_settings_form';
|
||||
import { FleetPackagePolicyGenerator } from '../../../../../../common/endpoint/data_generators/fleet_package_policy_generator';
|
||||
import type { UpsellingService } from '../../../../../common/lib/upsellings';
|
||||
import type { UpsellingService } from '@kbn/security-solution-upselling/service';
|
||||
|
||||
jest.mock('../../../../../common/hooks/use_license');
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
*/
|
||||
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { UpsellingService } from '@kbn/security-solution-upselling/service';
|
||||
import type { BreadcrumbsNav } from './common/breadcrumbs';
|
||||
import type { NavigationLink } from './common/links/types';
|
||||
import { UpsellingService } from './common/lib/upsellings';
|
||||
import type { PluginStart, PluginSetup } from './types';
|
||||
|
||||
const setupMock = (): PluginSetup => ({
|
||||
|
|
|
@ -88,7 +88,7 @@ export const entityAnalyticsLinks: LinkItem = {
|
|||
'Entity analytics, anomalies, and threats to narrow down the monitoring surface area.',
|
||||
}),
|
||||
path: ENTITY_ANALYTICS_PATH,
|
||||
capabilities: [`${SERVER_APP_ID}.show`],
|
||||
capabilities: [`${SERVER_APP_ID}.entity-analytics`],
|
||||
isBeta: false,
|
||||
licenseType: 'platinum',
|
||||
globalSearchKeywords: [ENTITY_ANALYTICS],
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import type { RouteProps } from 'react-router-dom';
|
||||
import { UpsellingService } from './common/lib/upsellings';
|
||||
import { UpsellingService } from '@kbn/security-solution-upselling/service';
|
||||
import type { ContractStartServices, PluginSetup, PluginStart } from './types';
|
||||
import type { AppLinksSwitcher } from './common/links';
|
||||
import { navLinks$ } from './common/links/nav_links';
|
||||
|
|
|
@ -54,6 +54,7 @@ import type { RouteProps } from 'react-router-dom';
|
|||
import type { DiscoverStart } from '@kbn/discover-plugin/public';
|
||||
import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public';
|
||||
import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public';
|
||||
import type { UpsellingService } from '@kbn/security-solution-upselling/service';
|
||||
import type { ResolverPluginSetup } from './resolver/types';
|
||||
import type { Inspect } from '../common/search_strategy';
|
||||
import type { Detections } from './detections';
|
||||
|
@ -74,7 +75,6 @@ import type { EntityAnalytics } from './entity_analytics';
|
|||
|
||||
import type { TelemetryClientStart } from './common/lib/telemetry';
|
||||
import type { Dashboards } from './dashboards';
|
||||
import type { UpsellingService } from './common/lib/upsellings';
|
||||
import type { BreadcrumbsNav } from './common/breadcrumbs/types';
|
||||
import type { TopValuesPopoverService } from './app/components/top_values_popover/top_values_popover_service';
|
||||
|
||||
|
|
|
@ -163,6 +163,7 @@
|
|||
"@kbn/cloud-chat-plugin",
|
||||
"@kbn/alerts-ui-shared",
|
||||
"@kbn/security-solution-navigation",
|
||||
"@kbn/security-solution-upselling",
|
||||
"@kbn/discover-plugin",
|
||||
"@kbn/data-view-editor-plugin",
|
||||
"@kbn/navigation-plugin",
|
||||
|
|
|
@ -1,34 +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 { i18n } from '@kbn/i18n';
|
||||
|
||||
export const PLATINUM = i18n.translate('xpack.securitySolutionEss.paywall.platinum', {
|
||||
defaultMessage: 'Platinum',
|
||||
});
|
||||
|
||||
export const UPGRADE_MESSAGE = i18n.translate('xpack.securitySolutionEss.paywall.upgradeMessage', {
|
||||
defaultMessage: 'This feature is available with Platinum or higher subscription',
|
||||
});
|
||||
|
||||
export const UPGRADE_BUTTON = i18n.translate('xpack.securitySolutionEss.paywall.upgradeButton', {
|
||||
defaultMessage: 'Upgrade to Platinum',
|
||||
});
|
||||
|
||||
export const ENTITY_ANALYTICS_LICENSE_DESC = i18n.translate(
|
||||
'xpack.securitySolutionEss.entityAnalytics.pageDesc',
|
||||
{
|
||||
defaultMessage: 'Detect threats from users and hosts within your network with Entity Analytics',
|
||||
}
|
||||
);
|
||||
|
||||
export const ENTITY_ANALYTICS_TITLE = i18n.translate(
|
||||
'xpack.securitySolutionEss.navigation.entityAnalytics',
|
||||
{
|
||||
defaultMessage: 'Entity Analytics',
|
||||
}
|
||||
);
|
|
@ -13,14 +13,15 @@ import type {
|
|||
SectionUpsellings,
|
||||
UpsellingMessageId,
|
||||
UpsellingSectionId,
|
||||
} from '@kbn/security-solution-plugin/public/common/lib/upsellings/types';
|
||||
} from '@kbn/security-solution-upselling/service/types';
|
||||
import type { ILicense, LicenseType } from '@kbn/licensing-plugin/public';
|
||||
import { lazy } from 'react';
|
||||
import type React from 'react';
|
||||
import { UPGRADE_INVESTIGATION_GUIDE } from './messages/investigation_guide_upselling';
|
||||
import React, { lazy } from 'react';
|
||||
import { UPGRADE_INVESTIGATION_GUIDE } from '@kbn/security-solution-upselling/messages';
|
||||
import type { Services } from '../common/services';
|
||||
import { withServicesProvider } from '../common/services';
|
||||
const EntityAnalyticsUpsellingLazy = lazy(() => import('./pages/entity_analytics_upselling'));
|
||||
const EntityAnalyticsUpsellingLazy = lazy(
|
||||
() => import('@kbn/security-solution-upselling/pages/entity_analytics')
|
||||
);
|
||||
|
||||
interface UpsellingsConfig {
|
||||
minimumLicenseRequired: LicenseType;
|
||||
|
@ -42,7 +43,7 @@ export const registerUpsellings = (
|
|||
license: ILicense,
|
||||
services: Services
|
||||
) => {
|
||||
const upsellingPagesToRegister = upsellingPages.reduce<PageUpsellings>(
|
||||
const upsellingPagesToRegister = upsellingPages(services).reduce<PageUpsellings>(
|
||||
(pageUpsellings, { pageName, minimumLicenseRequired, component }) => {
|
||||
if (!license.hasAtLeast(minimumLicenseRequired)) {
|
||||
pageUpsellings[pageName] = withServicesProvider(component, services);
|
||||
|
@ -78,12 +79,19 @@ export const registerUpsellings = (
|
|||
};
|
||||
|
||||
// Upsellings for entire pages, linked to a SecurityPageName
|
||||
export const upsellingPages: UpsellingPages = [
|
||||
export const upsellingPages: (services: Services) => UpsellingPages = (services) => [
|
||||
// It is highly advisable to make use of lazy loaded components to minimize bundle size.
|
||||
{
|
||||
pageName: SecurityPageName.entityAnalytics,
|
||||
minimumLicenseRequired: 'platinum',
|
||||
component: EntityAnalyticsUpsellingLazy,
|
||||
component: () => (
|
||||
<EntityAnalyticsUpsellingLazy
|
||||
requiredLicense="Platinum"
|
||||
subscriptionUrl={services.application.getUrlForApp('management', {
|
||||
path: 'stack/license_management',
|
||||
})}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -97,6 +105,6 @@ export const upsellingMessages: UpsellingMessages = [
|
|||
{
|
||||
id: 'investigation_guide',
|
||||
minimumLicenseRequired: 'platinum',
|
||||
message: UPGRADE_INVESTIGATION_GUIDE('platinum'),
|
||||
message: UPGRADE_INVESTIGATION_GUIDE('Platinum'),
|
||||
},
|
||||
];
|
||||
|
|
|
@ -21,6 +21,6 @@
|
|||
"@kbn/kibana-react-plugin",
|
||||
"@kbn/security-solution-navigation",
|
||||
"@kbn/licensing-plugin",
|
||||
"@kbn/shared-ux-page-kibana-template",
|
||||
"@kbn/security-solution-upselling",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -54,7 +54,8 @@ export class SecuritySolutionServerlessPlugin
|
|||
|
||||
const services = createServices(core, startDeps);
|
||||
|
||||
registerUpsellings(securitySolution.getUpselling(), this.config.productTypes);
|
||||
registerUpsellings(securitySolution.getUpselling(), this.config.productTypes, services);
|
||||
|
||||
securitySolution.setGetStartedPage(getSecurityGetStartedComponent(services, productTypes));
|
||||
|
||||
configureNavigation(services, this.config);
|
||||
|
|
|
@ -26,3 +26,7 @@ export const ThreatIntelligencePaywallLazy = withSuspenseUpsell(
|
|||
export const OsqueryResponseActionsUpsellingSectionLazy = withSuspenseUpsell(
|
||||
lazy(() => import('./pages/osquery_automated_response_actions'))
|
||||
);
|
||||
|
||||
export const EntityAnalyticsUpsellingLazy = withSuspenseUpsell(
|
||||
lazy(() => import('@kbn/security-solution-upselling/pages/entity_analytics'))
|
||||
);
|
||||
|
|
|
@ -1,24 +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 type { AppFeatureKey } from '@kbn/security-solution-plugin/common';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { getProductTypeByPLI } from '../hooks/use_product_type_by_pli';
|
||||
|
||||
export const UPGRADE_INVESTIGATION_GUIDE = (productTypeRequired: string) =>
|
||||
i18n.translate('xpack.securitySolutionServerless.markdown.insight.upsell', {
|
||||
defaultMessage:
|
||||
'Upgrade to {productTypeRequired} to make use of insights in investigation guides',
|
||||
values: {
|
||||
productTypeRequired,
|
||||
},
|
||||
});
|
||||
|
||||
export const investigationGuideUpselling = (requiredPLI: AppFeatureKey): string => {
|
||||
const productTypeRequired = getProductTypeByPLI(requiredPLI);
|
||||
return productTypeRequired ? UPGRADE_INVESTIGATION_GUIDE(productTypeRequired) : '';
|
||||
};
|
|
@ -1,43 +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 React from 'react';
|
||||
import { EuiEmptyPrompt, EuiLink } from '@elastic/eui';
|
||||
import type { AppFeatureKey } from '@kbn/security-solution-plugin/common';
|
||||
import { getProductTypeByPLI } from '../hooks/use_product_type_by_pli';
|
||||
|
||||
export const GenericUpsellingPage: React.FC<{ requiredPLI: AppFeatureKey }> = React.memo(
|
||||
function GenericUpsellingPage({ requiredPLI }) {
|
||||
const productTypeRequired = getProductTypeByPLI(requiredPLI);
|
||||
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
iconType="logoSecurity"
|
||||
title={<>{'This is a testing component for a Serverless upselling prompt.'}</>}
|
||||
body={
|
||||
<>
|
||||
{'Get'} <EuiLink href="#">{productTypeRequired}</EuiLink> {'to enable this feature'}
|
||||
<br />
|
||||
<br />
|
||||
<iframe
|
||||
title="money"
|
||||
src="https://giphy.com/embed/px8O7NANzzaqk"
|
||||
width="480"
|
||||
height="283"
|
||||
frameBorder="0"
|
||||
className="giphy-embed"
|
||||
allowFullScreen
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export { GenericUpsellingPage as default };
|
|
@ -14,16 +14,20 @@ import type {
|
|||
import type {
|
||||
MessageUpsellings,
|
||||
UpsellingMessageId,
|
||||
} from '@kbn/security-solution-plugin/public/common/lib/upsellings/types';
|
||||
} from '@kbn/security-solution-upselling/service/types';
|
||||
import React from 'react';
|
||||
import { UPGRADE_INVESTIGATION_GUIDE } from '@kbn/security-solution-upselling/messages';
|
||||
import { EndpointPolicyProtectionsLazy } from './sections/endpoint_management';
|
||||
import type { SecurityProductTypes } from '../../common/config';
|
||||
import { getProductAppFeatures } from '../../common/pli/pli_features';
|
||||
import { investigationGuideUpselling } from './messages/investigation_guide_upselling';
|
||||
import {
|
||||
OsqueryResponseActionsUpsellingSectionLazy,
|
||||
ThreatIntelligencePaywallLazy,
|
||||
EntityAnalyticsUpsellingLazy,
|
||||
} from './lazy_upselling';
|
||||
import { getProductTypeByPLI } from './hooks/use_product_type_by_pli';
|
||||
import type { Services } from '../common/services';
|
||||
import { withServicesProvider } from '../common/services';
|
||||
|
||||
interface UpsellingsConfig {
|
||||
pli: AppFeatureKey;
|
||||
|
@ -42,14 +46,15 @@ type UpsellingMessages = UpsellingsMessageConfig[];
|
|||
|
||||
export const registerUpsellings = (
|
||||
upselling: UpsellingService,
|
||||
productTypes: SecurityProductTypes
|
||||
productTypes: SecurityProductTypes,
|
||||
services: Services
|
||||
) => {
|
||||
const enabledPLIsSet = new Set(getProductAppFeatures(productTypes));
|
||||
|
||||
const upsellingPagesToRegister = upsellingPages.reduce<PageUpsellings>(
|
||||
(pageUpsellings, { pageName, pli, component }) => {
|
||||
if (!enabledPLIsSet.has(pli)) {
|
||||
pageUpsellings[pageName] = component;
|
||||
pageUpsellings[pageName] = withServicesProvider(component, services);
|
||||
}
|
||||
return pageUpsellings;
|
||||
},
|
||||
|
@ -84,6 +89,15 @@ export const registerUpsellings = (
|
|||
// Upsellings for entire pages, linked to a SecurityPageName
|
||||
export const upsellingPages: UpsellingPages = [
|
||||
// It is highly advisable to make use of lazy loaded components to minimize bundle size.
|
||||
{
|
||||
pageName: SecurityPageName.entityAnalytics,
|
||||
pli: AppFeatureKey.advancedInsights,
|
||||
component: () => (
|
||||
<EntityAnalyticsUpsellingLazy
|
||||
requiredProduct={getProductTypeByPLI(AppFeatureKey.advancedInsights) ?? undefined}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
pageName: SecurityPageName.threatIntelligence,
|
||||
pli: AppFeatureKey.threatIntelligence,
|
||||
|
@ -117,6 +131,8 @@ export const upsellingMessages: UpsellingMessages = [
|
|||
{
|
||||
id: 'investigation_guide',
|
||||
pli: AppFeatureKey.investigationGuide,
|
||||
message: investigationGuideUpselling(AppFeatureKey.investigationGuide),
|
||||
message: UPGRADE_INVESTIGATION_GUIDE(
|
||||
getProductTypeByPLI(AppFeatureKey.investigationGuide) ?? ''
|
||||
),
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,43 +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 React from 'react';
|
||||
import { EuiEmptyPrompt, EuiLink } from '@elastic/eui';
|
||||
import type { AppFeatureKey } from '@kbn/security-solution-plugin/common';
|
||||
import { getProductTypeByPLI } from '../hooks/use_product_type_by_pli';
|
||||
|
||||
export const GenericUpsellingSection: React.FC<{ requiredPLI: AppFeatureKey }> = React.memo(
|
||||
function GenericUpsellingSection({ requiredPLI }) {
|
||||
const productTypeRequired = getProductTypeByPLI(requiredPLI);
|
||||
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
iconType="logoSecurity"
|
||||
title={<>{'This is a testing component for a Serverless upselling prompt.'}</>}
|
||||
body={
|
||||
<>
|
||||
{'Get'} <EuiLink href="#">{productTypeRequired}</EuiLink> {'to enable this feature'}
|
||||
<br />
|
||||
<br />
|
||||
<iframe
|
||||
title="money"
|
||||
src="https://giphy.com/embed/px8O7NANzzaqk"
|
||||
width="480"
|
||||
height="283"
|
||||
frameBorder="0"
|
||||
className="giphy-embed"
|
||||
allowFullScreen
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export { GenericUpsellingSection as default };
|
|
@ -10,6 +10,8 @@
|
|||
"public/**/*.tsx",
|
||||
"server/**/*.ts",
|
||||
"../../../typings/**/*"
|
||||
,
|
||||
"../../packages/security-solution/upselling/sections/generic_upselling_section.tsx"
|
||||
],
|
||||
"exclude": ["target/**/*"],
|
||||
"kbn_references": [
|
||||
|
@ -23,6 +25,7 @@
|
|||
"@kbn/shared-ux-page-solution-nav",
|
||||
"@kbn/security-solution-side-nav",
|
||||
"@kbn/security-solution-navigation",
|
||||
"@kbn/security-solution-upselling",
|
||||
"@kbn/default-nav-ml",
|
||||
"@kbn/default-nav-devtools",
|
||||
"@kbn/kibana-react-plugin",
|
||||
|
|
|
@ -5310,6 +5310,10 @@
|
|||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/security-solution-upselling@link:x-pack/packages/security-solution/upselling":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/security-test-endpoints-plugin@link:x-pack/test/security_functional/plugins/test_endpoints":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue