mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Semantic Text UI] Display semantic_text based on licensing (#185902)
This PR includes changes related to displaying semantic_text based on the ml operations capacity of the users. The PR includes the following changes: - Display a banner based on the user's capacity to run ML operations. - Display semantic_text if the user has the capacity to run ML operations; otherwise, hide the semantic_text field. ### Trial License <img width="1052" alt="Screenshot 2024-06-10 at 4 06 16 PM" src="56a492db
-c181-44ca-a77d-ea14a54ed0a3"> ### Basic License <img width="1456" alt="Screenshot 2024-06-10 at 4 00 56 PM" src="aa9e0e6c
-7a5f-4637-896b-9c2c2a1e152a"> ### Serverless <img width="1083" alt="Screenshot 2024-06-10 at 3 52 19 PM" src="bd1fe21d
-aacb-4b6a-98d9-489fab62e506"> # How to test - Enable semantic_text in config/kibana.yml. `xpack.index_management.dev.enableSemanticText: true` - For Basic license, we can run elastic_search using: `yarn es snapshot` - For Trial license, we can run elastic_seach using: `yarn es snapshot --license trial` - For serverless, we can run elastic_search using: `yarn es serverless --projectType es`
This commit is contained in:
parent
dca0ea2ade
commit
385bb2b35b
9 changed files with 112 additions and 25 deletions
|
@ -647,6 +647,10 @@ describe('<IndexDetailsPage />', () => {
|
|||
});
|
||||
describe('Add Semantic text field', () => {
|
||||
const customInferenceModel = 'my-elser-model';
|
||||
const mockLicense = {
|
||||
isActive: true,
|
||||
hasAtLeast: jest.fn((type) => true),
|
||||
};
|
||||
beforeEach(async () => {
|
||||
httpRequestsMockHelpers.setInferenceModels({
|
||||
data: [
|
||||
|
@ -674,7 +678,18 @@ describe('<IndexDetailsPage />', () => {
|
|||
enterpriseSearch: '',
|
||||
},
|
||||
},
|
||||
core: {
|
||||
application: { capabilities: { ml: { canGetTrainedModels: true } } },
|
||||
},
|
||||
plugins: {
|
||||
licensing: {
|
||||
license$: {
|
||||
subscribe: jest.fn((callback) => {
|
||||
callback(mockLicense);
|
||||
return { unsubscribe: jest.fn() };
|
||||
}),
|
||||
},
|
||||
},
|
||||
ml: {
|
||||
mlApi: {
|
||||
trainedModels: {
|
||||
|
|
|
@ -20,7 +20,7 @@ describe('When semantic_text is enabled', () => {
|
|||
getItemSpy = jest.spyOn(Storage.prototype, 'getItem');
|
||||
setItemSpy = jest.spyOn(Storage.prototype, 'setItem');
|
||||
const setup = registerTestBed(SemanticTextBanner, {
|
||||
defaultProps: { isSemanticTextEnabled: true },
|
||||
defaultProps: { isSemanticTextEnabled: true, isPlatinumLicense: true },
|
||||
memoryRouter: { wrapComponent: false },
|
||||
});
|
||||
const testBed = setup();
|
||||
|
@ -56,6 +56,21 @@ describe('When semantic_text is enabled', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('when user does not have ML permissions', () => {
|
||||
const setupWithNoMlPermission = registerTestBed(SemanticTextBanner, {
|
||||
defaultProps: { isSemanticTextEnabled: true, isPlatinumLicense: false },
|
||||
memoryRouter: { wrapComponent: false },
|
||||
});
|
||||
|
||||
const { find } = setupWithNoMlPermission();
|
||||
|
||||
it('should contain content related to semantic_text', () => {
|
||||
expect(find('indexDetailsMappingsSemanticTextBanner').text()).toContain(
|
||||
'Semantic text now available for platinum license'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('When semantic_text is disabled', () => {
|
||||
const setup = registerTestBed(SemanticTextBanner, {
|
||||
defaultProps: { isSemanticTextEnabled: false },
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"browser": true,
|
||||
"configPath": ["xpack", "index_management"],
|
||||
"requiredPlugins": ["home", "management", "features", "share"],
|
||||
"optionalPlugins": ["security", "usageCollection", "fleet", "cloud", "ml", "console"],
|
||||
"optionalPlugins": ["security", "usageCollection", "fleet", "cloud", "ml", "console","licensing"],
|
||||
"requiredBundles": ["kibanaReact", "esUiShared", "runtimeFields"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import type { CloudSetup } from '@kbn/cloud-plugin/public';
|
|||
import type { ConsolePluginStart } from '@kbn/console-plugin/public';
|
||||
|
||||
import { EuiBreadcrumb } from '@elastic/eui';
|
||||
import { LicensingPluginStart } from '@kbn/licensing-plugin/public';
|
||||
import { ExtensionsService } from '../services';
|
||||
import { HttpService, NotificationService, UiMetricService } from './services';
|
||||
import { IndexManagementBreadcrumb } from './services/breadcrumbs';
|
||||
|
@ -48,6 +49,7 @@ export interface AppDependencies {
|
|||
share: SharePluginStart;
|
||||
cloud?: CloudSetup;
|
||||
console?: ConsolePluginStart;
|
||||
licensing?: LicensingPluginStart;
|
||||
ml?: MlPluginStart;
|
||||
};
|
||||
services: {
|
||||
|
|
|
@ -83,6 +83,7 @@ export function getIndexManagementDependencies({
|
|||
cloud,
|
||||
console: startDependencies.console,
|
||||
ml: startDependencies.ml,
|
||||
licensing: startDependencies.licensing,
|
||||
},
|
||||
services: {
|
||||
httpService,
|
||||
|
|
|
@ -27,6 +27,7 @@ import { css } from '@emotion/react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import React, { FunctionComponent, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { ILicense } from '@kbn/licensing-plugin/public';
|
||||
import { Index } from '../../../../../../common';
|
||||
import { useDetailsPageMappingsModelManagement } from '../../../../../hooks/use_details_page_mappings_model_management';
|
||||
import { useAppContext } from '../../../../app_context';
|
||||
|
@ -65,17 +66,33 @@ export const DetailsPageMappingsContent: FunctionComponent<{
|
|||
}> = ({ index, data, jsonData, refetchMapping, showAboutMappings }) => {
|
||||
const {
|
||||
services: { extensionsService },
|
||||
core: { getUrlForApp },
|
||||
plugins: { ml },
|
||||
core: {
|
||||
getUrlForApp,
|
||||
application: { capabilities },
|
||||
},
|
||||
plugins: { ml, licensing },
|
||||
url,
|
||||
config,
|
||||
} = useAppContext();
|
||||
|
||||
const [isPlatinumLicense, setIsPlatinumLicense] = useState<boolean>(false);
|
||||
useEffect(() => {
|
||||
const subscription = licensing?.license$.subscribe((license: ILicense) => {
|
||||
setIsPlatinumLicense(license.isActive && license.hasAtLeast('platinum'));
|
||||
});
|
||||
|
||||
return () => subscription?.unsubscribe();
|
||||
}, [licensing]);
|
||||
|
||||
const { enableSemanticText: isSemanticTextEnabled } = config;
|
||||
const [errorsInTrainedModelDeployment, setErrorsInTrainedModelDeployment] = useState<string[]>(
|
||||
[]
|
||||
);
|
||||
|
||||
const hasMLPermissions = capabilities?.ml?.canGetTrainedModels ? true : false;
|
||||
|
||||
const semanticTextInfo = {
|
||||
isSemanticTextEnabled,
|
||||
isSemanticTextEnabled: isSemanticTextEnabled && hasMLPermissions && isPlatinumLicense,
|
||||
indexName: index.name,
|
||||
ml,
|
||||
setErrorsInTrainedModelDeployment,
|
||||
|
@ -164,7 +181,7 @@ export const DetailsPageMappingsContent: FunctionComponent<{
|
|||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSemanticTextEnabled) {
|
||||
if (!isSemanticTextEnabled || !hasMLPermissions) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -182,15 +199,19 @@ export const DetailsPageMappingsContent: FunctionComponent<{
|
|||
return;
|
||||
}
|
||||
|
||||
if (!hasMLPermissions) {
|
||||
return;
|
||||
}
|
||||
|
||||
await fetchInferenceToModelIdMap();
|
||||
} catch (exception) {
|
||||
setSaveMappingError(exception.message);
|
||||
}
|
||||
}, [fetchInferenceToModelIdMap, isSemanticTextEnabled]);
|
||||
}, [fetchInferenceToModelIdMap, isSemanticTextEnabled, hasMLPermissions]);
|
||||
|
||||
const updateMappings = useCallback(async () => {
|
||||
try {
|
||||
if (isSemanticTextEnabled) {
|
||||
if (isSemanticTextEnabled && hasMLPermissions) {
|
||||
await fetchInferenceToModelIdMap();
|
||||
|
||||
if (pendingDeployments.length > 0) {
|
||||
|
@ -487,7 +508,12 @@ export const DetailsPageMappingsContent: FunctionComponent<{
|
|||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiFlexItem grow={true}>
|
||||
<SemanticTextBanner isSemanticTextEnabled={isSemanticTextEnabled} />
|
||||
{hasMLPermissions && (
|
||||
<SemanticTextBanner
|
||||
isSemanticTextEnabled={isSemanticTextEnabled}
|
||||
isPlatinumLicense={isPlatinumLicense}
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
{errorSavingMappings}
|
||||
{isAddingFields && (
|
||||
|
|
|
@ -11,9 +11,47 @@ import useLocalStorage from 'react-use/lib/useLocalStorage';
|
|||
|
||||
interface SemanticTextBannerProps {
|
||||
isSemanticTextEnabled: boolean;
|
||||
isPlatinumLicense?: boolean;
|
||||
}
|
||||
|
||||
export function SemanticTextBanner({ isSemanticTextEnabled }: SemanticTextBannerProps) {
|
||||
const defaultLicenseMessage = (
|
||||
<FormattedMessage
|
||||
id="xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.descriptionForPlatinumLicense"
|
||||
defaultMessage="{label} Upgrade your license to add semantic_text field types to your indices.'"
|
||||
values={{
|
||||
label: (
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.semanticTextFieldAvailableForPlatinumLicense"
|
||||
defaultMessage="Semantic text now available for platinum license."
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const platinumLicenseMessage = (
|
||||
<FormattedMessage
|
||||
id="xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.description"
|
||||
defaultMessage="{label} Add a field to your mapping and choose 'semantic_text' to get started.'"
|
||||
values={{
|
||||
label: (
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.semanticTextFieldAvailable"
|
||||
defaultMessage="semantic_text field type now available!"
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
export function SemanticTextBanner({
|
||||
isSemanticTextEnabled,
|
||||
isPlatinumLicense = false,
|
||||
}: SemanticTextBannerProps) {
|
||||
const [isSemanticTextBannerDisplayable, setIsSemanticTextBannerDisplayable] =
|
||||
useLocalStorage<boolean>('semantic-text-banner-display', true);
|
||||
|
||||
|
@ -23,20 +61,7 @@ export function SemanticTextBanner({ isSemanticTextEnabled }: SemanticTextBanner
|
|||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiText size="m" color="success">
|
||||
<FormattedMessage
|
||||
id="xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.description"
|
||||
defaultMessage="{label} Add a field to your mapping and choose 'semantic_text' to get started.'"
|
||||
values={{
|
||||
label: (
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.semanticTextFieldAvailable"
|
||||
defaultMessage="semantic_text field type now available!"
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
{isPlatinumLicense ? platinumLicenseMessage : defaultLicenseMessage}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
|
|
|
@ -112,7 +112,7 @@ export class IndexMgmtUIPlugin
|
|||
}
|
||||
|
||||
public start(coreStart: CoreStart, plugins: StartDependencies): IndexManagementPluginStart {
|
||||
const { fleet, usageCollection, cloud, share, console, ml } = plugins;
|
||||
const { fleet, usageCollection, cloud, share, console, ml, licensing } = plugins;
|
||||
return {
|
||||
extensionsService: this.extensionsService.setup(),
|
||||
getIndexMappingComponent: (deps: { history: ScopedHistory<unknown> }) => {
|
||||
|
@ -134,6 +134,7 @@ export class IndexMgmtUIPlugin
|
|||
cloud,
|
||||
console,
|
||||
ml,
|
||||
licensing,
|
||||
},
|
||||
services: {
|
||||
extensionsService: this.extensionsService,
|
||||
|
|
|
@ -17,6 +17,7 @@ import { ManagementSetup } from '@kbn/management-plugin/public';
|
|||
import { MlPluginStart } from '@kbn/ml-plugin/public';
|
||||
import { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public';
|
||||
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
|
||||
import { LicensingPluginStart } from '@kbn/licensing-plugin/public';
|
||||
|
||||
export interface IndexManagementStartServices {
|
||||
analytics: Pick<AnalyticsServiceStart, 'reportEvent'>;
|
||||
|
@ -40,6 +41,7 @@ export interface StartDependencies {
|
|||
fleet?: unknown;
|
||||
usageCollection: UsageCollectionSetup;
|
||||
management: ManagementSetup;
|
||||
licensing?: LicensingPluginStart;
|
||||
ml?: MlPluginStart;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue