mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
# Backport This will backport the following commits from `main` to `8.11`: - [[SecuritySolution] Remove `@kbn/subscription-tracking` (#171801)](https://github.com/elastic/kibana/pull/171801) <!--- Backport version: 8.9.8 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Jan Monschke","email":"jan.monschke@elastic.co"},"sourceCommit":{"committedDate":"2023-11-23T15:39:23Z","message":"[SecuritySolution] Remove `@kbn/subscription-tracking` (#171801)\n\n## Summary\r\n\r\nThe package data isn't needed anymore, so we can remove that package. On\r\ntop, it seems like the package was causing some issues.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"df16cd9c2cfc08bc999f436a11913df50e71ce19","branchLabelMapping":{"^v8.12.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["bug","release_note:skip","Team:Threat Hunting:Investigations","backport:prev-minor","v8.12.0","v8.11.2"],"number":171801,"url":"https://github.com/elastic/kibana/pull/171801","mergeCommit":{"message":"[SecuritySolution] Remove `@kbn/subscription-tracking` (#171801)\n\n## Summary\r\n\r\nThe package data isn't needed anymore, so we can remove that package. On\r\ntop, it seems like the package was causing some issues.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"df16cd9c2cfc08bc999f436a11913df50e71ce19"}},"sourceBranch":"main","suggestedTargetBranches":["8.11"],"targetPullRequestStates":[{"branch":"main","label":"v8.12.0","labelRegex":"^v8.12.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/171801","number":171801,"mergeCommit":{"message":"[SecuritySolution] Remove `@kbn/subscription-tracking` (#171801)\n\n## Summary\r\n\r\nThe package data isn't needed anymore, so we can remove that package. On\r\ntop, it seems like the package was causing some issues.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"df16cd9c2cfc08bc999f436a11913df50e71ce19"}},{"branch":"8.11","label":"v8.11.2","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
5559c9b84c
commit
350d069b00
36 changed files with 96 additions and 780 deletions
|
@ -733,7 +733,6 @@
|
|||
"@kbn/status-plugin-a-plugin": "link:test/server_integration/plugins/status_plugin_a",
|
||||
"@kbn/status-plugin-b-plugin": "link:test/server_integration/plugins/status_plugin_b",
|
||||
"@kbn/std": "link:packages/kbn-std",
|
||||
"@kbn/subscription-tracking": "link:packages/kbn-subscription-tracking",
|
||||
"@kbn/synthetics-plugin": "link:x-pack/plugins/synthetics",
|
||||
"@kbn/task-manager-fixture-plugin": "link:x-pack/test/alerting_api_integration/common/plugins/task_manager_fixture",
|
||||
"@kbn/task-manager-performance-plugin": "link:x-pack/test/plugin_api_perf/plugins/task_manager_performance",
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
# @kbn/subscription-tracking
|
||||
|
||||
This package leverages the `@kbn/analytics-client` package to send dedicated subscription tracking events.
|
||||
|
||||
It provides a set of React components that automatically track `impression` and `click` events. Consumers of those components need to specify a `subscription context` that gives more details on the type of feature that is advertised and the location of the upsell.
|
||||
|
||||
```typescript
|
||||
import { SubscriptionLink } from '@kbn/subscription-tracking';
|
||||
import type { SubscriptionContext } from '@kbn/subscription-tracking';
|
||||
|
||||
const subscriptionContext: SubscriptionContext = {
|
||||
feature: 'threat-intelligence',
|
||||
source: 'security__threat-intelligence',
|
||||
};
|
||||
|
||||
export const Paywall = () => {
|
||||
return (
|
||||
<div>
|
||||
<SubscriptionLink subscriptionContext={subscriptionContext}>
|
||||
Upgrade to Platinum to get this feature
|
||||
</SubscriptionLink>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
The example above uses a `SubscriptionLink` which is a wrapper of `EuiLink` . So it behaves just like a normal link. Alternatively, upsells can also use a `SubscriptionButton` or `SubscriptionButtonEmpty` which wrap `EuiButton` and `EuiButtonEmpty` respectively.
|
||||
|
||||
When the link is mounted, it will send off an `impression` event with the given `subscriptionContext`. That piece of metadata consists of an identifier of the advertised feature (in this case `threat-intelligence`) and the `source` (aka location) of the impression (in this case the `threat-intelligence` page in the `security` solution). `source` follows the following format: `{solution-identifier}__location-identifier`.
|
||||
|
||||
There are no special rules for how to name these identifiers but it's good practise to make sure that `feature` has the same value for all upsells advertising the same feature (e.g. use enums for features to prevent spelling mistakes).
|
||||
|
||||
Upon interaction with the upsell link/button, a special `click` event is sent, which, again, contains the same subscription context.
|
||||
|
||||
If you want to use the `subscription-tracking` elements in your app, you have to set up a `SubscriptionTrackingProvider` in your plugin setup and register the tracking events on startup. Have a look at https://github.com/elastic/kibana/pull/143910 for an example of an integration.
|
|
@ -1,17 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export * from './src/subscription_elements';
|
||||
|
||||
export {
|
||||
SubscriptionTrackingContext,
|
||||
SubscriptionTrackingProvider,
|
||||
registerEvents,
|
||||
} from './src/services';
|
||||
|
||||
export * from './types';
|
|
@ -1,13 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../..',
|
||||
roots: ['<rootDir>/packages/kbn-subscription-tracking'],
|
||||
};
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"id": "@kbn/subscription-tracking",
|
||||
"owner": "@elastic/security-threat-hunting-investigations"
|
||||
}
|
|
@ -1,28 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import { analyticsClientMock } from '@kbn/analytics-client/src/mocks';
|
||||
|
||||
import { SubscriptionTrackingProvider } from './src/services';
|
||||
|
||||
const analyticsClientMockInst = analyticsClientMock.create();
|
||||
|
||||
/**
|
||||
* Mock for the external services provider. Only use in tests!
|
||||
*/
|
||||
export const MockSubscriptionTrackingProvider: FC = ({ children }) => {
|
||||
return (
|
||||
<SubscriptionTrackingProvider
|
||||
navigateToApp={jest.fn()}
|
||||
analyticsClient={analyticsClientMockInst}
|
||||
>
|
||||
{children}
|
||||
</SubscriptionTrackingProvider>
|
||||
);
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"name": "@kbn/subscription-tracking",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "SSPL-1.0 OR Elastic License 2.0"
|
||||
}
|
|
@ -1,45 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { isValidContext } from './helpers';
|
||||
|
||||
describe('tracking', () => {
|
||||
describe('isValidLocation', () => {
|
||||
it('identifies correct contexts', () => {
|
||||
expect(
|
||||
isValidContext({
|
||||
feature: 'test',
|
||||
source: 'security__test',
|
||||
})
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('identifies incorrect contexts', () => {
|
||||
expect(
|
||||
isValidContext({
|
||||
feature: '',
|
||||
source: 'security__',
|
||||
})
|
||||
).toBeFalsy();
|
||||
|
||||
expect(
|
||||
isValidContext({
|
||||
feature: 'test',
|
||||
source: 'security__',
|
||||
})
|
||||
).toBeFalsy();
|
||||
|
||||
expect(
|
||||
isValidContext({
|
||||
feature: '',
|
||||
source: 'security__test',
|
||||
})
|
||||
).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,14 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { SubscriptionContextData } from '../types';
|
||||
|
||||
const sourceStringRegEx = /^(\w[\w\-_]*)__(\w[\w\-_]*)$/;
|
||||
export function isValidContext(context: SubscriptionContextData): boolean {
|
||||
return context.feature.length > 0 && sourceStringRegEx.test(context.source);
|
||||
}
|
|
@ -1,70 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { FC, useContext } from 'react';
|
||||
import type { AnalyticsClient, EventTypeOpts } from '@kbn/analytics-client';
|
||||
import { EVENT_NAMES, Services, SubscriptionContextData } from '../types';
|
||||
|
||||
export const SubscriptionTrackingContext = React.createContext<Services | null>(null);
|
||||
|
||||
/**
|
||||
* External services provider
|
||||
*/
|
||||
export const SubscriptionTrackingProvider: FC<Services> = ({ children, ...services }) => {
|
||||
return (
|
||||
<SubscriptionTrackingContext.Provider value={services}>
|
||||
{children}
|
||||
</SubscriptionTrackingContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* React hook for accessing pre-wired services.
|
||||
*/
|
||||
export function useServices() {
|
||||
const context = useContext(SubscriptionTrackingContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
'SubscriptionTrackingContext is missing. Ensure your component or React root is wrapped with SubscriptionTrackingProvider.'
|
||||
);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
const subscriptionContextSchema: EventTypeOpts<SubscriptionContextData>['schema'] = {
|
||||
source: {
|
||||
type: 'keyword',
|
||||
_meta: {
|
||||
description:
|
||||
'A human-readable identifier describing the location of the beginning of the subscription flow',
|
||||
},
|
||||
},
|
||||
feature: {
|
||||
type: 'keyword',
|
||||
_meta: {
|
||||
description: 'A human-readable identifier describing the feature that is being promoted',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers the subscription-specific event types
|
||||
*/
|
||||
export function registerEvents(analyticsClient: Pick<AnalyticsClient, 'registerEventType'>) {
|
||||
analyticsClient.registerEventType<SubscriptionContextData>({
|
||||
eventType: EVENT_NAMES.IMPRESSION,
|
||||
schema: subscriptionContextSchema,
|
||||
});
|
||||
|
||||
analyticsClient.registerEventType<SubscriptionContextData>({
|
||||
eventType: EVENT_NAMES.CLICK,
|
||||
schema: subscriptionContextSchema,
|
||||
});
|
||||
}
|
|
@ -1,108 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import {
|
||||
SubscriptionLink,
|
||||
SubscriptionButton,
|
||||
SubscriptionButtonEmpty,
|
||||
} from './subscription_elements';
|
||||
import { SubscriptionTrackingProvider } from './services';
|
||||
import { EVENT_NAMES, Services, SubscriptionContextData } from '../types';
|
||||
import { coolDownTimeMs, resetCoolDown } from './use_impression';
|
||||
|
||||
const testServices: Services = {
|
||||
navigateToApp: jest.fn(),
|
||||
analyticsClient: {
|
||||
reportEvent: jest.fn(),
|
||||
registerEventType: jest.fn(),
|
||||
} as any,
|
||||
};
|
||||
const testContext: SubscriptionContextData = { feature: 'test', source: 'security__test' };
|
||||
|
||||
const WithProviders: React.FC = ({ children }) => (
|
||||
<SubscriptionTrackingProvider
|
||||
analyticsClient={testServices.analyticsClient}
|
||||
navigateToApp={testServices.navigateToApp}
|
||||
>
|
||||
{children}
|
||||
</SubscriptionTrackingProvider>
|
||||
);
|
||||
|
||||
const renderWithProviders = (children: React.ReactElement) =>
|
||||
render(children, { wrapper: WithProviders });
|
||||
|
||||
const reset = () => {
|
||||
jest.resetAllMocks();
|
||||
resetCoolDown();
|
||||
};
|
||||
|
||||
describe('SubscriptionElements', () => {
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
[SubscriptionButton, SubscriptionLink, SubscriptionButtonEmpty].forEach((SubscriptionElement) => {
|
||||
describe(SubscriptionElement.name, () => {
|
||||
beforeEach(reset);
|
||||
|
||||
it('renders the children correctly', () => {
|
||||
renderWithProviders(
|
||||
<SubscriptionElement subscriptionContext={testContext}>Hello</SubscriptionElement>
|
||||
);
|
||||
expect(screen.getByText('Hello')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('fires an impression event when rendered', () => {
|
||||
renderWithProviders(<SubscriptionElement subscriptionContext={testContext} />);
|
||||
expect(testServices.analyticsClient.reportEvent).toHaveBeenCalledWith(
|
||||
EVENT_NAMES.IMPRESSION,
|
||||
testContext
|
||||
);
|
||||
});
|
||||
|
||||
it('fires an impression event when rendered (but only once)', () => {
|
||||
const { unmount } = renderWithProviders(
|
||||
<SubscriptionElement subscriptionContext={testContext} />
|
||||
);
|
||||
expect(testServices.analyticsClient.reportEvent).toHaveBeenCalledTimes(1);
|
||||
unmount();
|
||||
|
||||
// does not create an impression again when remounted
|
||||
const { unmount: unmountAgain } = renderWithProviders(
|
||||
<SubscriptionElement subscriptionContext={testContext} />
|
||||
);
|
||||
unmountAgain();
|
||||
expect(testServices.analyticsClient.reportEvent).toHaveBeenCalledTimes(1);
|
||||
|
||||
// only creates anew impression when the cooldown time has passed
|
||||
jest.setSystemTime(Date.now() + coolDownTimeMs);
|
||||
renderWithProviders(<SubscriptionElement subscriptionContext={testContext} />);
|
||||
expect(testServices.analyticsClient.reportEvent).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('tracks a click when clicked and navigates to page', () => {
|
||||
renderWithProviders(
|
||||
<SubscriptionElement subscriptionContext={testContext}>hello</SubscriptionElement>
|
||||
);
|
||||
|
||||
screen.getByText('hello').click();
|
||||
expect(testServices.analyticsClient.reportEvent).toHaveBeenCalledWith(
|
||||
EVENT_NAMES.CLICK,
|
||||
testContext
|
||||
);
|
||||
expect(testServices.navigateToApp).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,79 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiLink, EuiButton, EuiButtonEmpty } from '@elastic/eui';
|
||||
import type { EuiLinkProps, EuiButtonEmptyProps, EuiButtonProps } from '@elastic/eui';
|
||||
import { useGoToSubscription } from './use_go_to_subscription';
|
||||
import { useImpression } from './use_impression';
|
||||
import type { SubscriptionContextData } from '../types';
|
||||
|
||||
interface CommonProps {
|
||||
/** The context information for this subscription element */
|
||||
subscriptionContext: SubscriptionContextData;
|
||||
}
|
||||
|
||||
export type SubscriptionLinkProps = EuiLinkProps & CommonProps;
|
||||
|
||||
/**
|
||||
* Wrapper around `EuiLink` that provides subscription events
|
||||
*/
|
||||
export function SubscriptionLink({
|
||||
subscriptionContext,
|
||||
children,
|
||||
...restProps
|
||||
}: SubscriptionLinkProps) {
|
||||
const goToSubscription = useGoToSubscription({ subscriptionContext });
|
||||
useImpression(subscriptionContext);
|
||||
|
||||
return (
|
||||
<EuiLink {...restProps} onClick={goToSubscription}>
|
||||
{children}
|
||||
</EuiLink>
|
||||
);
|
||||
}
|
||||
|
||||
export type SubscriptionButtonProps = EuiButtonProps & CommonProps;
|
||||
|
||||
/**
|
||||
* Wrapper around `EuiButton` that provides subscription events
|
||||
*/
|
||||
export function SubscriptionButton({
|
||||
subscriptionContext,
|
||||
children,
|
||||
...restProps
|
||||
}: SubscriptionButtonProps) {
|
||||
const goToSubscription = useGoToSubscription({ subscriptionContext });
|
||||
useImpression(subscriptionContext);
|
||||
|
||||
return (
|
||||
<EuiButton {...restProps} onClick={goToSubscription}>
|
||||
{children}
|
||||
</EuiButton>
|
||||
);
|
||||
}
|
||||
|
||||
export type SubscriptionButtonEmptyProps = EuiButtonEmptyProps & CommonProps;
|
||||
|
||||
/**
|
||||
* Wrapper around `EuiButtonEmpty` that provides subscription events
|
||||
*/
|
||||
export function SubscriptionButtonEmpty({
|
||||
subscriptionContext,
|
||||
children,
|
||||
...restProps
|
||||
}: SubscriptionButtonEmptyProps) {
|
||||
const goToSubscription = useGoToSubscription({ subscriptionContext });
|
||||
useImpression(subscriptionContext);
|
||||
|
||||
return (
|
||||
<EuiButtonEmpty {...restProps} onClick={goToSubscription}>
|
||||
{children}
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
}
|
|
@ -1,35 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import { useCallback } from 'react';
|
||||
import { isValidContext } from './helpers';
|
||||
import { useServices } from './services';
|
||||
import { EVENT_NAMES, SubscriptionContextData } from '../types';
|
||||
|
||||
interface Options {
|
||||
subscriptionContext: SubscriptionContextData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a navigation function that navigates to the subscription
|
||||
* management page. When the function executes, a click event with the
|
||||
* given context is emitted.
|
||||
*/
|
||||
export const useGoToSubscription = ({ subscriptionContext }: Options) => {
|
||||
const { navigateToApp, analyticsClient } = useServices();
|
||||
const goToSubscription = useCallback(() => {
|
||||
if (isValidContext(subscriptionContext)) {
|
||||
analyticsClient.reportEvent(EVENT_NAMES.CLICK, subscriptionContext);
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('The provided subscription context is invalid', subscriptionContext);
|
||||
}
|
||||
navigateToApp('management', { path: 'stack/license_management' });
|
||||
}, [analyticsClient, navigateToApp, subscriptionContext]);
|
||||
|
||||
return goToSubscription;
|
||||
};
|
|
@ -1,57 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { isValidContext } from './helpers';
|
||||
import { useServices } from './services';
|
||||
import { EVENT_NAMES, SubscriptionContextData } from '../types';
|
||||
|
||||
/**
|
||||
* Sends an impression event with the given context.
|
||||
*
|
||||
* Note: impression events are throttled and will not fire more
|
||||
* often than once every 30 seconds.
|
||||
*/
|
||||
export const useImpression = (context: SubscriptionContextData) => {
|
||||
const { analyticsClient } = useServices();
|
||||
|
||||
useEffect(() => {
|
||||
if (!isValidContext(context)) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('The provided subscription context is invalid', context);
|
||||
return;
|
||||
}
|
||||
if (!isCoolingDown(context)) {
|
||||
analyticsClient.reportEvent(EVENT_NAMES.IMPRESSION, context);
|
||||
coolDown(context);
|
||||
}
|
||||
}, [analyticsClient, context]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Impressions from the same context should not fire more than once every 30 seconds.
|
||||
* This prevents logging too many impressions in case a page is reloaded often or
|
||||
* if the user is navigating back and forth rapidly.
|
||||
*/
|
||||
export const coolDownTimeMs = 30 * 1000;
|
||||
let impressionCooldown = new WeakMap<SubscriptionContextData, number>();
|
||||
|
||||
function isCoolingDown(context: SubscriptionContextData) {
|
||||
const previousLog = impressionCooldown.get(context);
|
||||
|
||||
// we logged before and we are in the cooldown period
|
||||
return previousLog && Date.now() - previousLog < coolDownTimeMs;
|
||||
}
|
||||
|
||||
function coolDown(context: SubscriptionContextData) {
|
||||
impressionCooldown.set(context, Date.now());
|
||||
}
|
||||
|
||||
export function resetCoolDown() {
|
||||
impressionCooldown = new WeakMap<SubscriptionContextData, number>();
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": ["jest", "node", "react"]
|
||||
},
|
||||
"include": ["**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["target/**/*"],
|
||||
"kbn_references": ["@kbn/analytics-client"]
|
||||
}
|
|
@ -1,47 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import type { AnalyticsClient } from '@kbn/analytics-client';
|
||||
|
||||
enum SolutionIdentifier {
|
||||
observability = 'observability',
|
||||
security = 'security',
|
||||
}
|
||||
type LocationString = string;
|
||||
type SourceIdentifier = `${SolutionIdentifier}__${LocationString}`;
|
||||
/**
|
||||
* A piece of metadata which consists of an identifier of the advertised feature and
|
||||
* the `source` (e.g. location) of the subscription element.
|
||||
*/
|
||||
export interface SubscriptionContextData {
|
||||
/**
|
||||
* A human-readable identifier describing the location of the beginning of the
|
||||
* subscription flow.
|
||||
* Location identifiers are prefixed with a solution identifier, e.g. `security__`
|
||||
*
|
||||
* @example "security__host-overview" - the user is looking at an upsell button
|
||||
* on the host overview page in the security solution
|
||||
*/
|
||||
source: SourceIdentifier;
|
||||
|
||||
/**
|
||||
* A human-readable identifier describing the feature that is being promoted.
|
||||
*
|
||||
* @example "alerts-by-process-ancestry"
|
||||
*/
|
||||
feature: string;
|
||||
}
|
||||
|
||||
export interface Services {
|
||||
navigateToApp: (app: string, options: { path: string }) => void;
|
||||
analyticsClient: Pick<AnalyticsClient, 'reportEvent'>;
|
||||
}
|
||||
|
||||
export enum EVENT_NAMES {
|
||||
CLICK = 'subscription__upsell__click',
|
||||
IMPRESSION = 'subscription__upsell__impression',
|
||||
}
|
|
@ -1462,8 +1462,6 @@
|
|||
"@kbn/stdio-dev-helpers/*": ["packages/kbn-stdio-dev-helpers/*"],
|
||||
"@kbn/storybook": ["packages/kbn-storybook"],
|
||||
"@kbn/storybook/*": ["packages/kbn-storybook/*"],
|
||||
"@kbn/subscription-tracking": ["packages/kbn-subscription-tracking"],
|
||||
"@kbn/subscription-tracking/*": ["packages/kbn-subscription-tracking/*"],
|
||||
"@kbn/synthetics-plugin": ["x-pack/plugins/synthetics"],
|
||||
"@kbn/synthetics-plugin/*": ["x-pack/plugins/synthetics/*"],
|
||||
"@kbn/task-manager-fixture-plugin": ["x-pack/test/alerting_api_integration/common/plugins/task_manager_fixture"],
|
||||
|
@ -1638,9 +1636,7 @@
|
|||
"@kbn/yarn-lock-validator/*": ["packages/kbn-yarn-lock-validator/*"],
|
||||
// END AUTOMATED PACKAGE LISTING
|
||||
// Allows for importing from `kibana` package for the exported types.
|
||||
"@emotion/core": [
|
||||
"typings/@emotion"
|
||||
],
|
||||
"@emotion/core": ["typings/@emotion"]
|
||||
},
|
||||
// Support .tsx files and transform JSX into calls to React.createElement
|
||||
"jsx": "react",
|
||||
|
|
|
@ -6,15 +6,8 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiEmptyPrompt, EuiPageSection } from '@elastic/eui';
|
||||
import { EuiEmptyPrompt, EuiLink, EuiPageSection } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { SubscriptionLink } from '@kbn/subscription-tracking';
|
||||
import type { SubscriptionContextData } from '@kbn/subscription-tracking';
|
||||
|
||||
const subscriptionContext: SubscriptionContextData = {
|
||||
feature: 'cloud-security-posture',
|
||||
source: 'security__cloud-security-posture',
|
||||
};
|
||||
|
||||
export const SubscriptionNotAllowed = ({
|
||||
licenseManagementLocator,
|
||||
|
@ -41,12 +34,12 @@ export const SubscriptionNotAllowed = ({
|
|||
defaultMessage="To use these cloud security features, you must {link}."
|
||||
values={{
|
||||
link: (
|
||||
<SubscriptionLink subscriptionContext={subscriptionContext}>
|
||||
<EuiLink href={licenseManagementLocator}>
|
||||
<FormattedMessage
|
||||
id="xpack.csp.subscriptionNotAllowed.promptLinkText"
|
||||
defaultMessage="start a trial or upgrade your subscription"
|
||||
/>
|
||||
</SubscriptionLink>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -11,7 +11,6 @@ import { I18nProvider } from '@kbn/i18n-react';
|
|||
// eslint-disable-next-line no-restricted-imports
|
||||
import { Router } from 'react-router-dom';
|
||||
import { Route, Routes } from '@kbn/shared-ux-router';
|
||||
import { MockSubscriptionTrackingProvider } from '@kbn/subscription-tracking/mocks';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { coreMock } from '@kbn/core/public/mocks';
|
||||
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
|
||||
|
@ -52,11 +51,9 @@ export const TestProvider: React.FC<Partial<CspAppDeps>> = ({
|
|||
<QueryClientProvider client={queryClient}>
|
||||
<Router history={params.history}>
|
||||
<I18nProvider>
|
||||
<MockSubscriptionTrackingProvider>
|
||||
<Routes>
|
||||
<Route path="*" render={() => <>{children}</>} />
|
||||
</Routes>
|
||||
</MockSubscriptionTrackingProvider>
|
||||
<Routes>
|
||||
<Route path="*" render={() => <>{children}</>} />
|
||||
</Routes>
|
||||
</I18nProvider>
|
||||
</Router>
|
||||
</QueryClientProvider>
|
||||
|
|
|
@ -50,7 +50,6 @@
|
|||
"@kbn/share-plugin",
|
||||
"@kbn/core-http-server",
|
||||
"@kbn/core-http-browser",
|
||||
"@kbn/subscription-tracking",
|
||||
"@kbn/discover-utils",
|
||||
"@kbn/unified-data-table",
|
||||
"@kbn/cell-actions",
|
||||
|
|
|
@ -16,7 +16,6 @@ import { License } from '../common/license';
|
|||
import { licenseMock } from '../common/licensing.mock';
|
||||
import { coreMock } from '@kbn/core/public/mocks';
|
||||
import { HttpInterceptor } from '@kbn/core/public';
|
||||
import type { AnalyticsServiceSetup } from '@kbn/core-analytics-browser';
|
||||
|
||||
const coreStart = coreMock.createStart();
|
||||
describe('licensing plugin', () => {
|
||||
|
@ -443,69 +442,5 @@ describe('licensing plugin', () => {
|
|||
|
||||
expect(removeInterceptorMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('registers the subscription upsell events', async () => {
|
||||
const sessionStorage = coreMock.createStorage();
|
||||
plugin = new LicensingPlugin(coreMock.createPluginInitializerContext(), sessionStorage);
|
||||
|
||||
const coreSetup = coreMock.createSetup();
|
||||
|
||||
await plugin.setup(coreSetup);
|
||||
await plugin.stop();
|
||||
|
||||
expect(findRegisteredEventTypeByName('subscription__upsell__click', coreSetup.analytics))
|
||||
.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"eventType": "subscription__upsell__click",
|
||||
"schema": Object {
|
||||
"feature": Object {
|
||||
"_meta": Object {
|
||||
"description": "A human-readable identifier describing the feature that is being promoted",
|
||||
},
|
||||
"type": "keyword",
|
||||
},
|
||||
"source": Object {
|
||||
"_meta": Object {
|
||||
"description": "A human-readable identifier describing the location of the beginning of the subscription flow",
|
||||
},
|
||||
"type": "keyword",
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
expect(findRegisteredEventTypeByName('subscription__upsell__impression', coreSetup.analytics))
|
||||
.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"eventType": "subscription__upsell__impression",
|
||||
"schema": Object {
|
||||
"feature": Object {
|
||||
"_meta": Object {
|
||||
"description": "A human-readable identifier describing the feature that is being promoted",
|
||||
},
|
||||
"type": "keyword",
|
||||
},
|
||||
"source": Object {
|
||||
"_meta": Object {
|
||||
"description": "A human-readable identifier describing the location of the beginning of the subscription flow",
|
||||
},
|
||||
"type": "keyword",
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function findRegisteredEventTypeByName(
|
||||
eventTypeName: string,
|
||||
analyticsClientMock: jest.Mocked<AnalyticsServiceSetup>
|
||||
) {
|
||||
return analyticsClientMock.registerEventType.mock.calls.find(
|
||||
([{ eventType }]) => eventType === eventTypeName
|
||||
)!;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import { Observable, Subject, Subscription } from 'rxjs';
|
||||
|
||||
import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public';
|
||||
import { registerEvents as registerSubscriptionTrackingEvents } from '@kbn/subscription-tracking';
|
||||
import { ILicense } from '../common/types';
|
||||
import { LicensingPluginSetup, LicensingPluginStart } from './types';
|
||||
import { createLicenseUpdate } from '../common/license_update';
|
||||
|
@ -85,7 +84,6 @@ export class LicensingPlugin implements Plugin<LicensingPluginSetup, LicensingPl
|
|||
);
|
||||
|
||||
registerAnalyticsContextProvider(core.analytics, license$);
|
||||
registerSubscriptionTrackingEvents(core.analytics);
|
||||
|
||||
this.internalSubscription = license$.subscribe((license) => {
|
||||
if (license.isAvailable) {
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
"@kbn/std",
|
||||
"@kbn/i18n",
|
||||
"@kbn/analytics-client",
|
||||
"@kbn/subscription-tracking",
|
||||
"@kbn/core-analytics-browser"
|
||||
],
|
||||
"exclude": ["target/**/*"]
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
import React from 'react';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
import { SubscriptionTrackingProvider } from '@kbn/subscription-tracking';
|
||||
import { SecurityApp } from './app';
|
||||
import type { RenderAppProps } from './types';
|
||||
import { AppRoutes } from './app_routes';
|
||||
|
@ -21,7 +20,6 @@ export const renderApp = ({
|
|||
usageCollection,
|
||||
subPluginRoutes,
|
||||
theme$,
|
||||
subscriptionTrackingServices,
|
||||
}: RenderAppProps): (() => void) => {
|
||||
const ApplicationUsageTrackingProvider =
|
||||
usageCollection?.components.ApplicationUsageTrackingProvider ?? React.Fragment;
|
||||
|
@ -34,12 +32,7 @@ export const renderApp = ({
|
|||
theme$={theme$}
|
||||
>
|
||||
<ApplicationUsageTrackingProvider>
|
||||
<SubscriptionTrackingProvider
|
||||
analyticsClient={subscriptionTrackingServices.analyticsClient}
|
||||
navigateToApp={subscriptionTrackingServices.navigateToApp}
|
||||
>
|
||||
<AppRoutes subPluginRoutes={subPluginRoutes} services={services} />
|
||||
</SubscriptionTrackingProvider>
|
||||
<AppRoutes subPluginRoutes={subPluginRoutes} services={services} />
|
||||
</ApplicationUsageTrackingProvider>
|
||||
</SecurityApp>,
|
||||
element
|
||||
|
|
|
@ -19,7 +19,6 @@ import type { RouteProps } from 'react-router-dom';
|
|||
import type { AppMountParameters } from '@kbn/core/public';
|
||||
import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
|
||||
import type { TableState } from '@kbn/securitysolution-data-table';
|
||||
import type { Services as SubscriptionTrackingServices } from '@kbn/subscription-tracking';
|
||||
import type { ExploreReducer, ExploreState } from '../explore';
|
||||
import type { StartServices } from '../types';
|
||||
|
||||
|
@ -30,7 +29,6 @@ export interface RenderAppProps extends AppMountParameters {
|
|||
services: StartServices;
|
||||
store: Store<State, Action>;
|
||||
subPluginRoutes: RouteProps[];
|
||||
subscriptionTrackingServices: SubscriptionTrackingServices;
|
||||
usageCollection?: UsageCollectionSetup;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,17 +6,11 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from '@elastic/eui';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiText } from '@elastic/eui';
|
||||
|
||||
import { euiStyled } from '@kbn/kibana-react-plugin/common';
|
||||
import { SubscriptionLink } from '@kbn/subscription-tracking';
|
||||
import type { SubscriptionContextData } from '@kbn/subscription-tracking';
|
||||
import { INSIGHTS_UPSELL } from './translations';
|
||||
|
||||
const subscriptionContext: SubscriptionContextData = {
|
||||
feature: 'alert-details-insights',
|
||||
source: 'security__alert-details-flyout',
|
||||
};
|
||||
import { useKibana } from '../../../lib/kibana';
|
||||
|
||||
const UpsellContainer = euiStyled.div`
|
||||
border: 1px solid ${({ theme }) => theme.eui.euiColorLightShade};
|
||||
|
@ -29,6 +23,7 @@ const StyledIcon = euiStyled(EuiIcon)`
|
|||
`;
|
||||
|
||||
export const RelatedAlertsUpsell = React.memo(() => {
|
||||
const { application } = useKibana().services;
|
||||
return (
|
||||
<UpsellContainer>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="none">
|
||||
|
@ -37,13 +32,15 @@ export const RelatedAlertsUpsell = React.memo(() => {
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiText size="s">
|
||||
<SubscriptionLink
|
||||
<EuiLink
|
||||
color="subdued"
|
||||
target="_blank"
|
||||
subscriptionContext={subscriptionContext}
|
||||
href={application.getUrlForApp('management', {
|
||||
path: 'stack/license_management/home',
|
||||
})}
|
||||
>
|
||||
{INSIGHTS_UPSELL}
|
||||
</SubscriptionLink>
|
||||
</EuiLink>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -21,7 +21,6 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|||
import type { Action } from '@kbn/ui-actions-plugin/public';
|
||||
import { CellActionsProvider } from '@kbn/cell-actions';
|
||||
import { ExpandableFlyoutProvider } from '@kbn/expandable-flyout';
|
||||
import { MockSubscriptionTrackingProvider } from '@kbn/subscription-tracking/mocks';
|
||||
import { useKibana } from '../lib/kibana';
|
||||
import { UpsellingProvider } from '../components/upselling_provider';
|
||||
import { MockAssistantProvider } from './mock_assistant_provider';
|
||||
|
@ -76,29 +75,27 @@ export const TestProvidersComponent: React.FC<Props> = ({
|
|||
return (
|
||||
<I18nProvider>
|
||||
<MockKibanaContextProvider>
|
||||
<MockSubscriptionTrackingProvider>
|
||||
<UpsellingProviderMock>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<MockDiscoverInTimelineContext>
|
||||
<MockAssistantProvider>
|
||||
<ExpandableFlyoutProvider>
|
||||
<ConsoleManager>
|
||||
<CellActionsProvider
|
||||
getTriggerCompatibleActions={() => Promise.resolve(cellActions)}
|
||||
>
|
||||
<DragDropContext onDragEnd={onDragEnd}>{children}</DragDropContext>
|
||||
</CellActionsProvider>
|
||||
</ConsoleManager>
|
||||
</ExpandableFlyoutProvider>
|
||||
</MockAssistantProvider>
|
||||
</MockDiscoverInTimelineContext>
|
||||
</QueryClientProvider>
|
||||
</ThemeProvider>
|
||||
</ReduxStoreProvider>
|
||||
</UpsellingProviderMock>
|
||||
</MockSubscriptionTrackingProvider>
|
||||
<UpsellingProviderMock>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<MockDiscoverInTimelineContext>
|
||||
<MockAssistantProvider>
|
||||
<ExpandableFlyoutProvider>
|
||||
<ConsoleManager>
|
||||
<CellActionsProvider
|
||||
getTriggerCompatibleActions={() => Promise.resolve(cellActions)}
|
||||
>
|
||||
<DragDropContext onDragEnd={onDragEnd}>{children}</DragDropContext>
|
||||
</CellActionsProvider>
|
||||
</ConsoleManager>
|
||||
</ExpandableFlyoutProvider>
|
||||
</MockAssistantProvider>
|
||||
</MockDiscoverInTimelineContext>
|
||||
</QueryClientProvider>
|
||||
</ThemeProvider>
|
||||
</ReduxStoreProvider>
|
||||
</UpsellingProviderMock>
|
||||
</MockKibanaContextProvider>
|
||||
</I18nProvider>
|
||||
);
|
||||
|
@ -130,33 +127,31 @@ const TestProvidersWithPrivilegesComponent: React.FC<Props> = ({
|
|||
return (
|
||||
<I18nProvider>
|
||||
<MockKibanaContextProvider>
|
||||
<MockSubscriptionTrackingProvider>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<MockDiscoverInTimelineContext>
|
||||
<MockAssistantProvider>
|
||||
<UserPrivilegesProvider
|
||||
kibanaCapabilities={
|
||||
{
|
||||
siem: { show: true, crud: true },
|
||||
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: false },
|
||||
[ASSISTANT_FEATURE_ID]: { 'ai-assistant': true },
|
||||
} as unknown as Capabilities
|
||||
}
|
||||
<ReduxStoreProvider store={store}>
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<MockDiscoverInTimelineContext>
|
||||
<MockAssistantProvider>
|
||||
<UserPrivilegesProvider
|
||||
kibanaCapabilities={
|
||||
{
|
||||
siem: { show: true, crud: true },
|
||||
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: false },
|
||||
[ASSISTANT_FEATURE_ID]: { 'ai-assistant': true },
|
||||
} as unknown as Capabilities
|
||||
}
|
||||
>
|
||||
<CellActionsProvider
|
||||
getTriggerCompatibleActions={() => Promise.resolve(cellActions)}
|
||||
>
|
||||
<CellActionsProvider
|
||||
getTriggerCompatibleActions={() => Promise.resolve(cellActions)}
|
||||
>
|
||||
<DragDropContext onDragEnd={onDragEnd}>{children}</DragDropContext>
|
||||
</CellActionsProvider>
|
||||
</UserPrivilegesProvider>
|
||||
</MockAssistantProvider>
|
||||
</MockDiscoverInTimelineContext>
|
||||
</QueryClientProvider>
|
||||
</ThemeProvider>
|
||||
</ReduxStoreProvider>
|
||||
</MockSubscriptionTrackingProvider>
|
||||
<DragDropContext onDragEnd={onDragEnd}>{children}</DragDropContext>
|
||||
</CellActionsProvider>
|
||||
</UserPrivilegesProvider>
|
||||
</MockAssistantProvider>
|
||||
</MockDiscoverInTimelineContext>
|
||||
</QueryClientProvider>
|
||||
</ThemeProvider>
|
||||
</ReduxStoreProvider>
|
||||
</MockKibanaContextProvider>
|
||||
</I18nProvider>
|
||||
);
|
||||
|
|
|
@ -19,7 +19,8 @@ import { RESPONSE_ACTION_TYPES } from '../../../../../common/api/detection_engin
|
|||
import { login, ROLE } from '../../tasks/login';
|
||||
|
||||
describe('Form', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
|
||||
describe('User with no access can not create an endpoint response action', () => {
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/169334
|
||||
describe.skip('User with no access can not create an endpoint response action', () => {
|
||||
before(() => {
|
||||
login(ROLE.endpoint_response_actions_no_access);
|
||||
});
|
||||
|
|
|
@ -226,11 +226,6 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
const services = await startServices(params);
|
||||
await this.registerActions(store, params.history, services);
|
||||
|
||||
const subscriptionTrackingServices = {
|
||||
analyticsClient: coreStart.analytics,
|
||||
navigateToApp: coreStart.application.navigateToApp,
|
||||
};
|
||||
|
||||
const { renderApp } = await this.lazyApplicationDependencies();
|
||||
const { getSubPluginRoutesByCapabilities } = await this.lazyHelpersForRoutes();
|
||||
|
||||
|
@ -244,7 +239,6 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
coreStart.application.capabilities,
|
||||
services
|
||||
),
|
||||
subscriptionTrackingServices,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
|
@ -173,10 +173,9 @@
|
|||
"@kbn/security-solution-features",
|
||||
"@kbn/content-management-plugin",
|
||||
"@kbn/discover-utils",
|
||||
"@kbn/subscription-tracking",
|
||||
"@kbn/core-application-common",
|
||||
"@kbn/openapi-generator",
|
||||
"@kbn/es",
|
||||
"@kbn/react-kibana-mount"
|
||||
"@kbn/react-kibana-mount",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -6,17 +6,21 @@
|
|||
*/
|
||||
|
||||
import React, { VFC } from 'react';
|
||||
import { EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiEmptyPrompt,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiIcon,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { SubscriptionButtonEmpty } from '@kbn/subscription-tracking';
|
||||
import type { SubscriptionContextData } from '@kbn/subscription-tracking';
|
||||
|
||||
const subscriptionContext: SubscriptionContextData = {
|
||||
feature: 'threat-intelligence',
|
||||
source: 'security__threat-intelligence',
|
||||
};
|
||||
import { useKibana } from '../hooks/use_kibana';
|
||||
|
||||
export const Paywall: VFC = () => {
|
||||
const {
|
||||
services: { application },
|
||||
} = useKibana();
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
icon={<EuiIcon type="logoSecurity" size="xl" />}
|
||||
|
@ -52,12 +56,18 @@ export const Paywall: VFC = () => {
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<div>
|
||||
<SubscriptionButtonEmpty subscriptionContext={subscriptionContext}>
|
||||
<EuiButtonEmpty
|
||||
onClick={() =>
|
||||
application.navigateToApp('management', {
|
||||
path: 'stack/license_management/home',
|
||||
})
|
||||
}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.threatIntelligence.paywall.trial"
|
||||
defaultMessage="Start a free trial"
|
||||
/>
|
||||
</SubscriptionButtonEmpty>
|
||||
</EuiButtonEmpty>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -12,7 +12,6 @@ import { CoreStart, IUiSettingsClient } from '@kbn/core/public';
|
|||
import { TimelinesUIStart } from '@kbn/timelines-plugin/public';
|
||||
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
|
||||
import { RequestAdapter } from '@kbn/inspector-plugin/common';
|
||||
import { MockSubscriptionTrackingProvider } from '@kbn/subscription-tracking/mocks';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import type { SettingsStart } from '@kbn/core-ui-settings-browser';
|
||||
import { mockIndicatorsFiltersContext } from './mock_indicators_filters_context';
|
||||
|
@ -108,9 +107,7 @@ export const StoryProvidersComponent: VFC<StoryProvidersComponentProps> = ({
|
|||
<SecuritySolutionContext.Provider value={securitySolutionContextMock}>
|
||||
<IndicatorsFiltersContext.Provider value={mockIndicatorsFiltersContext}>
|
||||
<KibanaReactContext.Provider>
|
||||
<MockSubscriptionTrackingProvider>
|
||||
<BlockListProvider>{children}</BlockListProvider>
|
||||
</MockSubscriptionTrackingProvider>
|
||||
<BlockListProvider>{children}</BlockListProvider>
|
||||
</KibanaReactContext.Provider>
|
||||
</IndicatorsFiltersContext.Provider>
|
||||
</SecuritySolutionContext.Provider>
|
||||
|
|
|
@ -17,7 +17,6 @@ import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks
|
|||
import { createTGridMocks } from '@kbn/timelines-plugin/public/mock';
|
||||
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
|
||||
import { RequestAdapter } from '@kbn/inspector-plugin/common';
|
||||
import { MockSubscriptionTrackingProvider } from '@kbn/subscription-tracking/mocks';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { casesPluginMock } from '@kbn/cases-plugin/public/mocks';
|
||||
|
@ -142,13 +141,11 @@ export const TestProvidersComponent: FC = ({ children }) => (
|
|||
<EuiThemeProvider>
|
||||
<SecuritySolutionContext.Provider value={mockSecurityContext}>
|
||||
<KibanaContext.Provider value={{ services: mockedServices } as any}>
|
||||
<MockSubscriptionTrackingProvider>
|
||||
<I18nProvider>
|
||||
<IndicatorsFiltersContext.Provider value={mockIndicatorsFiltersContext}>
|
||||
{children}
|
||||
</IndicatorsFiltersContext.Provider>
|
||||
</I18nProvider>
|
||||
</MockSubscriptionTrackingProvider>
|
||||
<I18nProvider>
|
||||
<IndicatorsFiltersContext.Provider value={mockIndicatorsFiltersContext}>
|
||||
{children}
|
||||
</IndicatorsFiltersContext.Provider>
|
||||
</I18nProvider>
|
||||
</KibanaContext.Provider>
|
||||
</SecuritySolutionContext.Provider>
|
||||
</EuiThemeProvider>
|
||||
|
|
|
@ -11,7 +11,6 @@ import { Provider as ReduxStoreProvider } from 'react-redux';
|
|||
import React, { Suspense } from 'react';
|
||||
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
|
||||
import { ExternalReferenceAttachmentType } from '@kbn/cases-plugin/public/client/attachment_framework/types';
|
||||
import { SubscriptionTrackingProvider } from '@kbn/subscription-tracking';
|
||||
import { generateAttachmentType } from './modules/cases/utils/attachments';
|
||||
import { KibanaContextProvider } from './hooks/use_kibana';
|
||||
import {
|
||||
|
@ -44,16 +43,11 @@ export const createApp =
|
|||
<ReduxStoreProvider store={securitySolutionContext.securitySolutionStore}>
|
||||
<SecuritySolutionContext.Provider value={securitySolutionContext}>
|
||||
<KibanaContextProvider services={services}>
|
||||
<SubscriptionTrackingProvider
|
||||
analyticsClient={services.analytics}
|
||||
navigateToApp={services.application.navigateToApp}
|
||||
>
|
||||
<EnterpriseGuard>
|
||||
<Suspense fallback={<div />}>
|
||||
<LazyIndicatorsPageWrapper />
|
||||
</Suspense>
|
||||
</EnterpriseGuard>
|
||||
</SubscriptionTrackingProvider>
|
||||
<EnterpriseGuard>
|
||||
<Suspense fallback={<div />}>
|
||||
<LazyIndicatorsPageWrapper />
|
||||
</Suspense>
|
||||
</EnterpriseGuard>
|
||||
</KibanaContextProvider>
|
||||
</SecuritySolutionContext.Provider>
|
||||
</ReduxStoreProvider>
|
||||
|
|
|
@ -32,8 +32,7 @@
|
|||
"@kbn/utility-types",
|
||||
"@kbn/ui-theme",
|
||||
"@kbn/securitysolution-io-ts-list-types",
|
||||
"@kbn/core-ui-settings-browser",
|
||||
"@kbn/subscription-tracking"
|
||||
"@kbn/core-ui-settings-browser"
|
||||
],
|
||||
"exclude": ["target/**/*"]
|
||||
}
|
||||
|
|
|
@ -5951,10 +5951,6 @@
|
|||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/subscription-tracking@link:packages/kbn-subscription-tracking":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/synthetics-plugin@link:x-pack/plugins/synthetics":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue