Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { LazyAnomalyChartsContainer } from './lazy_anomaly_charts_container';
import { getAnomalyChartsServiceDependencies } from './get_anomaly_charts_services_dependencies';
import { buildDataViewPublishingApi } from '../common/build_data_view_publishing_api';
import { EmbeddableAnomalyChartsUserInput } from './anomaly_charts_setup_flyout';
import { ensureLicense } from '../ensure_license';

export const getAnomalyChartsReactEmbeddableFactory = (
getStartServices: StartServicesAccessor<MlStartDependencies, MlPluginStart>,
Expand All @@ -49,6 +50,7 @@ export const getAnomalyChartsReactEmbeddableFactory = (
const factory: EmbeddableFactory<AnomalyChartsEmbeddableState, AnomalyChartsEmbeddableApi> = {
type: ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE,
buildEmbeddable: async ({ initialState, finalizeApi, uuid, parentApi }) => {
await ensureLicense(getStartServices);
if (!apiHasExecutionContext(parentApi)) {
throw new Error('Parent API does not have execution context');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ const mockResponse = of([
},
]);

jest.mock('../ensure_license', () => {
return {
ensureLicense: jest.fn(),
};
});

jest.mock('../../application/services/anomaly_detector_service', () => {
return {
AnomalyDetectorService: jest.fn().mockImplementation(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import { initializeSwimLaneControls, swimLaneComparators } from './initialize_sw
import { initializeSwimLaneDataFetcher } from './initialize_swim_lane_data_fetcher';
import type { AnomalySwimLaneEmbeddableApi } from './types';
import { AnomalySwimlaneUserInput } from './anomaly_swimlane_setup_flyout';
import { ensureLicense } from '../ensure_license';

/**
* Provides the services required by the Anomaly Swimlane Embeddable.
Expand Down Expand Up @@ -90,6 +91,7 @@ export const getAnomalySwimLaneEmbeddableFactory = (
const factory: EmbeddableFactory<AnomalySwimLaneEmbeddableState, AnomalySwimLaneEmbeddableApi> = {
type: ANOMALY_SWIMLANE_EMBEDDABLE_TYPE,
buildEmbeddable: async ({ initialState, finalizeApi, uuid, parentApi }) => {
await ensureLicense(getStartServices);
if (!apiHasExecutionContext(parentApi)) {
throw new Error('Parent API does not have execution context');
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* 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 { MlCapabilities } from '@kbn/ml-common-types/capabilities';
import { isFullLicense } from '../../common/license';
import type { MlCoreSetup } from '../plugin';

export async function ensureLicense(getStartServices: MlCoreSetup['getStartServices']) {
const [coreStart, pluginStart] = await getStartServices();
const license = await pluginStart.licensing.getLicense();
if (
!isFullLicense(license) ||
!(coreStart.application.capabilities.ml as MlCapabilities).canGetMlInfo
) {
throw new Error('Invalid license');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { getServices } from './get_services';
import { useReactEmbeddableExecutionContext } from '../common/use_embeddable_execution_context';
import { getSingleMetricViewerComponent } from '../../shared_components/single_metric_viewer';
import { EmbeddableSingleMetricViewerUserInput } from './single_metric_viewer_setup_flyout';
import { ensureLicense } from '../ensure_license';

export const getSingleMetricViewerEmbeddableFactory = (
getStartServices: StartServicesAccessor<MlStartDependencies, MlPluginStart>,
Expand All @@ -47,6 +48,7 @@ export const getSingleMetricViewerEmbeddableFactory = (
> = {
type: ANOMALY_SINGLE_METRIC_VIEWER_EMBEDDABLE_TYPE,
buildEmbeddable: async ({ initialState, finalizeApi, uuid, parentApi }) => {
await ensureLicense(getStartServices);
const services = await getServices(getStartServices, usageCollection);
const subscriptions = new Subscription();
const titleManager = initializeTitleManager(initialState);
Expand Down
13 changes: 5 additions & 8 deletions x-pack/platform/plugins/shared/ml/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,11 @@ export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
this.managementLocator = new MlManagementLocatorInternal(pluginsSetup.share);
}

if (this.enabledFeatures.ad) {
registerEmbeddables(pluginsSetup.embeddable, core, pluginsSetup.usageCollection);
}
registerMlUiActions(pluginsSetup.uiActions, core);

const licensing = pluginsSetup.licensing.license$.pipe(take(1));
licensing
.pipe(
Expand Down Expand Up @@ -276,14 +281,6 @@ export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
);
}

if (fullLicense && mlCapabilities.canGetMlInfo && this.enabledFeatures.ad) {
registerEmbeddables(pluginsSetup.embeddable, core, pluginsSetup.usageCollection);
}

if (fullLicense && mlCapabilities.canGetMlInfo) {
registerMlUiActions(pluginsSetup.uiActions, core);
}

const { registerSearchLinks, registerCasesAttachments } = await import(
'./register_helper'
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* 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 { MlCapabilities } from '@kbn/ml-common-types/capabilities';
import { isFullLicense } from '../../common/license';
import type { MlCoreSetup } from '../plugin';

export async function isValidLicense(getStartServices: MlCoreSetup['getStartServices']) {
const [coreStart, pluginStart] = await getStartServices();
const license = await pluginStart.licensing.getLicense();
return (
isFullLicense(license) && (coreStart.application.capabilities.ml as MlCapabilities).canGetMlInfo
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
ANOMALY_SINGLE_METRIC_VIEWER_EMBEDDABLE_TYPE,
} from '../embeddables';
import { CONTROLLED_BY_ANOMALY_CHARTS_FILTER } from './constants';
import { isValidLicense } from './action_license_check';

export const APPLY_ENTITY_FIELD_FILTERS_ACTION = 'applyEntityFieldFiltersAction';

Expand Down Expand Up @@ -80,6 +81,7 @@ export function createApplyEntityFieldFiltersAction(
);
},
async isCompatible({ embeddable, data }) {
if (!(await isValidLicense(getStartServices))) return false;
return (
(embeddable.type === ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE ||
embeddable.type === ANOMALY_SINGLE_METRIC_VIEWER_EMBEDDABLE_TYPE) &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { VIEW_BY_JOB_LABEL } from '../application/explorer/explorer_constants';
import type { SwimLaneDrilldownContext } from '../embeddables';
import type { MlCoreSetup } from '../plugin';
import { CONTROLLED_BY_SWIM_LANE_FILTER } from './constants';
import { isValidLicense } from './action_license_check';

export const APPLY_INFLUENCER_FILTERS_ACTION = 'applyInfluencerFiltersAction';

Expand Down Expand Up @@ -76,6 +77,7 @@ export function createApplyInfluencerFiltersAction(
);
},
async isCompatible(context: EmbeddableApiContext) {
if (!(await isValidLicense(getStartServices))) return false;
const [{ application }] = await getStartServices();
const appId = await firstValueFrom(application.currentAppId$);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { isAnomalySwimlaneSelectionTriggerContext } from './triggers';
import type { AppStateSelectedCells } from '../application/explorer/explorer_utils';
import type { AnomalySwimLaneEmbeddableApi } from '../embeddables/anomaly_swimlane/types';
import type { MlCoreSetup } from '../plugin';
import { isValidLicense } from './action_license_check';

export const APPLY_TIME_RANGE_SELECTION_ACTION = 'applyTimeRangeSelectionAction';

Expand Down Expand Up @@ -64,6 +65,7 @@ export function createApplyTimeRangeSelectionAction(
});
},
async isCompatible(context) {
if (!(await isValidLicense(getStartServices))) return false;
const [{ application }] = await getStartServices();
const appId = await firstValueFrom(application.currentAppId$);
return isAnomalySwimlaneSelectionTriggerContext(context) && supportedApps.includes(appId!);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { i18n } from '@kbn/i18n';
import type { UiActionsActionDefinition } from '@kbn/ui-actions-plugin/public';
import type { MlCoreSetup } from '../plugin';
import { isValidLicense } from './action_license_check';

export const CLEAR_SELECTION_ACTION = 'clearSelectionAction';

Expand All @@ -33,6 +34,7 @@ export function createClearSelectionAction(
updateCallback();
},
async isCompatible({ updateCallback }) {
if (!(await isValidLicense(getStartServices))) return false;
return typeof updateCallback === 'function';
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type { AnomalyChartsEmbeddableApi } from '../embeddables';
import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE } from '../embeddables';
import type { MlCoreSetup } from '../plugin';
import { EmbeddableAnomalyChartsUserInput } from '../embeddables/anomaly_charts/anomaly_charts_setup_flyout';
import { isValidLicense } from './action_license_check';

export const EDIT_ANOMALY_CHARTS_PANEL_ACTION = 'editAnomalyChartsPanelAction';

Expand Down Expand Up @@ -55,6 +56,7 @@ export function createAddAnomalyChartsPanelAction(
defaultMessage: 'View anomaly detection results in a chart.',
}),
async isCompatible(context: EmbeddableApiContext) {
if (!(await isValidLicense(getStartServices))) return false;
return Boolean(await parentApiIsCompatible(context.embeddable));
},
async execute(context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { ANOMALY_SINGLE_METRIC_VIEWER_EMBEDDABLE_TYPE } from '../embeddables';
import type { SingleMetricViewerEmbeddableApi } from '../embeddables/types';
import type { MlCoreSetup } from '../plugin';
import { EmbeddableSingleMetricViewerUserInput } from '../embeddables/single_metric_viewer/single_metric_viewer_setup_flyout';
import { isValidLicense } from './action_license_check';

export type CreateSingleMetricViewerPanelActionContext = EmbeddableApiContext & {
embeddable: SingleMetricViewerEmbeddableApi;
Expand Down Expand Up @@ -56,6 +57,7 @@ export function createAddSingleMetricViewerPanelAction(
'View anomaly detection results for a single metric on a focused date range.',
}),
async isCompatible(context: EmbeddableApiContext) {
if (!(await isValidLicense(getStartServices))) return false;
return Boolean(await parentApiIsCompatible(context.embeddable));
},
async execute(context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ANOMALY_SWIMLANE_EMBEDDABLE_TYPE } from '../embeddables';
import type { AnomalySwimLaneEmbeddableApi } from '../embeddables/anomaly_swimlane/types';
import type { MlCoreSetup } from '../plugin';
import { AnomalySwimlaneUserInput } from '../embeddables/anomaly_swimlane/anomaly_swimlane_setup_flyout';
import { isValidLicense } from './action_license_check';

export const EDIT_SWIMLANE_PANEL_ACTION = 'editSwimlanePanelAction';

Expand Down Expand Up @@ -55,6 +56,7 @@ export function createAddSwimlanePanelAction(
defaultMessage: 'View anomaly detection results in a timeline.',
}),
async isCompatible(context: EmbeddableApiContext) {
if (!(await isValidLicense(getStartServices))) return false;
return Boolean(await parentApiIsCompatible(context.embeddable));
},
async execute(context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
type CreateCategorizationADJobContext,
} from '@kbn/ml-ui-actions';
import type { MlCoreSetup } from '../plugin';
import { isValidLicense } from './action_license_check';

export function createCategorizationADJobAction(
getStartServices: MlCoreSetup['getStartServices']
Expand Down Expand Up @@ -50,6 +51,7 @@ export function createCategorizationADJobAction(
}
},
async isCompatible({ dataView, field }: CreateCategorizationADJobContext) {
if (!(await isValidLicense(getStartServices))) return false;
return (
dataView.timeFieldName !== undefined &&
dataView.fields.find((f) => f.name === field.name) !== undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type { AnomalySwimLaneEmbeddableApi } from '../embeddables/anomaly_swimla
import { isSwimLaneEmbeddableContext } from '../embeddables/anomaly_swimlane/types';
import type { MlCoreSetup } from '../plugin';
import { getEmbeddableTimeRange } from './get_embeddable_time_range';
import { isValidLicense } from './action_license_check';

export interface OpenInAnomalyExplorerSwimLaneActionContext extends EmbeddableApiContext {
embeddable: AnomalySwimLaneEmbeddableApi;
Expand Down Expand Up @@ -154,6 +155,7 @@ export function createOpenInExplorerAction(
}
},
async isCompatible(context: EmbeddableApiContext) {
if (!(await isValidLicense(getStartServices))) return false;
return isSwimLaneEmbeddableContext(context) || isAnomalyChartsEmbeddableContext(context);
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ANOMALY_SINGLE_METRIC_VIEWER_EMBEDDABLE_TYPE } from '../embeddables';

import type { MlCoreSetup } from '../plugin';
import { getEmbeddableTimeRange } from './get_embeddable_time_range';
import { isValidLicense } from './action_license_check';

export interface OpenInSingleMetricViewerActionContext extends EmbeddableApiContext {
embeddable: SingleMetricViewerEmbeddableApi;
Expand Down Expand Up @@ -91,6 +92,7 @@ export function createOpenInSingleMetricViewerAction(
}
},
async isCompatible(context: EmbeddableApiContext) {
if (!(await isValidLicense(getStartServices))) return false;
return isSingleMetricViewerEmbeddableContext(context);
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { isOfAggregateQueryType } from '@kbn/es-query';
import { LENS_EMBEDDABLE_TYPE } from '@kbn/lens-common';
import type { ActionApi } from './types';
import type { MlCoreSetup } from '../plugin';
import { isValidLicense } from './action_license_check';

export const CREATE_LENS_VIS_TO_ML_AD_JOB_ACTION = 'createMLADJobAction';

Expand Down Expand Up @@ -57,6 +58,7 @@ export function createVisToADJobAction(
}
},
async isCompatible({ embeddable }: EmbeddableApiContext) {
if (!(await isValidLicense(getStartServices))) return false;
if (
!isApiCompatible(embeddable) ||
!(apiIsOfType(embeddable, LENS_EMBEDDABLE_TYPE) || apiIsOfType(embeddable, 'map'))
Expand Down
Loading