From c18513e23da91e64a1b0802af185551b590862c6 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 27 Sep 2023 09:32:17 +0100 Subject: [PATCH 1/3] [ML] Removing global isServerless flag (#166651) Removed `isServerless` flag which lived in our global context and had to be passed about to the various components which create their own version of the context using `getMlGlobalServices` This PR adds a new context which contains flags for all of the features which can be toggled when in serverless mode. Flags added: ``` showNodeInfo showMLNavMenu showLicenseInfo isADEnabled isDFAEnabled isNLPEnabled ``` The enabled features flags are now read from the config file client side, rather than using capabilities. Additional changes: - Changes the wording of the awaiting ML node callout in serverless. - In the search project, the default ML page is the trained models list and not Overview - Reenables the Memory Usage page for all projects --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../test_suites/core_plugins/rendering.ts | 3 + .../components/full_time_range_selector.tsx | 18 +-- .../src/hooks/use_date_picker_context.tsx | 4 +- .../change_point_detection_root.tsx | 6 +- .../log_categorization_app_state.tsx | 6 +- .../log_rate_analysis_app_state.tsx | 6 +- .../log_rate_analysis_content_wrapper.tsx | 5 +- .../index_data_visualizer.tsx | 9 +- x-pack/plugins/ml/common/constants/app.ts | 20 +++ .../aiops/change_point_detection.tsx | 7 +- .../application/aiops/log_categorization.tsx | 7 +- .../application/aiops/log_rate_analysis.tsx | 7 +- x-pack/plugins/ml/public/application/app.tsx | 26 ++-- .../export_jobs_flyout/export_jobs_flyout.tsx | 4 +- .../import_jobs_flyout/import_jobs_flyout.tsx | 4 +- .../components/job_messages/job_messages.tsx | 8 +- .../components/job_spaces_sync/sync_list.tsx | 4 +- .../jobs_awaiting_node_warning.tsx | 6 +- .../new_job_awaiting_node.tsx | 32 ++--- .../ml_entity_selector/ml_entity_selector.tsx | 8 +- .../components/ml_page/ml_page.tsx | 7 +- .../application/contexts/kibana/index.ts | 1 - .../contexts/kibana/use_is_serverless.ts | 12 -- .../public/application/contexts/ml/index.ts | 1 + .../contexts/ml/serverless_context.tsx | 57 ++++++++ .../pages/job_map/components/controls.tsx | 10 +- .../index_based/index_data_visualizer.tsx | 7 +- .../jobs_list_view/jobs_list_view.js | 6 +- .../jobs_stats_bar/jobs_stats_bar.js | 14 +- .../application/jobs/jobs_list/jobs.tsx | 8 +- .../components/time_range_step/time_range.tsx | 4 +- .../ml/public/application/management/index.ts | 6 +- .../jobs_list_page/jobs_list_page.tsx | 127 +++++++++--------- .../space_management/space_management.tsx | 8 +- .../application/management/jobs_list/index.ts | 7 +- .../memory_usage/memory_tree_map/tree_map.tsx | 8 +- .../memory_usage/memory_usage_page.tsx | 6 +- .../model_management/expanded_row.tsx | 20 +-- .../model_management/models_list.tsx | 4 +- .../components/notifications_list.tsx | 8 +- .../anomaly_detection_panel.tsx | 7 +- .../anomaly_detection_panel/utils.ts | 12 +- .../overview/components/content.tsx | 4 +- .../routing/routes/memory_usage.tsx | 11 +- .../application/routing/routes/overview.tsx | 9 ++ .../anomaly_charts_embeddable_factory.test.ts | 2 +- .../anomaly_charts_embeddable_factory.ts | 5 +- .../anomaly_charts_setup_flyout.tsx | 3 +- ...omaly_swimlane_embeddable_factory.test.tsx | 2 +- .../anomaly_swimlane_embeddable_factory.ts | 5 +- .../anomaly_swimlane_setup_flyout.tsx | 3 +- .../common/resolve_job_selection.tsx | 5 +- x-pack/plugins/ml/public/embeddables/index.ts | 14 +- .../job_creation/common/create_flyout.tsx | 3 +- .../job_creation/lens/show_flyout.tsx | 4 +- .../job_creation/map/show_flyout.tsx | 13 +- x-pack/plugins/ml/public/plugin.ts | 31 +++-- .../register_search_links.ts | 4 +- .../search_deep_links.ts | 10 +- .../edit_anomaly_charts_panel_action.tsx | 4 +- .../ui_actions/edit_swimlane_panel_action.tsx | 9 +- x-pack/plugins/ml/public/ui_actions/index.ts | 15 +-- .../ui_actions/open_vis_in_ml_action.tsx | 22 +-- x-pack/plugins/ml/server/config_schema.ts | 4 +- x-pack/plugins/ml/server/index.ts | 8 +- .../server/lib/alerts/register_ml_alerts.ts | 2 +- .../lib/capabilities/capabilities_switcher.ts | 2 +- .../plugins/ml/server/lib/register_cases.ts | 2 +- .../lib/register_sample_data_set_links.ts | 2 +- .../data_frame_analytics/analytics_manager.ts | 2 +- .../models/model_management/memory_usage.ts | 2 +- .../notifications_service_provider.ts | 2 +- x-pack/plugins/ml/server/plugin.ts | 26 ++-- .../ml/server/routes/data_frame_analytics.ts | 4 +- .../ml/server/routes/trained_models.ts | 9 +- x-pack/plugins/ml/server/types.ts | 3 +- 76 files changed, 398 insertions(+), 378 deletions(-) delete mode 100644 x-pack/plugins/ml/public/application/contexts/kibana/use_is_serverless.ts create mode 100644 x-pack/plugins/ml/public/application/contexts/ml/serverless_context.tsx diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts index f3f6d8b8ac4e9..f47b019176eed 100644 --- a/test/plugin_functional/test_suites/core_plugins/rendering.ts +++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts @@ -258,6 +258,9 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'xpack.license_management.ui.enabled (boolean)', 'xpack.maps.preserveDrawingBuffer (boolean)', 'xpack.maps.showMapsInspectorAdapter (boolean)', + 'xpack.ml.ad.enabled (boolean)', + 'xpack.ml.dfa.enabled (boolean)', + 'xpack.ml.nlp.enabled (boolean)', 'xpack.osquery.actionEnabled (boolean)', 'xpack.remote_clusters.ui.enabled (boolean)', /** diff --git a/x-pack/packages/ml/date_picker/src/components/full_time_range_selector.tsx b/x-pack/packages/ml/date_picker/src/components/full_time_range_selector.tsx index fcf4622e87cc0..20e4ca43e233b 100644 --- a/x-pack/packages/ml/date_picker/src/components/full_time_range_selector.tsx +++ b/x-pack/packages/ml/date_picker/src/components/full_time_range_selector.tsx @@ -72,10 +72,6 @@ export interface FullTimeRangeSelectorProps { * @param value - The time field range response. */ apiPath?: SetFullTimeRangeApiPath; - /** - * Optional flag to disable the frozen data tier choice. - */ - hideFrozenDataTierChoice?: boolean; } /** @@ -96,13 +92,12 @@ export const FullTimeRangeSelector: FC = (props) => disabled, callback, apiPath, - hideFrozenDataTierChoice = false, } = props; const { http, notifications: { toasts }, - isServerless, + showFrozenDataTierChoice, } = useDatePickerContext(); // wrapper around setFullTimeRange to allow for the calling of the optional callBack prop @@ -114,9 +109,7 @@ export const FullTimeRangeSelector: FC = (props) => toasts, http, query, - isServerless || hideFrozenDataTierChoice - ? false - : frozenDataPreference === FROZEN_TIER_PREFERENCE.EXCLUDE, + showFrozenDataTierChoice ? frozenDataPreference === FROZEN_TIER_PREFERENCE.EXCLUDE : false, apiPath ); if (typeof callback === 'function' && fullTimeRange !== undefined) { @@ -138,8 +131,7 @@ export const FullTimeRangeSelector: FC = (props) => toasts, http, query, - isServerless, - hideFrozenDataTierChoice, + showFrozenDataTierChoice, frozenDataPreference, apiPath, callback, @@ -230,7 +222,7 @@ export const FullTimeRangeSelector: FC = (props) => /> - {isServerless || hideFrozenDataTierChoice ? null : ( + {showFrozenDataTierChoice ? ( = (props) => {popoverContent} - )} + ) : null} ); }; diff --git a/x-pack/packages/ml/date_picker/src/hooks/use_date_picker_context.tsx b/x-pack/packages/ml/date_picker/src/hooks/use_date_picker_context.tsx index 60b1c66f95984..49b090ef58983 100644 --- a/x-pack/packages/ml/date_picker/src/hooks/use_date_picker_context.tsx +++ b/x-pack/packages/ml/date_picker/src/hooks/use_date_picker_context.tsx @@ -45,9 +45,9 @@ export interface DatePickerDependencies { */ i18n: I18nStart; /** - * Optional flag to indicate whether kibana is running in serverless + * Optional flag to disable the frozen data tier choice. */ - isServerless?: boolean; + showFrozenDataTierChoice?: boolean; } /** diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx index c18764e45797d..414e214fd1fe7 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx @@ -51,19 +51,19 @@ export interface ChangePointDetectionAppStateProps { /** App dependencies */ appDependencies: AiopsAppDependencies; /** Optional flag to indicate whether kibana is running in serverless */ - isServerless?: boolean; + showFrozenDataTierChoice?: boolean; } export const ChangePointDetectionAppState: FC = ({ dataView, savedSearch, appDependencies, - isServerless = false, + showFrozenDataTierChoice = true, }) => { const datePickerDeps: DatePickerDependencies = { ...pick(appDependencies, ['data', 'http', 'notifications', 'theme', 'uiSettings', 'i18n']), uiSettingsKeys: UI_SETTINGS, - isServerless, + showFrozenDataTierChoice, }; const warning = timeSeriesDataViewWarning(dataView, 'change_point_detection'); diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_app_state.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_app_state.tsx index 5ddf65d5b938d..c6e2600b7e4f4 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_app_state.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_app_state.tsx @@ -36,14 +36,14 @@ export interface LogCategorizationAppStateProps { /** App dependencies */ appDependencies: AiopsAppDependencies; /** Optional flag to indicate whether kibana is running in serverless */ - isServerless?: boolean; + showFrozenDataTierChoice?: boolean; } export const LogCategorizationAppState: FC = ({ dataView, savedSearch, appDependencies, - isServerless = false, + showFrozenDataTierChoice = true, }) => { if (!dataView) return null; @@ -56,7 +56,7 @@ export const LogCategorizationAppState: FC = ({ const datePickerDeps: DatePickerDependencies = { ...pick(appDependencies, ['data', 'http', 'notifications', 'theme', 'uiSettings', 'i18n']), uiSettingsKeys: UI_SETTINGS, - isServerless, + showFrozenDataTierChoice, }; return ( diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_app_state.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_app_state.tsx index 0a41900feb9fb..daa717b4fd410 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_app_state.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_app_state.tsx @@ -41,7 +41,7 @@ export interface LogRateAnalysisAppStateProps { /** Option to make main histogram sticky */ stickyHistogram?: boolean; /** Optional flag to indicate whether kibana is running in serverless */ - isServerless?: boolean; + showFrozenDataTierChoice?: boolean; } export const LogRateAnalysisAppState: FC = ({ @@ -49,7 +49,7 @@ export const LogRateAnalysisAppState: FC = ({ savedSearch, appDependencies, stickyHistogram, - isServerless = false, + showFrozenDataTierChoice = true, }) => { if (!dataView) return null; @@ -62,7 +62,7 @@ export const LogRateAnalysisAppState: FC = ({ const datePickerDeps: DatePickerDependencies = { ...pick(appDependencies, ['data', 'http', 'notifications', 'theme', 'uiSettings', 'i18n']), uiSettingsKeys: UI_SETTINGS, - isServerless, + showFrozenDataTierChoice, }; return ( diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper.tsx index b19e72e7c4b5a..806dc7f0e2cb2 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper.tsx @@ -58,7 +58,7 @@ export interface LogRateAnalysisContentWrapperProps { */ onAnalysisCompleted?: (d: LogRateAnalysisResultsData) => void; /** Optional flag to indicate whether kibana is running in serverless */ - isServerless?: boolean; + showFrozenDataTierChoice?: boolean; } export const LogRateAnalysisContentWrapper: FC = ({ @@ -72,7 +72,7 @@ export const LogRateAnalysisContentWrapper: FC { if (!dataView) return null; @@ -85,6 +85,7 @@ export const LogRateAnalysisContentWrapper: FC = ({ getAdditionalLinks, isServerless = false }) => { +export const IndexDataVisualizer: FC = ({ + getAdditionalLinks, + showFrozenDataTierChoice = true, +}) => { const coreStart = getCoreStart(); const { data, @@ -302,7 +305,7 @@ export const IndexDataVisualizer: FC = ({ getAdditionalLinks, isServerles const datePickerDeps: DatePickerDependencies = { ...pick(services, ['data', 'http', 'notifications', 'theme', 'uiSettings', 'i18n']), uiSettingsKeys: UI_SETTINGS, - isServerless, + showFrozenDataTierChoice, }; return ( diff --git a/x-pack/plugins/ml/common/constants/app.ts b/x-pack/plugins/ml/common/constants/app.ts index 00f6fa7a42d05..3c7aabbedea24 100644 --- a/x-pack/plugins/ml/common/constants/app.ts +++ b/x-pack/plugins/ml/common/constants/app.ts @@ -16,3 +16,23 @@ export const ML_APP_NAME = i18n.translate('xpack.ml.navMenu.mlAppNameText', { export const ML_APP_ROUTE = '/app/ml'; export const ML_INTERNAL_BASE_PATH = '/internal/ml'; export const ML_EXTERNAL_BASE_PATH = '/api/ml'; + +export type MlFeatures = Record<'ad' | 'dfa' | 'nlp', boolean>; + +export interface ConfigSchema { + ad?: { enabled: boolean }; + dfa?: { enabled: boolean }; + nlp?: { enabled: boolean }; +} + +export function initEnabledFeatures(enabledFeatures: MlFeatures, config: ConfigSchema) { + if (config.ad?.enabled !== undefined) { + enabledFeatures.ad = config.ad.enabled; + } + if (config.dfa?.enabled !== undefined) { + enabledFeatures.dfa = config.dfa.enabled; + } + if (config.nlp?.enabled !== undefined) { + enabledFeatures.nlp = config.nlp.enabled; + } +} diff --git a/x-pack/plugins/ml/public/application/aiops/change_point_detection.tsx b/x-pack/plugins/ml/public/application/aiops/change_point_detection.tsx index e0c1728d893a5..774c3ac6f9d2d 100644 --- a/x-pack/plugins/ml/public/application/aiops/change_point_detection.tsx +++ b/x-pack/plugins/ml/public/application/aiops/change_point_detection.tsx @@ -15,15 +15,16 @@ import { ChangePointDetection } from '@kbn/aiops-plugin/public'; import { useDataSource } from '../contexts/ml/data_source_context'; import { useFieldStatsTrigger, FieldStatsFlyoutProvider } from '../components/field_stats_flyout'; -import { useMlKibana, useIsServerless } from '../contexts/kibana'; +import { useMlKibana } from '../contexts/kibana'; import { HelpMenu } from '../components/help_menu'; import { TechnicalPreviewBadge } from '../components/technical_preview_badge'; import { MlPageHeader } from '../components/page_header'; +import { useEnabledFeatures } from '../contexts/ml/serverless_context'; export const ChangePointDetectionPage: FC = () => { const { services } = useMlKibana(); - const isServerless = useIsServerless(); + const { showNodeInfo } = useEnabledFeatures(); const { selectedDataView: dataView, selectedSavedSearch: savedSearch } = useDataSource(); @@ -46,7 +47,7 @@ export const ChangePointDetectionPage: FC = () => { { const { services } = useMlKibana(); - const isServerless = useIsServerless(); + const { showNodeInfo } = useEnabledFeatures(); const { selectedDataView: dataView, selectedSavedSearch: savedSearch } = useDataSource(); @@ -41,7 +42,7 @@ export const LogCategorizationPage: FC = () => { { const { services } = useMlKibana(); - const isServerless = useIsServerless(); + const { showNodeInfo } = useEnabledFeatures(); const { selectedDataView: dataView, selectedSavedSearch: savedSearch } = useDataSource(); @@ -44,7 +45,7 @@ export const LogRateAnalysisPage: FC = () => { stickyHistogram={false} dataView={dataView} savedSearch={savedSearch} - isServerless={isServerless} + showFrozenDataTierChoice={showNodeInfo} appDependencies={pick(services, [ 'application', 'data', diff --git a/x-pack/plugins/ml/public/application/app.tsx b/x-pack/plugins/ml/public/application/app.tsx index 5acc519f242a7..e403222815b9a 100644 --- a/x-pack/plugins/ml/public/application/app.tsx +++ b/x-pack/plugins/ml/public/application/app.tsx @@ -19,6 +19,7 @@ import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-pl import { StorageContextProvider } from '@kbn/ml-local-storage'; import useLifecycles from 'react-use/lib/useLifecycles'; import useObservable from 'react-use/lib/useObservable'; +import type { MlFeatures } from '../../common/constants/app'; import { MlLicense } from '../../common/license'; import { MlCapabilitiesService } from './capabilities/check_capabilities'; import { ML_STORAGE_KEYS } from '../../common/types/storage'; @@ -30,6 +31,7 @@ import { MlRouter } from './routing'; import { mlApiServicesProvider } from './services/ml_api_service'; import { HttpService } from './services/http_service'; import type { PageDependencies } from './routing/router'; +import { EnabledFeaturesContextProvider } from './contexts/ml'; export type MlDependencies = Omit< MlSetupDependencies, @@ -42,6 +44,7 @@ interface AppProps { deps: MlDependencies; appMountParams: AppMountParameters; isServerless: boolean; + mlFeatures: MlFeatures; } const localStorage = new Storage(window.localStorage); @@ -49,11 +52,7 @@ const localStorage = new Storage(window.localStorage); /** * Provides global services available across the entire ML app. */ -export function getMlGlobalServices( - httpStart: HttpStart, - isServerless: boolean, - usageCollection?: UsageCollectionSetup -) { +export function getMlGlobalServices(httpStart: HttpStart, usageCollection?: UsageCollectionSetup) { const httpService = new HttpService(httpStart); const mlApiServices = mlApiServicesProvider(httpService); @@ -63,7 +62,6 @@ export function getMlGlobalServices( mlUsageCollection: mlUsageCollectionProvider(usageCollection), mlCapabilities: new MlCapabilitiesService(mlApiServices), mlLicense: new MlLicense(), - isServerless, }; } @@ -73,7 +71,7 @@ export interface MlServicesContext { export type MlGlobalServices = ReturnType; -const App: FC = ({ coreStart, deps, appMountParams, isServerless }) => { +const App: FC = ({ coreStart, deps, appMountParams, isServerless, mlFeatures }) => { const pageDeps: PageDependencies = { history: appMountParams.history, setHeaderActionMenu: appMountParams.setHeaderActionMenu, @@ -106,9 +104,9 @@ const App: FC = ({ coreStart, deps, appMountParams, isServerless }) => contentManagement: deps.contentManagement, presentationUtil: deps.presentationUtil, ...coreStart, - mlServices: getMlGlobalServices(coreStart.http, isServerless, deps.usageCollection), + mlServices: getMlGlobalServices(coreStart.http, deps.usageCollection), }; - }, [deps, coreStart, isServerless]); + }, [deps, coreStart]); useLifecycles( function setupLicenseOnMount() { @@ -132,7 +130,7 @@ const App: FC = ({ coreStart, deps, appMountParams, isServerless }) => const datePickerDeps: DatePickerDependencies = { ...pick(services, ['data', 'http', 'notifications', 'theme', 'uiSettings', 'i18n']), uiSettingsKeys: UI_SETTINGS, - isServerless, + showFrozenDataTierChoice: !isServerless, }; const I18nContext = coreStart.i18n.Context; @@ -146,7 +144,9 @@ const App: FC = ({ coreStart, deps, appMountParams, isServerless }) => - + + + @@ -160,7 +160,8 @@ export const renderApp = ( coreStart: CoreStart, deps: MlDependencies, appMountParams: AppMountParameters, - isServerless: boolean + isServerless: boolean, + mlFeatures: MlFeatures ) => { setDependencyCache({ timefilter: deps.data.query.timefilter, @@ -194,6 +195,7 @@ export const renderApp = ( deps={deps} appMountParams={appMountParams} isServerless={isServerless} + mlFeatures={mlFeatures} />, appMountParams.element ); diff --git a/x-pack/plugins/ml/public/application/components/import_export_jobs/export_jobs_flyout/export_jobs_flyout.tsx b/x-pack/plugins/ml/public/application/components/import_export_jobs/export_jobs_flyout/export_jobs_flyout.tsx index 5bf0e73b2cee1..396f2d000290c 100644 --- a/x-pack/plugins/ml/public/application/components/import_export_jobs/export_jobs_flyout/export_jobs_flyout.tsx +++ b/x-pack/plugins/ml/public/application/components/import_export_jobs/export_jobs_flyout/export_jobs_flyout.tsx @@ -32,7 +32,7 @@ import { JobsExportService } from './jobs_export_service'; import type { JobDependencies } from './jobs_export_service'; import { toastNotificationServiceProvider } from '../../../services/toast_notification_service'; import type { JobType } from '../../../../../common/types/saved_objects'; -import { usePermissionCheck } from '../../../capabilities/check_capabilities'; +import { useEnabledFeatures } from '../../../contexts/ml'; interface Props { isDisabled: boolean; @@ -68,7 +68,7 @@ export const ExportJobsFlyout: FC = ({ isDisabled, currentTab }) => { () => toastNotificationServiceProvider(toasts), [toasts] ); - const [isADEnabled, isDFAEnabled] = usePermissionCheck(['isADEnabled', 'isDFAEnabled']); + const { isADEnabled, isDFAEnabled } = useEnabledFeatures(); const [jobDependencies, setJobDependencies] = useState([]); const [selectedJobDependencies, setSelectedJobDependencies] = useState([]); diff --git a/x-pack/plugins/ml/public/application/components/import_export_jobs/import_jobs_flyout/import_jobs_flyout.tsx b/x-pack/plugins/ml/public/application/components/import_export_jobs/import_jobs_flyout/import_jobs_flyout.tsx index 61e6df17676cb..d6f616bed93ef 100644 --- a/x-pack/plugins/ml/public/application/components/import_export_jobs/import_jobs_flyout/import_jobs_flyout.tsx +++ b/x-pack/plugins/ml/public/application/components/import_export_jobs/import_jobs_flyout/import_jobs_flyout.tsx @@ -40,7 +40,7 @@ import { toastNotificationServiceProvider } from '../../../services/toast_notifi import { JobImportService } from './jobs_import_service'; import { useValidateIds } from './validate'; import type { ImportedAdJob, JobIdObject, SkippedJobs } from './jobs_import_service'; -import { usePermissionCheck } from '../../../capabilities/check_capabilities'; +import { useEnabledFeatures } from '../../../contexts/ml'; interface Props { isDisabled: boolean; @@ -82,7 +82,7 @@ export const ImportJobsFlyout: FC = ({ isDisabled }) => { () => toastNotificationServiceProvider(toasts), [toasts] ); - const [isADEnabled, isDFAEnabled] = usePermissionCheck(['isADEnabled', 'isDFAEnabled']); + const { isADEnabled, isDFAEnabled } = useEnabledFeatures(); const [validateIds] = useValidateIds( jobType, diff --git a/x-pack/plugins/ml/public/application/components/job_messages/job_messages.tsx b/x-pack/plugins/ml/public/application/components/job_messages/job_messages.tsx index 20faeaf0bdfd2..c5a7826ac0ee9 100644 --- a/x-pack/plugins/ml/public/application/components/job_messages/job_messages.tsx +++ b/x-pack/plugins/ml/public/application/components/job_messages/job_messages.tsx @@ -25,7 +25,7 @@ import { JobMessage } from '../../../../common/types/audit_message'; import { blurButtonOnClick } from '../../util/component_utils'; import { JobIcon } from '../job_message_icon'; -import { useIsServerless } from '../../contexts/kibana'; +import { useEnabledFeatures } from '../../contexts/ml'; interface JobMessagesProps { messages: JobMessage[]; @@ -46,7 +46,7 @@ export const JobMessages: FC = ({ refreshMessage, actionHandler, }) => { - const isServerless = useIsServerless(); + const { showNodeInfo } = useEnabledFeatures(); const columns: Array> = useMemo(() => { const cols = [ { @@ -90,7 +90,7 @@ export const JobMessages: FC = ({ }, ]; - if (isServerless === false) { + if (showNodeInfo) { cols.splice(2, 0, { field: 'node_name', name: i18n.translate('xpack.ml.jobMessages.nodeLabel', { @@ -101,7 +101,7 @@ export const JobMessages: FC = ({ } return cols; - }, [isServerless, refreshMessage]); + }, [showNodeInfo, refreshMessage]); if (typeof actionHandler === 'function') { columns.push({ diff --git a/x-pack/plugins/ml/public/application/components/job_spaces_sync/sync_list.tsx b/x-pack/plugins/ml/public/application/components/job_spaces_sync/sync_list.tsx index 211dc1aa8c57b..550b93deb5e5e 100644 --- a/x-pack/plugins/ml/public/application/components/job_spaces_sync/sync_list.tsx +++ b/x-pack/plugins/ml/public/application/components/job_spaces_sync/sync_list.tsx @@ -18,10 +18,10 @@ import { } from '@elastic/eui'; import type { SyncSavedObjectResponse, SyncResult } from '../../../../common/types/saved_objects'; -import { usePermissionCheck } from '../../capabilities/check_capabilities'; +import { useEnabledFeatures } from '../../contexts/ml'; export const SyncList: FC<{ syncItems: SyncSavedObjectResponse | null }> = ({ syncItems }) => { - const [isADEnabled] = usePermissionCheck(['isADEnabled']); + const { isADEnabled } = useEnabledFeatures(); if (syncItems === null) { return null; diff --git a/x-pack/plugins/ml/public/application/components/jobs_awaiting_node_warning/jobs_awaiting_node_warning.tsx b/x-pack/plugins/ml/public/application/components/jobs_awaiting_node_warning/jobs_awaiting_node_warning.tsx index a85776aeacc82..336e707c32499 100644 --- a/x-pack/plugins/ml/public/application/components/jobs_awaiting_node_warning/jobs_awaiting_node_warning.tsx +++ b/x-pack/plugins/ml/public/application/components/jobs_awaiting_node_warning/jobs_awaiting_node_warning.tsx @@ -10,15 +10,15 @@ import React, { FC } from 'react'; import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { lazyMlNodesAvailable } from '../../ml_nodes_check'; -import { useIsServerless } from '../../contexts/kibana'; +import { useEnabledFeatures } from '../../contexts/ml'; interface Props { jobCount: number; } export const JobsAwaitingNodeWarning: FC = ({ jobCount }) => { - const isServerless = useIsServerless(); - if (isServerless || lazyMlNodesAvailable() === false || jobCount === 0) { + const { showNodeInfo } = useEnabledFeatures(); + if (showNodeInfo === false || lazyMlNodesAvailable() === false || jobCount === 0) { return null; } diff --git a/x-pack/plugins/ml/public/application/components/jobs_awaiting_node_warning/new_job_awaiting_node.tsx b/x-pack/plugins/ml/public/application/components/jobs_awaiting_node_warning/new_job_awaiting_node.tsx index 5055efad210f0..e120a068d0ddd 100644 --- a/x-pack/plugins/ml/public/application/components/jobs_awaiting_node_warning/new_job_awaiting_node.tsx +++ b/x-pack/plugins/ml/public/application/components/jobs_awaiting_node_warning/new_job_awaiting_node.tsx @@ -11,30 +11,37 @@ import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { JobType } from '../../../../common/types/saved_objects'; import { lazyMlNodesAvailable } from '../../ml_nodes_check'; -import { useIsServerless } from '../../contexts/kibana'; +import { useEnabledFeatures } from '../../contexts/ml'; interface Props { jobType: JobType; } export const NewJobAwaitingNodeWarning: FC = () => { - const isServerless = useIsServerless(); + const { showNodeInfo } = useEnabledFeatures(); if (lazyMlNodesAvailable() === false) { return null; } - return isServerless ? ( + return showNodeInfo ? ( <> } color="primary" iconType="iInCircle" - /> + > +
+ +
+
) : ( @@ -42,20 +49,13 @@ export const NewJobAwaitingNodeWarning: FC = () => { } color="primary" iconType="iInCircle" - > -
- -
-
+ /> ); diff --git a/x-pack/plugins/ml/public/application/components/ml_entity_selector/ml_entity_selector.tsx b/x-pack/plugins/ml/public/application/components/ml_entity_selector/ml_entity_selector.tsx index 22c14ff7d72a9..a23daa689e447 100644 --- a/x-pack/plugins/ml/public/application/components/ml_entity_selector/ml_entity_selector.tsx +++ b/x-pack/plugins/ml/public/application/components/ml_entity_selector/ml_entity_selector.tsx @@ -17,7 +17,7 @@ import { countBy } from 'lodash'; import useMount from 'react-use/lib/useMount'; import { useMlApiContext } from '../../contexts/kibana'; import { useToastNotificationService } from '../../services/toast_notification_service'; -import { usePermissionCheck } from '../../capabilities/check_capabilities'; +import { useEnabledFeatures } from '../../contexts/ml'; type EntityType = 'anomaly_detector' | 'data_frame_analytics' | 'trained_models'; @@ -61,11 +61,7 @@ export const MlEntitySelector: FC = ({ onSelectionChange, handleDuplicates = false, }) => { - const [isADEnabled, isDFAEnabled, isNLPEnabled] = usePermissionCheck([ - 'isADEnabled', - 'isDFAEnabled', - 'isNLPEnabled', - ]); + const { isADEnabled, isDFAEnabled, isNLPEnabled } = useEnabledFeatures(); const { jobs: jobsApi, trainedModels, dataFrameAnalytics } = useMlApiContext(); const { displayErrorToast } = useToastNotificationService(); const visColorsBehindText = euiPaletteColorBlindBehindText(); diff --git a/x-pack/plugins/ml/public/application/components/ml_page/ml_page.tsx b/x-pack/plugins/ml/public/application/components/ml_page/ml_page.tsx index 3eb1deb83e7ae..982ee749cc834 100644 --- a/x-pack/plugins/ml/public/application/components/ml_page/ml_page.tsx +++ b/x-pack/plugins/ml/public/application/components/ml_page/ml_page.tsx @@ -20,7 +20,7 @@ import { DatePickerWrapper } from '@kbn/ml-date-picker'; import * as routes from '../../routing/routes'; import { MlPageWrapper } from '../../routing/ml_page_wrapper'; -import { useMlKibana, useNavigateToPath, useIsServerless } from '../../contexts/kibana'; +import { useMlKibana, useNavigateToPath } from '../../contexts/kibana'; import type { MlRoute, PageDependencies } from '../../routing/router'; import { useActiveRoute } from '../../routing/use_active_route'; import { useDocTitle } from '../../routing/use_doc_title'; @@ -28,6 +28,7 @@ import { useDocTitle } from '../../routing/use_doc_title'; import { MlPageHeaderRenderer } from '../page_header/page_header'; import { useSideNavItems } from './side_nav'; +import { useEnabledFeatures } from '../../contexts/ml'; const ML_APP_SELECTOR = '[data-test-subj="mlApp"]'; @@ -55,7 +56,7 @@ export const MlPage: FC<{ pageDeps: PageDependencies }> = React.memo(({ pageDeps mlServices: { httpService }, }, } = useMlKibana(); - const isServerless = useIsServerless(); + const { showMLNavMenu } = useEnabledFeatures(); const headerPortalNode = useMemo(() => createHtmlPortalNode(), []); const [isHeaderMounted, setIsHeaderMounted] = useState(false); @@ -127,7 +128,7 @@ export const MlPage: FC<{ pageDeps: PageDependencies }> = React.memo(({ pageDeps data-test-subj={'mlApp'} restrictWidth={false} solutionNav={ - isServerless === false + showMLNavMenu ? { name: i18n.translate('xpack.ml.plugin.title', { defaultMessage: 'Machine Learning', diff --git a/x-pack/plugins/ml/public/application/contexts/kibana/index.ts b/x-pack/plugins/ml/public/application/contexts/kibana/index.ts index afedbdef15b6c..e8696e9b49068 100644 --- a/x-pack/plugins/ml/public/application/contexts/kibana/index.ts +++ b/x-pack/plugins/ml/public/application/contexts/kibana/index.ts @@ -16,4 +16,3 @@ export { useMlApiContext } from './use_ml_api_context'; export { useFieldFormatter } from './use_field_formatter'; export { useCurrentThemeVars } from './use_current_theme'; export { useMlLicenseInfo } from './use_ml_license'; -export { useIsServerless } from './use_is_serverless'; diff --git a/x-pack/plugins/ml/public/application/contexts/kibana/use_is_serverless.ts b/x-pack/plugins/ml/public/application/contexts/kibana/use_is_serverless.ts deleted file mode 100644 index ca98dd44c17ce..0000000000000 --- a/x-pack/plugins/ml/public/application/contexts/kibana/use_is_serverless.ts +++ /dev/null @@ -1,12 +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 { useMlKibana } from './kibana_context'; - -export function useIsServerless(): boolean { - return useMlKibana().services.mlServices.isServerless; -} diff --git a/x-pack/plugins/ml/public/application/contexts/ml/index.ts b/x-pack/plugins/ml/public/application/contexts/ml/index.ts index eb51ff6186fe6..d5935bdc2ad97 100644 --- a/x-pack/plugins/ml/public/application/contexts/ml/index.ts +++ b/x-pack/plugins/ml/public/application/contexts/ml/index.ts @@ -6,3 +6,4 @@ */ export { DataSourceContextProvider, useDataSource } from './data_source_context'; +export { EnabledFeaturesContextProvider, useEnabledFeatures } from './serverless_context'; diff --git a/x-pack/plugins/ml/public/application/contexts/ml/serverless_context.tsx b/x-pack/plugins/ml/public/application/contexts/ml/serverless_context.tsx new file mode 100644 index 0000000000000..3662a817a03da --- /dev/null +++ b/x-pack/plugins/ml/public/application/contexts/ml/serverless_context.tsx @@ -0,0 +1,57 @@ +/* + * 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, { createContext, FC, useContext, useMemo } from 'react'; +import type { MlFeatures } from '../../../../common/constants/app'; + +export interface EnabledFeatures { + showNodeInfo: boolean; + showMLNavMenu: boolean; + showLicenseInfo: boolean; + isADEnabled: boolean; + isDFAEnabled: boolean; + isNLPEnabled: boolean; +} +export const EnabledFeaturesContext = createContext({ + showNodeInfo: true, + showMLNavMenu: true, + showLicenseInfo: true, + isADEnabled: true, + isDFAEnabled: true, + isNLPEnabled: true, +}); + +interface Props { + isServerless: boolean; + mlFeatures: MlFeatures; +} + +export const EnabledFeaturesContextProvider: FC = ({ + children, + isServerless, + mlFeatures, +}) => { + const features: EnabledFeatures = { + showNodeInfo: !isServerless, + showMLNavMenu: !isServerless, + showLicenseInfo: !isServerless, + isADEnabled: mlFeatures.ad, + isDFAEnabled: mlFeatures.dfa, + isNLPEnabled: mlFeatures.nlp, + }; + + return ( + {children} + ); +}; + +export function useEnabledFeatures() { + const context = useContext(EnabledFeaturesContext); + return useMemo(() => { + return context; + }, [context]); +} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/controls.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/controls.tsx index 1df5180934fc4..82695b39e0066 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/controls.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/controls.tsx @@ -37,8 +37,8 @@ import { useNotifications, useNavigateToPath, useMlKibana, - useIsServerless, } from '../../../../contexts/kibana'; +import { useEnabledFeatures } from '../../../../contexts/ml'; import { getDataViewIdFromName } from '../../../../util/index_utils'; import { useNavigateToWizardWithClonedJob } from '../../analytics_management/components/action_clone/clone_action_name'; import { @@ -55,9 +55,9 @@ interface Props { refreshJobsCallback: () => void; } -function getListItemsFactory(isServerless: boolean) { +function getListItemsFactory(showLicenseInfo: boolean) { return (details: Record): EuiDescriptionListProps['listItems'] => { - if (isServerless) { + if (showLicenseInfo === false) { delete details.license_level; } @@ -94,8 +94,8 @@ export const Controls: FC = React.memo( const canCreateDataFrameAnalytics: boolean = usePermissionCheck('canCreateDataFrameAnalytics'); const canDeleteDataFrameAnalytics: boolean = usePermissionCheck('canDeleteDataFrameAnalytics'); const deleteAction = useDeleteAction(canDeleteDataFrameAnalytics); - const isServerless = useIsServerless(); - const getListItems = useMemo(() => getListItemsFactory(isServerless), [isServerless]); + const { showLicenseInfo } = useEnabledFeatures(); + const getListItems = useMemo(() => getListItemsFactory(showLicenseInfo), [showLicenseInfo]); const { closeDeleteJobCheckModal, diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/index_data_visualizer.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/index_data_visualizer.tsx index 3debbda8021c3..69c034738cf5f 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/index_data_visualizer.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/index_data_visualizer.tsx @@ -15,13 +15,14 @@ import type { GetAdditionalLinksParams, } from '@kbn/data-visualizer-plugin/public'; import { useTimefilter } from '@kbn/ml-date-picker'; -import { useMlKibana, useMlLocator, useIsServerless } from '../../contexts/kibana'; +import { useMlKibana, useMlLocator } from '../../contexts/kibana'; import { HelpMenu } from '../../components/help_menu'; import { ML_PAGES } from '../../../../common/constants/locator'; import { isFullLicense } from '../../license'; import { mlNodesAvailable, getMlNodeCount } from '../../ml_nodes_check/check_ml_nodes'; import { checkPermission } from '../../capabilities/check_capabilities'; import { MlPageHeader } from '../../components/page_header'; +import { useEnabledFeatures } from '../../contexts/ml'; export const IndexDataVisualizerPage: FC = () => { useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false }); @@ -37,7 +38,7 @@ export const IndexDataVisualizerPage: FC = () => { }, }, } = useMlKibana(); - const isServerless = useIsServerless(); + const { showNodeInfo } = useEnabledFeatures(); const mlLocator = useMlLocator()!; const mlFeaturesDisabled = !isFullLicense(); getMlNodeCount(); @@ -191,7 +192,7 @@ export const IndexDataVisualizerPage: FC = () => { ) : null} diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js index dcecda5575d87..23d20bf6aa529 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js @@ -137,7 +137,7 @@ export class JobsListView extends Component { loadFullJob(jobId) .then((job) => { const fullJobsList = { ...this.state.fullJobsList }; - if (this.props.isServerless) { + if (this.props.showNodeInfo === false) { job = removeNodeInfo(job); } fullJobsList[jobId] = job; @@ -318,7 +318,7 @@ export class JobsListView extends Component { const fullJobsList = {}; const jobsSummaryList = jobs.map((job) => { if (job.fullJob !== undefined) { - if (this.props.isServerless) { + if (this.props.showNodeInfo === false) { job.fullJob = removeNodeInfo(job.fullJob); } fullJobsList[job.id] = job.fullJob; @@ -417,7 +417,7 @@ export class JobsListView extends Component { diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js index c8b2868267d80..bebb342b2939b 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js @@ -12,11 +12,9 @@ import PropTypes from 'prop-types'; import React from 'react'; import { i18n } from '@kbn/i18n'; -function createJobStats(jobsSummaryList, isServerless) { - const displayNodeInfo = isServerless === false; - +function createJobStats(jobsSummaryList, showNodeInfo) { const jobStats = { - ...(displayNodeInfo + ...(showNodeInfo ? { activeNodes: { label: i18n.translate('xpack.ml.jobsList.statsBar.activeMLNodesLabel', { @@ -100,20 +98,20 @@ function createJobStats(jobsSummaryList, isServerless) { jobStats.failed.show = false; } - if (displayNodeInfo) { + if (showNodeInfo) { jobStats.activeNodes.value = Object.keys(mlNodes).length; } return jobStats; } -export const JobStatsBar = ({ jobsSummaryList, isServerless }) => { - const jobStats = createJobStats(jobsSummaryList, isServerless); +export const JobStatsBar = ({ jobsSummaryList, showNodeInfo }) => { + const jobStats = createJobStats(jobsSummaryList, showNodeInfo); return ; }; JobStatsBar.propTypes = { jobsSummaryList: PropTypes.array.isRequired, - isServerless: PropTypes.bool.isRequired, + showNodeInfo: PropTypes.bool.isRequired, }; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/jobs.tsx b/x-pack/plugins/ml/public/application/jobs/jobs_list/jobs.tsx index f413b97cb3602..a34ed51eab0ad 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/jobs.tsx +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/jobs.tsx @@ -12,10 +12,11 @@ import { JobsListView } from './components/jobs_list_view'; import { ML_PAGES } from '../../../../common/constants/locator'; import { ListingPageUrlState } from '../../../../common/types/common'; import { HelpMenu } from '../../components/help_menu'; -import { useIsServerless, useMlKibana } from '../../contexts/kibana'; +import { useMlKibana } from '../../contexts/kibana'; import { MlPageHeader } from '../../components/page_header'; import { HeaderMenuPortal } from '../../components/header_menu_portal'; import { JobsActionMenu } from '../components/jobs_action_menu'; +import { useEnabledFeatures } from '../../contexts/ml'; interface PageUrlState { pageKey: typeof ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE; @@ -42,7 +43,8 @@ export const JobsPage: FC = ({ isMlEnabledInSpace, lastRefresh }) const { services: { docLinks }, } = useMlKibana(); - const isServerless = useIsServerless(); + + const { showNodeInfo } = useEnabledFeatures(); const helpLink = docLinks.links.ml.anomalyDetection; return ( <> @@ -57,7 +59,7 @@ export const JobsPage: FC = ({ isMlEnabledInSpace, lastRefresh }) lastRefresh={lastRefresh} jobsViewState={pageState} onJobsViewStateUpdate={setPageState} - isServerless={isServerless} + showNodeInfo={showNodeInfo} /> diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/time_range_step/time_range.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/time_range_step/time_range.tsx index d9fa9e542c0c5..d93eb690c31d9 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/time_range_step/time_range.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/time_range_step/time_range.tsx @@ -22,7 +22,7 @@ import { EventRateChart } from '../charts/event_rate_chart'; import { LineChartPoint } from '../../../common/chart_loader'; import { JOB_TYPE } from '../../../../../../../common/constants/new_job'; import { TimeRangePicker, TimeRange } from '../../../common/components'; -import { useMlKibana, useIsServerless } from '../../../../../contexts/kibana'; +import { useMlKibana } from '../../../../../contexts/kibana'; import { ML_FROZEN_TIER_PREFERENCE, type MlStorageKey, @@ -33,7 +33,6 @@ export const TimeRangeStep: FC = ({ setCurrentStep, isCurrentStep }) const timefilter = useTimefilter(); const { services } = useMlKibana(); const dataSourceContext = useDataSource(); - const isServerless = useIsServerless(); const { jobCreator, jobCreatorUpdate, jobCreatorUpdated, chartLoader, chartInterval } = useContext(JobCreatorContext); @@ -138,7 +137,6 @@ export const TimeRangeStep: FC = ({ setCurrentStep, isCurrentStep }) callback={fullTimeRangeCallback} timefilter={timefilter} apiPath={`${ML_INTERNAL_BASE_PATH}/fields_service/time_field_range`} - hideFrozenDataTierChoice={isServerless} /> diff --git a/x-pack/plugins/ml/public/application/management/index.ts b/x-pack/plugins/ml/public/application/management/index.ts index dea3ba6cd8afa..7f8030f4e7ddb 100644 --- a/x-pack/plugins/ml/public/application/management/index.ts +++ b/x-pack/plugins/ml/public/application/management/index.ts @@ -11,13 +11,15 @@ import type { CoreSetup } from '@kbn/core/public'; import type { ManagementSetup } from '@kbn/management-plugin/public'; import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; import type { ManagementAppMountParams } from '@kbn/management-plugin/public'; +import type { MlFeatures } from '../../../common/constants/app'; import type { MlStartDependencies } from '../../plugin'; export function registerManagementSection( management: ManagementSetup, core: CoreSetup, deps: { usageCollection?: UsageCollectionSetup }, - isServerless: boolean + isServerless: boolean, + mlFeatures: MlFeatures ) { return management.sections.section.insightsAndAlerting.registerApp({ id: 'jobsListLink', @@ -27,7 +29,7 @@ export function registerManagementSection( order: 4, async mount(params: ManagementAppMountParams) { const { mountApp } = await import('./jobs_list'); - return mountApp(core, params, deps, isServerless); + return mountApp(core, params, deps, isServerless, mlFeatures); }, }); } diff --git a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx index 2cf92278cfa63..1627fbc13e497 100644 --- a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx +++ b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx @@ -30,7 +30,8 @@ import { import type { SharePluginStart } from '@kbn/share-plugin/public'; import type { SpacesContextProps, SpacesPluginStart } from '@kbn/spaces-plugin/public'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; -import { PLUGIN_ID } from '../../../../../../common/constants/app'; +import { EnabledFeaturesContextProvider } from '../../../../contexts/ml'; +import { type MlFeatures, PLUGIN_ID } from '../../../../../../common/constants/app'; import { checkGetManagementMlJobsResolver } from '../../../../capabilities/check_capabilities'; @@ -46,7 +47,7 @@ import { DocsLink } from './docs_link'; const getEmptyFunctionComponent: React.FC = ({ children }) => <>{children}; -export const JobsListPage: FC<{ +interface Props { coreStart: CoreStart; share: SharePluginStart; history: ManagementAppMountParams['history']; @@ -55,7 +56,10 @@ export const JobsListPage: FC<{ usageCollection?: UsageCollectionSetup; fieldFormats: FieldFormatsStart; isServerless: boolean; -}> = ({ + mlFeatures: MlFeatures; +} + +export const JobsListPage: FC = ({ coreStart, share, history, @@ -64,6 +68,7 @@ export const JobsListPage: FC<{ usageCollection, fieldFormats, isServerless, + mlFeatures, }) => { const [initialized, setInitialized] = useState(false); const [accessDenied, setAccessDenied] = useState(false); @@ -74,8 +79,8 @@ export const JobsListPage: FC<{ const theme$ = coreStart.theme.theme$; const mlServices = useMemo( - () => getMlGlobalServices(coreStart.http, isServerless, usageCollection), - [coreStart.http, isServerless, usageCollection] + () => getMlGlobalServices(coreStart.http, usageCollection), + [coreStart.http, usageCollection] ); const check = async () => { @@ -134,62 +139,64 @@ export const JobsListPage: FC<{ }} > - - - } - description={ - - } - rightSideItems={[]} - bottomBorder - paddingSize={'none'} - /> - - - - - - - <> - setShowSyncFlyout(true)} - data-test-subj="mlStackMgmtSyncButton" - > - {i18n.translate('xpack.ml.management.jobsList.syncFlyoutButton', { - defaultMessage: 'Synchronize saved objects', - })} - - {showSyncFlyout && } - - - - - + + + } + description={ + - - - - - - - - + } + rightSideItems={[]} + bottomBorder + paddingSize={'none'} + /> + + + + + + + <> + setShowSyncFlyout(true)} + data-test-subj="mlStackMgmtSyncButton" + > + {i18n.translate('xpack.ml.management.jobsList.syncFlyoutButton', { + defaultMessage: 'Synchronize saved objects', + })} + + {showSyncFlyout && } + + + + + + + + + + + + + + diff --git a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/space_management/space_management.tsx b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/space_management/space_management.tsx index bb21b971a8c12..afd59431e3734 100644 --- a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/space_management/space_management.tsx +++ b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/space_management/space_management.tsx @@ -21,7 +21,7 @@ import { import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import { useTableState } from '@kbn/ml-in-memory-table'; -import { usePermissionCheck } from '../../../../../capabilities/check_capabilities'; +import { useEnabledFeatures } from '../../../../../contexts/ml'; import type { JobType, MlSavedObjectType } from '../../../../../../../common/types/saved_objects'; import type { ManagementListResponse, @@ -46,11 +46,7 @@ export const SpaceManagement: FC = ({ spacesApi, setCurrentTab }) => { const [filters, setFilters] = useState(); const [isLoading, setIsLoading] = useState(false); - const [isADEnabled, isDFAEnabled, isNLPEnabled] = usePermissionCheck([ - 'isADEnabled', - 'isDFAEnabled', - 'isNLPEnabled', - ]); + const { isADEnabled, isDFAEnabled, isNLPEnabled } = useEnabledFeatures(); const { onTableChange, pagination, sorting, setPageIndex } = useTableState( items ?? [], diff --git a/x-pack/plugins/ml/public/application/management/jobs_list/index.ts b/x-pack/plugins/ml/public/application/management/jobs_list/index.ts index 2e512e2d708bf..2c4c3bef4e1c5 100644 --- a/x-pack/plugins/ml/public/application/management/jobs_list/index.ts +++ b/x-pack/plugins/ml/public/application/management/jobs_list/index.ts @@ -14,6 +14,7 @@ import type { ManagementAppMountParams } from '@kbn/management-plugin/public'; import type { SharePluginStart } from '@kbn/share-plugin/public'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import type { MlFeatures } from '../../../../common/constants/app'; import type { MlStartDependencies } from '../../../plugin'; import { JobsListPage } from './components'; import { getJobsListBreadcrumbs } from '../breadcrumbs'; @@ -26,6 +27,7 @@ const renderApp = ( data: DataPublicPluginStart, fieldFormats: FieldFormatsStart, isServerless: boolean, + mlFeatures: MlFeatures, spacesApi?: SpacesPluginStart, usageCollection?: UsageCollectionSetup ) => { @@ -39,6 +41,7 @@ const renderApp = ( usageCollection, fieldFormats, isServerless, + mlFeatures, }), element ); @@ -51,7 +54,8 @@ export async function mountApp( core: CoreSetup, params: ManagementAppMountParams, deps: { usageCollection?: UsageCollectionSetup }, - isServerless: boolean + isServerless: boolean, + mlFeatures: MlFeatures ) { const [coreStart, pluginsStart] = await core.getStartServices(); @@ -64,6 +68,7 @@ export async function mountApp( pluginsStart.data, pluginsStart.fieldFormats, isServerless, + mlFeatures, pluginsStart.spaces, deps.usageCollection ); diff --git a/x-pack/plugins/ml/public/application/memory_usage/memory_tree_map/tree_map.tsx b/x-pack/plugins/ml/public/application/memory_usage/memory_tree_map/tree_map.tsx index f134b2e5fbb83..b236e2cb33a83 100644 --- a/x-pack/plugins/ml/public/application/memory_usage/memory_tree_map/tree_map.tsx +++ b/x-pack/plugins/ml/public/application/memory_usage/memory_tree_map/tree_map.tsx @@ -29,7 +29,7 @@ import { useFieldFormatter, useMlKibana } from '../../contexts/kibana'; import { useRefresh } from '../../routing/use_refresh'; import { getMemoryItemColor } from '../memory_item_colors'; import { useToastNotificationService } from '../../services/toast_notification_service'; -import { usePermissionCheck } from '../../capabilities/check_capabilities'; +import { useEnabledFeatures } from '../../contexts/ml'; interface Props { node?: string; @@ -73,11 +73,7 @@ export const JobMemoryTreeMap: FC = ({ node, type, height }) => { [isDarkTheme] ); - const [isADEnabled, isDFAEnabled, isNLPEnabled] = usePermissionCheck([ - 'isADEnabled', - 'isDFAEnabled', - 'isNLPEnabled', - ]); + const { isADEnabled, isDFAEnabled, isNLPEnabled } = useEnabledFeatures(); const bytesFormatter = useFieldFormatter(FIELD_FORMAT_IDS.BYTES); const { displayErrorToast } = useToastNotificationService(); diff --git a/x-pack/plugins/ml/public/application/memory_usage/memory_usage_page.tsx b/x-pack/plugins/ml/public/application/memory_usage/memory_usage_page.tsx index 249d9c959809d..5d03fb8315c53 100644 --- a/x-pack/plugins/ml/public/application/memory_usage/memory_usage_page.tsx +++ b/x-pack/plugins/ml/public/application/memory_usage/memory_usage_page.tsx @@ -13,7 +13,7 @@ import { NodesList } from './nodes_overview'; import { MlPageHeader } from '../components/page_header'; import { MemoryPage, JobMemoryTreeMap } from './memory_tree_map'; import { SavedObjectsWarning } from '../components/saved_objects_warning'; -import { useIsServerless } from '../contexts/kibana'; +import { useEnabledFeatures } from '../contexts/ml'; enum TAB { NODES, @@ -24,7 +24,7 @@ export const MemoryUsagePage: FC = () => { const [selectedTab, setSelectedTab] = useState(TAB.NODES); useTimefilter({ timeRangeSelector: false, autoRefreshSelector: true }); - const isServerless = useIsServerless(); + const { showNodeInfo } = useEnabledFeatures(); const refresh = useCallback(() => { mlTimefilterRefresh$.next({ @@ -47,7 +47,7 @@ export const MemoryUsagePage: FC = () => { - {isServerless === false ? ( + {showNodeInfo ? ( <> = ({ item }) => { const formatToListItems = useListItemsFormatter(); - const isServerless = useIsServerless(); + const { showLicenseInfo, showNodeInfo } = useEnabledFeatures(); const { inference_config: inferenceConfig, @@ -151,17 +151,17 @@ export const ExpandedRow: FC = ({ item }) => { estimated_operations, estimated_heap_memory_usage_bytes, default_field_map, - ...(isServerless ? {} : { license_level }), + ...(showLicenseInfo ? { license_level } : {}), }; }, [ - default_field_map, description, - estimated_heap_memory_usage_bytes, - estimated_operations, - license_level, tags, version, - isServerless, + estimated_operations, + estimated_heap_memory_usage_bytes, + default_field_map, + showLicenseInfo, + license_level, ]); const deploymentStatItems: AllocatedModel[] = useMemo(() => { @@ -200,8 +200,8 @@ export const ExpandedRow: FC = ({ item }) => { }, [stats]); const hideColumns = useMemo(() => { - return isServerless ? ['model_id', 'node_name'] : ['model_id']; - }, [isServerless]); + return showNodeInfo ? ['model_id'] : ['model_id', 'node_name']; + }, [showNodeInfo]); const tabs = useMemo(() => { return [ diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index 1ce6fa18fbc3f..b959c1e234064 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -63,7 +63,7 @@ import { useRefresh } from '../routing/use_refresh'; import { SavedObjectsWarning } from '../components/saved_objects_warning'; import { TestTrainedModelFlyout } from './test_models'; import { AddInferencePipelineFlyout } from '../components/ml_inference'; -import { usePermissionCheck } from '../capabilities/check_capabilities'; +import { useEnabledFeatures } from '../contexts/ml'; type Stats = Omit; @@ -105,7 +105,7 @@ export const ModelsList: FC = ({ }, } = useMlKibana(); - const [isNLPEnabled] = usePermissionCheck(['isNLPEnabled']); + const { isNLPEnabled } = useEnabledFeatures(); useTimefilter({ timeRangeSelector: false, autoRefreshSelector: true }); diff --git a/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx b/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx index 5edd19f4d8119..850ce45fde123 100644 --- a/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx +++ b/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx @@ -40,7 +40,7 @@ import type { NotificationItem, } from '../../../../common/types/notifications'; import { useMlKibana } from '../../contexts/kibana'; -import { usePermissionCheck } from '../../capabilities/check_capabilities'; +import { useEnabledFeatures } from '../../contexts/ml'; const levelBadgeMap: Record = { [ML_NOTIFICATIONS_MESSAGE_LEVEL.ERROR]: 'danger', @@ -67,11 +67,7 @@ export const NotificationsList: FC = () => { }, } = useMlKibana(); - const [isADEnabled, isDFAEnabled, isNLPEnabled] = usePermissionCheck([ - 'isADEnabled', - 'isDFAEnabled', - 'isNLPEnabled', - ]); + const { isADEnabled, isDFAEnabled, isNLPEnabled } = useEnabledFeatures(); const { displayErrorToast } = useToastNotificationService(); diff --git a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/anomaly_detection_panel.tsx b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/anomaly_detection_panel.tsx index 9707515deb800..8cb97d75c9c3a 100644 --- a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/anomaly_detection_panel.tsx +++ b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/anomaly_detection_panel.tsx @@ -19,7 +19,7 @@ import { import { ML_PAGES } from '../../../../../common/constants/locator'; import { OverviewStatsBar } from '../../../components/collapsible_panel/collapsible_panel'; import { CollapsiblePanel } from '../../../components/collapsible_panel'; -import { useMlKibana, useMlLink, useIsServerless } from '../../../contexts/kibana'; +import { useMlKibana, useMlLink } from '../../../contexts/kibana'; import { AnomalyDetectionTable } from './table'; import { ml } from '../../../services/ml_api_service'; import { getGroupsFromJobs, getStatsBarData } from './utils'; @@ -31,6 +31,7 @@ import { AnomalyTimelineService } from '../../../services/anomaly_timeline_servi import type { OverallSwimlaneData } from '../../../explorer/explorer_utils'; import { AnomalyDetectionEmptyState } from '../../../jobs/jobs_list/components/anomaly_detection_empty_state'; import { overviewPanelDefaultState } from '../../overview_page'; +import { useEnabledFeatures } from '../../../contexts/ml'; export type GroupsDictionary = Dictionary; @@ -57,7 +58,7 @@ export const AnomalyDetectionPanel: FC = ({ anomalyTimelineService, setLa } = useMlKibana(); const { displayErrorToast } = useToastNotificationService(); - const isServerless = useIsServerless(); + const { showNodeInfo } = useEnabledFeatures(); const refresh = useRefresh(); @@ -92,7 +93,7 @@ export const AnomalyDetectionPanel: FC = ({ anomalyTimelineService, setLa return job; }); const { groups: jobsGroups, count } = getGroupsFromJobs(jobsSummaryList); - const stats = getStatsBarData(jobsSummaryList, isServerless); + const stats = getStatsBarData(jobsSummaryList, showNodeInfo); const statGroups = groupBy( Object.entries(stats) diff --git a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts index 3e46b258c3a05..edc993f324bab 100644 --- a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts +++ b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts @@ -74,7 +74,7 @@ export function getGroupsFromJobs(jobs: MlSummaryJobs): { return { groups, count }; } -export function getStatsBarData(jobsList: MlSummaryJob[] | undefined, isServerless: boolean) { +export function getStatsBarData(jobsList: MlSummaryJob[] | undefined, showNodeInfo: boolean) { const jobStats = { total: { label: i18n.translate('xpack.ml.overviewJobsList.statsBar.totalJobsLabel', { @@ -108,9 +108,8 @@ export function getStatsBarData(jobsList: MlSummaryJob[] | undefined, isServerle show: false, group: 0, }, - ...(isServerless - ? {} - : { + ...(showNodeInfo + ? { activeNodes: { label: i18n.translate('xpack.ml.overviewJobsList.statsBar.activeMLNodesLabel', { defaultMessage: 'Active ML nodes', @@ -119,7 +118,8 @@ export function getStatsBarData(jobsList: MlSummaryJob[] | undefined, isServerle show: true, group: 1, }, - }), + } + : {}), activeDatafeeds: { label: i18n.translate('xpack.ml.jobsList.statsBar.activeDatafeedsLabel', { defaultMessage: 'Active datafeeds', @@ -166,7 +166,7 @@ export function getStatsBarData(jobsList: MlSummaryJob[] | undefined, isServerle jobStats.failed.show = false; } - if (isServerless === false) { + if (showNodeInfo) { jobStats.activeNodes!.value = Object.keys(mlNodes).length; } diff --git a/x-pack/plugins/ml/public/application/overview/components/content.tsx b/x-pack/plugins/ml/public/application/overview/components/content.tsx index 15998b9f58b93..1d0db5c6b9632 100644 --- a/x-pack/plugins/ml/public/application/overview/components/content.tsx +++ b/x-pack/plugins/ml/public/application/overview/components/content.tsx @@ -13,7 +13,7 @@ import { AnalyticsPanel } from './analytics_panel'; import { AnomalyTimelineService } from '../../services/anomaly_timeline_service'; import { mlResultsServiceProvider } from '../../services/results_service'; import { useMlKibana } from '../../contexts/kibana'; -import { usePermissionCheck } from '../../capabilities/check_capabilities'; +import { useEnabledFeatures } from '../../contexts/ml'; interface Props { createAnomalyDetectionJobDisabled: boolean; @@ -33,7 +33,7 @@ export const OverviewContent: FC = ({ }, } = useMlKibana(); - const [isADEnabled, isDFAEnabled] = usePermissionCheck(['isADEnabled', 'isDFAEnabled']); + const { isADEnabled, isDFAEnabled } = useEnabledFeatures(); const timefilter = useTimefilter(); diff --git a/x-pack/plugins/ml/public/application/routing/routes/memory_usage.tsx b/x-pack/plugins/ml/public/application/routing/routes/memory_usage.tsx index dce72c080e94f..c1a309736eece 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/memory_usage.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/memory_usage.tsx @@ -36,16 +36,7 @@ export const nodesListRouteFactory = ( }); const PageWrapper: FC = () => { - const { context } = useRouteResolver( - 'full', - // only enabled in non-serverless mode - // if a serverless project ever contains all three features - // this check will have to be changed to an - // explicit isServerless check which will probably - // require a change in useRouteResolver - ['isADEnabled', 'isDFAEnabled', 'isNLPEnabled'], - basicResolvers() - ); + const { context } = useRouteResolver('full', [], basicResolvers()); return ( diff --git a/x-pack/plugins/ml/public/application/routing/routes/overview.tsx b/x-pack/plugins/ml/public/application/routing/routes/overview.tsx index 33346b0d0fe72..77242b9155715 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/overview.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/overview.tsx @@ -11,6 +11,7 @@ import React, { FC, Suspense } from 'react'; import { Redirect } from 'react-router-dom'; import { ML_PAGES } from '../../../locator'; import type { NavigateToPath } from '../../contexts/kibana'; +import { useEnabledFeatures } from '../../contexts/ml/serverless_context'; import { getMlNodeCount } from '../../ml_nodes_check'; import { loadMlServerInfo } from '../../services/ml_server_info'; import { getBreadcrumbWithUrlForApp } from '../breadcrumbs'; @@ -67,5 +68,13 @@ export const appRootRouteFactory = (navigateToPath: NavigateToPath, basePath: st }); const Page: FC = () => { + const { isADEnabled, isDFAEnabled, isNLPEnabled } = useEnabledFeatures(); + if (isADEnabled === false && isDFAEnabled === false && isNLPEnabled === true) { + // if only NLP is enabled, redirect to the trained models page. + // in the search serverless project, the overview page is blank, so we + // need to redirect to the trained models page instead + return ; + } + return ; }; diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.test.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.test.ts index 4e1771c25d0ef..dd7408287cbf4 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.test.ts +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.test.ts @@ -27,7 +27,7 @@ describe('AnomalyChartsEmbeddableFactory', () => { const [coreStart, pluginsStart] = await getStartServices(); // act - const factory = new AnomalyChartsEmbeddableFactory(getStartServices, false); + const factory = new AnomalyChartsEmbeddableFactory(getStartServices); await factory.create({ jobIds: ['test-job'], diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.ts index a6056e84a87be..e502d03bcd964 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.ts +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.ts @@ -35,8 +35,7 @@ export class AnomalyChartsEmbeddableFactory ]; constructor( - private getStartServices: StartServicesAccessor, - private isServerless: boolean + private getStartServices: StartServicesAccessor ) {} public async isEditable() { @@ -62,7 +61,7 @@ export class AnomalyChartsEmbeddableFactory const { resolveEmbeddableAnomalyChartsUserInput } = await import( './anomaly_charts_setup_flyout' ); - return await resolveEmbeddableAnomalyChartsUserInput(coreStart, this.isServerless); + return await resolveEmbeddableAnomalyChartsUserInput(coreStart); } catch (e) { return Promise.reject(); } diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_setup_flyout.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_setup_flyout.tsx index 13849c8e6064c..92aea068c5b15 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_setup_flyout.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_setup_flyout.tsx @@ -19,7 +19,6 @@ import { mlApiServicesProvider } from '../../application/services/ml_api_service export async function resolveEmbeddableAnomalyChartsUserInput( coreStart: CoreStart, - isServerless: boolean, input?: AnomalyChartsEmbeddableInput ): Promise> { const { http, overlays, theme, i18n } = coreStart; @@ -28,7 +27,7 @@ export async function resolveEmbeddableAnomalyChartsUserInput( return new Promise(async (resolve, reject) => { try { - const { jobIds } = await resolveJobSelection(coreStart, isServerless, input?.jobIds); + const { jobIds } = await resolveJobSelection(coreStart, input?.jobIds); const title = input?.title ?? getDefaultExplorerChartsPanelTitle(jobIds); const { jobs } = await getJobs({ jobId: jobIds.join(',') }); const influencers = extractInfluencers(jobs); diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.test.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.test.tsx index 2b75202095e3f..cb759f6783b46 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.test.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.test.tsx @@ -27,7 +27,7 @@ describe('AnomalySwimlaneEmbeddableFactory', () => { const [coreStart, pluginsStart] = await getStartServices(); // act - const factory = new AnomalySwimlaneEmbeddableFactory(getStartServices, false); + const factory = new AnomalySwimlaneEmbeddableFactory(getStartServices); await factory.create({ jobIds: ['test-job'], diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.ts b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.ts index 421d12193e56f..3e7f958ea778e 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.ts +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.ts @@ -35,8 +35,7 @@ export class AnomalySwimlaneEmbeddableFactory ]; constructor( - private getStartServices: StartServicesAccessor, - private isServerless: boolean + private getStartServices: StartServicesAccessor ) {} public async isEditable() { @@ -60,7 +59,7 @@ export class AnomalySwimlaneEmbeddableFactory try { const { resolveAnomalySwimlaneUserInput } = await import('./anomaly_swimlane_setup_flyout'); - return await resolveAnomalySwimlaneUserInput(coreStart, this.isServerless); + return await resolveAnomalySwimlaneUserInput(coreStart); } catch (e) { return Promise.reject(); } diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_setup_flyout.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_setup_flyout.tsx index 2c0e6c5e2d963..dc2ca931cc805 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_setup_flyout.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_setup_flyout.tsx @@ -19,7 +19,6 @@ import { mlApiServicesProvider } from '../../application/services/ml_api_service export async function resolveAnomalySwimlaneUserInput( coreStart: CoreStart, - isServerless: boolean, input?: AnomalySwimlaneEmbeddableInput ): Promise> { const { http, overlays, theme, i18n } = coreStart; @@ -28,7 +27,7 @@ export async function resolveAnomalySwimlaneUserInput( return new Promise(async (resolve, reject) => { try { - const { jobIds } = await resolveJobSelection(coreStart, isServerless, input?.jobIds); + const { jobIds } = await resolveJobSelection(coreStart, input?.jobIds); const title = input?.title ?? getDefaultSwimlanePanelTitle(jobIds); const { jobs } = await getJobs({ jobId: jobIds.join(',') }); const influencers = extractInfluencers(jobs); diff --git a/x-pack/plugins/ml/public/embeddables/common/resolve_job_selection.tsx b/x-pack/plugins/ml/public/embeddables/common/resolve_job_selection.tsx index 3cd49afd8c361..00c4a02d4e929 100644 --- a/x-pack/plugins/ml/public/embeddables/common/resolve_job_selection.tsx +++ b/x-pack/plugins/ml/public/embeddables/common/resolve_job_selection.tsx @@ -26,7 +26,6 @@ import { JobSelectorFlyout } from './components/job_selector_flyout'; */ export async function resolveJobSelection( coreStart: CoreStart, - isServerless: boolean, selectedJobIds?: JobId[] ): Promise<{ jobIds: string[]; groups: Array<{ groupId: string; jobIds: string[] }> }> { const { @@ -70,9 +69,7 @@ export async function resolveJobSelection( const flyoutSession = coreStart.overlays.openFlyout( toMountPoint( - + { const { @@ -54,7 +53,7 @@ export function createFlyout( data, lens, dashboardService, - mlServices: getMlGlobalServices(http, isServerless), + mlServices: getMlGlobalServices(http), }} > { return createFlyout( LensLayerSelectionFlyout, @@ -30,7 +29,6 @@ export async function showLensVisToADJobFlyout( share, data, dashboardService, - isServerless, lens ); } diff --git a/x-pack/plugins/ml/public/embeddables/job_creation/map/show_flyout.tsx b/x-pack/plugins/ml/public/embeddables/job_creation/map/show_flyout.tsx index da2d8bb2e0d4c..5380513f1dc97 100644 --- a/x-pack/plugins/ml/public/embeddables/job_creation/map/show_flyout.tsx +++ b/x-pack/plugins/ml/public/embeddables/job_creation/map/show_flyout.tsx @@ -19,16 +19,7 @@ export async function showMapVisToADJobFlyout( coreStart: CoreStart, share: SharePluginStart, data: DataPublicPluginStart, - dashboardService: DashboardStart, - isServerless: boolean + dashboardService: DashboardStart ): Promise { - return createFlyout( - GeoJobFlyout, - embeddable, - coreStart, - share, - data, - dashboardService, - isServerless - ); + return createFlyout(GeoJobFlyout, embeddable, coreStart, share, data, dashboardService); } diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index 140ac875ed8f3..725bf422c58c6 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -58,7 +58,14 @@ import { MlLocatorDefinition, type MlLocator } from './locator'; import { setDependencyCache } from './application/util/dependency_cache'; import { registerHomeFeature } from './register_home_feature'; import { isFullLicense, isMlEnabled } from '../common/license'; -import { ML_APP_ROUTE, PLUGIN_ICON_SOLUTION, PLUGIN_ID } from '../common/constants/app'; +import { + initEnabledFeatures, + type MlFeatures, + ML_APP_ROUTE, + PLUGIN_ICON_SOLUTION, + PLUGIN_ID, + type ConfigSchema, +} from '../common/constants/app'; import type { MlCapabilities } from './shared'; export interface MlStartDependencies { @@ -113,9 +120,15 @@ export class MlPlugin implements Plugin { private sharedMlServices: MlSharedServices | undefined; private isServerless: boolean = false; + private enabledFeatures: MlFeatures = { + ad: true, + dfa: true, + nlp: true, + }; - constructor(private initializerContext: PluginInitializerContext) { + constructor(private initializerContext: PluginInitializerContext) { this.isServerless = initializerContext.env.packageInfo.buildFlavor === 'serverless'; + initEnabledFeatures(this.enabledFeatures, initializerContext.config.get()); } setup(core: MlCoreSetup, pluginsSetup: MlSetupDependencies) { @@ -164,7 +177,8 @@ export class MlPlugin implements Plugin { presentationUtil: pluginsStart.presentationUtil, }, params, - this.isServerless + this.isServerless, + this.enabledFeatures ); }, }); @@ -180,7 +194,8 @@ export class MlPlugin implements Plugin { { usageCollection: pluginsSetup.usageCollection, }, - this.isServerless + this.isServerless, + this.enabledFeatures ).enable(); } @@ -208,13 +223,13 @@ export class MlPlugin implements Plugin { registerMapExtension, registerCasesAttachments, } = await import('./register_helper'); - registerSearchLinks(this.appUpdater$, fullLicense, mlCapabilities, this.isServerless); + registerSearchLinks(this.appUpdater$, fullLicense, mlCapabilities, !this.isServerless); if (fullLicense) { - registerMlUiActions(pluginsSetup.uiActions, core, this.isServerless); + registerMlUiActions(pluginsSetup.uiActions, core); - if (mlCapabilities.isADEnabled) { - registerEmbeddables(pluginsSetup.embeddable, core, this.isServerless); + if (this.enabledFeatures.ad) { + registerEmbeddables(pluginsSetup.embeddable, core); if (pluginsSetup.cases) { registerCasesAttachments(pluginsSetup.cases, coreStart, pluginStart); diff --git a/x-pack/plugins/ml/public/register_helper/register_search_links/register_search_links.ts b/x-pack/plugins/ml/public/register_helper/register_search_links/register_search_links.ts index 6b7f10103b440..7e0a4dd593c64 100644 --- a/x-pack/plugins/ml/public/register_helper/register_search_links/register_search_links.ts +++ b/x-pack/plugins/ml/public/register_helper/register_search_links/register_search_links.ts @@ -16,7 +16,7 @@ export function registerSearchLinks( appUpdater: BehaviorSubject, isFullLicense: boolean, mlCapabilities: MlCapabilities, - isServerless: boolean + showMLNavMenu: boolean ) { appUpdater.next(() => ({ keywords: [ @@ -24,6 +24,6 @@ export function registerSearchLinks( defaultMessage: 'ML', }), ], - deepLinks: getDeepLinks(isFullLicense, mlCapabilities, isServerless), + deepLinks: getDeepLinks(isFullLicense, mlCapabilities, showMLNavMenu), })); } diff --git a/x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts b/x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts index 9b9642a8a982a..ca48b8a2a4075 100644 --- a/x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts +++ b/x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts @@ -15,13 +15,13 @@ import type { MlCapabilities } from '../../shared'; function createDeepLinks( mlCapabilities: MlCapabilities, isFullLicense: boolean, - isServerless: boolean + showMLNavMenu: boolean ) { function getNavStatus( visible: boolean, showInServerless: boolean = true ): AppNavLinkStatus | undefined { - if (isServerless) { + if (showMLNavMenu === false) { // in serverless the status needs to be "visible" rather than "default" // for the links to appear in the nav menu. return showInServerless && visible ? AppNavLinkStatus.visible : AppNavLinkStatus.hidden; @@ -145,7 +145,7 @@ function createDeepLinks( defaultMessage: 'Memory Usage', }), path: `/${ML_PAGES.MEMORY_USAGE}`, - navLinkStatus: getNavStatus(isFullLicense, false), + navLinkStatus: getNavStatus(isFullLicense, true), }; }, @@ -279,8 +279,8 @@ function createDeepLinks( export function getDeepLinks( isFullLicense: boolean, mlCapabilities: MlCapabilities, - isServerless: boolean + showMLNavMenu: boolean ) { - const links = createDeepLinks(mlCapabilities, isFullLicense, isServerless); + const links = createDeepLinks(mlCapabilities, isFullLicense, showMLNavMenu); return Object.values(links).map((link) => link()); } diff --git a/x-pack/plugins/ml/public/ui_actions/edit_anomaly_charts_panel_action.tsx b/x-pack/plugins/ml/public/ui_actions/edit_anomaly_charts_panel_action.tsx index e4f765f87c598..d79c897958554 100644 --- a/x-pack/plugins/ml/public/ui_actions/edit_anomaly_charts_panel_action.tsx +++ b/x-pack/plugins/ml/public/ui_actions/edit_anomaly_charts_panel_action.tsx @@ -17,8 +17,7 @@ import { export const EDIT_ANOMALY_CHARTS_PANEL_ACTION = 'editAnomalyChartsPanelAction'; export function createEditAnomalyChartsPanelAction( - getStartServices: MlCoreSetup['getStartServices'], - isServerless: boolean + getStartServices: MlCoreSetup['getStartServices'] ): UiActionsActionDefinition { return { id: 'edit-anomaly-charts', @@ -44,7 +43,6 @@ export function createEditAnomalyChartsPanelAction( const result = await resolveEmbeddableAnomalyChartsUserInput( coreStart, - isServerless, embeddable.getInput() ); embeddable.updateInput(result); diff --git a/x-pack/plugins/ml/public/ui_actions/edit_swimlane_panel_action.tsx b/x-pack/plugins/ml/public/ui_actions/edit_swimlane_panel_action.tsx index 5070862023598..4352dc2df89bf 100644 --- a/x-pack/plugins/ml/public/ui_actions/edit_swimlane_panel_action.tsx +++ b/x-pack/plugins/ml/public/ui_actions/edit_swimlane_panel_action.tsx @@ -14,8 +14,7 @@ import { ANOMALY_SWIMLANE_EMBEDDABLE_TYPE, EditSwimlanePanelContext } from '../e export const EDIT_SWIMLANE_PANEL_ACTION = 'editSwimlanePanelAction'; export function createEditSwimlanePanelAction( - getStartServices: MlCoreSetup['getStartServices'], - isServerless: boolean + getStartServices: MlCoreSetup['getStartServices'] ): UiActionsActionDefinition { return { id: 'edit-anomaly-swimlane', @@ -39,11 +38,7 @@ export function createEditSwimlanePanelAction( '../embeddables/anomaly_swimlane/anomaly_swimlane_setup_flyout' ); - const result = await resolveAnomalySwimlaneUserInput( - coreStart, - isServerless, - embeddable.getInput() - ); + const result = await resolveAnomalySwimlaneUserInput(coreStart, embeddable.getInput()); embeddable.updateInput(result); } catch (e) { return Promise.reject(); diff --git a/x-pack/plugins/ml/public/ui_actions/index.ts b/x-pack/plugins/ml/public/ui_actions/index.ts index f08f5bcd886bc..4067547e08956 100644 --- a/x-pack/plugins/ml/public/ui_actions/index.ts +++ b/x-pack/plugins/ml/public/ui_actions/index.ts @@ -34,24 +34,17 @@ export { SWIM_LANE_SELECTION_TRIGGER }; */ export function registerMlUiActions( uiActions: UiActionsSetup, - core: CoreSetup, - isServerless: boolean + core: CoreSetup ) { // Initialize actions - const editSwimlanePanelAction = createEditSwimlanePanelAction( - core.getStartServices, - isServerless - ); + const editSwimlanePanelAction = createEditSwimlanePanelAction(core.getStartServices); const openInExplorerAction = createOpenInExplorerAction(core.getStartServices); const applyInfluencerFiltersAction = createApplyInfluencerFiltersAction(core.getStartServices); const applyEntityFieldFilterAction = createApplyEntityFieldFiltersAction(core.getStartServices); const applyTimeRangeSelectionAction = createApplyTimeRangeSelectionAction(core.getStartServices); const clearSelectionAction = createClearSelectionAction(core.getStartServices); - const editExplorerPanelAction = createEditAnomalyChartsPanelAction( - core.getStartServices, - isServerless - ); - const visToAdJobAction = createVisToADJobAction(core.getStartServices, isServerless); + const editExplorerPanelAction = createEditAnomalyChartsPanelAction(core.getStartServices); + const visToAdJobAction = createVisToADJobAction(core.getStartServices); // Register actions uiActions.registerAction(editSwimlanePanelAction); diff --git a/x-pack/plugins/ml/public/ui_actions/open_vis_in_ml_action.tsx b/x-pack/plugins/ml/public/ui_actions/open_vis_in_ml_action.tsx index 3e06b6175d61e..fb0aa38e44d90 100644 --- a/x-pack/plugins/ml/public/ui_actions/open_vis_in_ml_action.tsx +++ b/x-pack/plugins/ml/public/ui_actions/open_vis_in_ml_action.tsx @@ -15,8 +15,7 @@ import { isLensEmbeddable, isMapEmbeddable } from '../application/jobs/new_job/j export const CREATE_LENS_VIS_TO_ML_AD_JOB_ACTION = 'createMLADJobAction'; export function createVisToADJobAction( - getStartServices: MlCoreSetup['getStartServices'], - isServerless: boolean + getStartServices: MlCoreSetup['getStartServices'] ): UiActionsActionDefinition<{ embeddable: Embeddable | MapEmbeddable }> { return { id: 'create-ml-ad-job-action', @@ -40,26 +39,11 @@ export function createVisToADJobAction( if (lens === undefined) { return; } - await showLensVisToADJobFlyout( - embeddable, - coreStart, - share, - data, - lens, - dashboard, - isServerless - ); + await showLensVisToADJobFlyout(embeddable, coreStart, share, data, lens, dashboard); } else if (isMapEmbeddable(embeddable)) { const [{ showMapVisToADJobFlyout }, [coreStart, { share, data, dashboard }]] = await Promise.all([import('../embeddables/job_creation/map'), getStartServices()]); - await showMapVisToADJobFlyout( - embeddable, - coreStart, - share, - data, - dashboard, - isServerless - ); + await showMapVisToADJobFlyout(embeddable, coreStart, share, data, dashboard); } } catch (e) { return Promise.reject(); diff --git a/x-pack/plugins/ml/server/config_schema.ts b/x-pack/plugins/ml/server/config_schema.ts index 16db0505cc4e0..cc00b6a836d54 100644 --- a/x-pack/plugins/ml/server/config_schema.ts +++ b/x-pack/plugins/ml/server/config_schema.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { schema, type TypeOf } from '@kbn/config-schema'; +import { schema } from '@kbn/config-schema'; const enabledSchema = schema.maybe( schema.object({ @@ -17,5 +17,3 @@ export const configSchema = schema.object({ dfa: enabledSchema, nlp: enabledSchema, }); - -export type ConfigSchema = TypeOf; diff --git a/x-pack/plugins/ml/server/index.ts b/x-pack/plugins/ml/server/index.ts index 53c5b81ec9591..232df4ae7f1df 100644 --- a/x-pack/plugins/ml/server/index.ts +++ b/x-pack/plugins/ml/server/index.ts @@ -6,6 +6,8 @@ */ import type { PluginConfigDescriptor, PluginInitializerContext } from '@kbn/core/server'; +import { type ConfigSchema } from '../common/constants/app'; +import { configSchema } from './config_schema'; import { MlServerPlugin } from './plugin'; export type { MlPluginSetup, MlPluginStart } from './plugin'; export type { @@ -26,10 +28,14 @@ export { InsufficientMLCapabilities, MLPrivilegesUninitialized, } from './shared'; -import { configSchema, type ConfigSchema } from './config_schema'; export const config: PluginConfigDescriptor = { schema: configSchema, + exposeToBrowser: { + ad: true, + dfa: true, + nlp: true, + }, }; export const plugin = (ctx: PluginInitializerContext) => new MlServerPlugin(ctx); diff --git a/x-pack/plugins/ml/server/lib/alerts/register_ml_alerts.ts b/x-pack/plugins/ml/server/lib/alerts/register_ml_alerts.ts index 582676b61928b..d6f0260778ea2 100644 --- a/x-pack/plugins/ml/server/lib/alerts/register_ml_alerts.ts +++ b/x-pack/plugins/ml/server/lib/alerts/register_ml_alerts.ts @@ -7,11 +7,11 @@ import type { Logger } from '@kbn/core/server'; import type { AlertingPlugin } from '@kbn/alerting-plugin/server'; +import type { MlFeatures } from '../../../common/constants/app'; import { registerAnomalyDetectionAlertType } from './register_anomaly_detection_alert_type'; import type { SharedServices } from '../../shared_services'; import { registerJobsMonitoringRuleType } from './register_jobs_monitoring_rule_type'; import type { MlServicesProviders } from '../../shared_services/shared_services'; -import type { MlFeatures } from '../../types'; export interface RegisterAlertParams { alerting: AlertingPlugin['setup']; diff --git a/x-pack/plugins/ml/server/lib/capabilities/capabilities_switcher.ts b/x-pack/plugins/ml/server/lib/capabilities/capabilities_switcher.ts index dadb15b8a46b8..ba08ab5066701 100644 --- a/x-pack/plugins/ml/server/lib/capabilities/capabilities_switcher.ts +++ b/x-pack/plugins/ml/server/lib/capabilities/capabilities_switcher.ts @@ -9,13 +9,13 @@ import { cloneDeep } from 'lodash'; import { firstValueFrom, Observable } from 'rxjs'; import type { CapabilitiesSwitcher, CoreSetup, Logger } from '@kbn/core/server'; import type { ILicense } from '@kbn/licensing-plugin/common/types'; +import type { MlFeatures } from '../../../common/constants/app'; import { isFullLicense, isMinimumLicense, isMlEnabled } from '../../../common/license'; import { type MlCapabilities, basicLicenseMlCapabilities, featureCapabilities, } from '../../../common/types/capabilities'; -import type { MlFeatures } from '../../types'; export const setupCapabilitiesSwitcher = ( coreSetup: CoreSetup, diff --git a/x-pack/plugins/ml/server/lib/register_cases.ts b/x-pack/plugins/ml/server/lib/register_cases.ts index a9644638e11dc..b8a226afd9bc8 100644 --- a/x-pack/plugins/ml/server/lib/register_cases.ts +++ b/x-pack/plugins/ml/server/lib/register_cases.ts @@ -6,11 +6,11 @@ */ import type { CasesSetup } from '@kbn/cases-plugin/server'; +import type { MlFeatures } from '../../common/constants/app'; import { CASE_ATTACHMENT_TYPE_ID_ANOMALY_EXPLORER_CHARTS, CASE_ATTACHMENT_TYPE_ID_ANOMALY_SWIMLANE, } from '../../common/constants/cases'; -import type { MlFeatures } from '../types'; export function registerCasesPersistableState(cases: CasesSetup, enabledFeatures: MlFeatures) { if (enabledFeatures.ad === true) { diff --git a/x-pack/plugins/ml/server/lib/register_sample_data_set_links.ts b/x-pack/plugins/ml/server/lib/register_sample_data_set_links.ts index b4aa199a55a0a..8f79966ac64db 100644 --- a/x-pack/plugins/ml/server/lib/register_sample_data_set_links.ts +++ b/x-pack/plugins/ml/server/lib/register_sample_data_set_links.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import type { HomeServerPluginSetup } from '@kbn/home-plugin/server'; -import type { MlFeatures } from '../types'; +import type { MlFeatures } from '../../common/constants/app'; export function registerSampleDataSetLinks( home: HomeServerPluginSetup, diff --git a/x-pack/plugins/ml/server/models/data_frame_analytics/analytics_manager.ts b/x-pack/plugins/ml/server/models/data_frame_analytics/analytics_manager.ts index bee696f02de81..27e0bd893a53d 100644 --- a/x-pack/plugins/ml/server/models/data_frame_analytics/analytics_manager.ts +++ b/x-pack/plugins/ml/server/models/data_frame_analytics/analytics_manager.ts @@ -19,6 +19,7 @@ import { type MapElements, } from '@kbn/ml-data-frame-analytics-utils'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import type { MlFeatures } from '../../../common/constants/app'; import type { ModelService } from '../model_management/models_provider'; import { modelsProvider } from '../model_management'; import { @@ -36,7 +37,6 @@ import { isTransformLinkReturnType, } from './types'; import type { MlClient } from '../../lib/ml_client'; -import type { MlFeatures } from '../../types'; import { DEFAULT_TRAINED_MODELS_PAGE_SIZE } from '../../routes/trained_models'; export class AnalyticsManager { diff --git a/x-pack/plugins/ml/server/models/model_management/memory_usage.ts b/x-pack/plugins/ml/server/models/model_management/memory_usage.ts index cd665c387302f..6d81062dfd84d 100644 --- a/x-pack/plugins/ml/server/models/model_management/memory_usage.ts +++ b/x-pack/plugins/ml/server/models/model_management/memory_usage.ts @@ -9,6 +9,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import numeral from '@elastic/numeral'; import { pick } from 'lodash'; import { isDefined } from '@kbn/ml-is-defined'; +import type { MlFeatures } from '../../../common/constants/app'; import type { MemoryUsageInfo, TrainedModelStatsResponse, @@ -22,7 +23,6 @@ import type { NodeDeploymentStatsResponse, NodesOverviewResponse, } from '../../../common/types/trained_models'; -import type { MlFeatures } from '../../types'; // @ts-expect-error numeral missing value const AD_EXTRA_MEMORY = numeral('10MB').value(); diff --git a/x-pack/plugins/ml/server/models/notifications_service/notifications_service_provider.ts b/x-pack/plugins/ml/server/models/notifications_service/notifications_service_provider.ts index 981b7ba6be7e6..95c32d8f1d21f 100644 --- a/x-pack/plugins/ml/server/models/notifications_service/notifications_service_provider.ts +++ b/x-pack/plugins/ml/server/models/notifications_service/notifications_service_provider.ts @@ -7,6 +7,7 @@ import { IScopedClusterClient } from '@kbn/core/server'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { MlFeatures } from '../../../common/constants/app'; import type { MLSavedObjectService } from '../../saved_objects'; import type { NotificationItem, NotificationSource } from '../../../common/types/notifications'; import { ML_NOTIFICATION_INDEX_PATTERN } from '../../../common/constants/index_patterns'; @@ -19,7 +20,6 @@ import type { NotificationsCountResponse, NotificationsSearchResponse, } from '../../../common/types/notifications'; -import type { MlFeatures } from '../../types'; const MAX_NOTIFICATIONS_SIZE = 10000; diff --git a/x-pack/plugins/ml/server/plugin.ts b/x-pack/plugins/ml/server/plugin.ts index dcd97acabcbd8..962a9cff24b59 100644 --- a/x-pack/plugins/ml/server/plugin.ts +++ b/x-pack/plugins/ml/server/plugin.ts @@ -25,12 +25,16 @@ import type { SpacesPluginSetup } from '@kbn/spaces-plugin/server'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/server'; import type { HomeServerPluginSetup } from '@kbn/home-plugin/server'; import type { CasesSetup } from '@kbn/cases-plugin/server'; -import type { MlFeatures, PluginsSetup, PluginsStart, RouteInitialization } from './types'; +import type { PluginsSetup, PluginsStart, RouteInitialization } from './types'; import type { MlCapabilities } from '../common/types/capabilities'; -import type { ConfigSchema } from './config_schema'; import { jsonSchemaRoutes } from './routes/json_schema'; import { notificationsRoutes } from './routes/notifications'; -import { PLUGIN_ID } from '../common/constants/app'; +import { + type MlFeatures, + PLUGIN_ID, + type ConfigSchema, + initEnabledFeatures, +} from '../common/constants/app'; import { initMlServerLog } from './lib/log'; import { annotationRoutes } from './routes/annotations'; import { calendars } from './routes/calendars'; @@ -102,7 +106,7 @@ export class MlServerPlugin this.mlLicense = new MlLicense(); this.isMlReady = new Promise((resolve) => (this.setMlReady = resolve)); this.savedObjectsSyncService = new SavedObjectsSyncService(this.log); - this.initEnabledFeatures(ctx.config.get()); + initEnabledFeatures(this.enabledFeatures, ctx.config.get()); } public setup(coreSetup: CoreSetup, plugins: PluginsSetup): MlPluginSetup { @@ -240,11 +244,11 @@ export class MlServerPlugin // Register Trained Model Management routes if (this.enabledFeatures.dfa || this.enabledFeatures.nlp) { - modelManagementRoutes(routeInit); trainedModelsRoutes(routeInit, plugins.cloud); } // Register Miscellaneous routes + modelManagementRoutes(routeInit); dataVisualizerRoutes(routeInit); fieldsService(routeInit); indicesRoutes(routeInit); @@ -332,16 +336,4 @@ export class MlServerPlugin public stop() { this.mlLicense.unsubscribe(); } - - private initEnabledFeatures(config: ConfigSchema) { - if (config.ad?.enabled !== undefined) { - this.enabledFeatures.ad = config.ad.enabled; - } - if (config.dfa?.enabled !== undefined) { - this.enabledFeatures.dfa = config.dfa.enabled; - } - if (config.nlp?.enabled !== undefined) { - this.enabledFeatures.nlp = config.nlp.enabled; - } - } } diff --git a/x-pack/plugins/ml/server/routes/data_frame_analytics.ts b/x-pack/plugins/ml/server/routes/data_frame_analytics.ts index fb5eca48d8fa1..0914500341424 100644 --- a/x-pack/plugins/ml/server/routes/data_frame_analytics.ts +++ b/x-pack/plugins/ml/server/routes/data_frame_analytics.ts @@ -12,10 +12,10 @@ import { JOB_MAP_NODE_TYPES, type DeleteDataFrameAnalyticsWithIndexStatus, } from '@kbn/ml-data-frame-analytics-utils'; -import { ML_INTERNAL_BASE_PATH } from '../../common/constants/app'; +import { type MlFeatures, ML_INTERNAL_BASE_PATH } from '../../common/constants/app'; import { wrapError } from '../client/error_wrapper'; import { analyticsAuditMessagesProvider } from '../models/data_frame_analytics/analytics_audit_messages'; -import type { MlFeatures, RouteInitialization } from '../types'; +import type { RouteInitialization } from '../types'; import { dataAnalyticsJobConfigSchema, dataAnalyticsJobUpdateSchema, diff --git a/x-pack/plugins/ml/server/routes/trained_models.ts b/x-pack/plugins/ml/server/routes/trained_models.ts index f6eeb6fa16d4a..7c9f0c14ec6b4 100644 --- a/x-pack/plugins/ml/server/routes/trained_models.ts +++ b/x-pack/plugins/ml/server/routes/trained_models.ts @@ -8,12 +8,11 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { schema } from '@kbn/config-schema'; import type { ErrorType } from '@kbn/ml-error-utils'; -import type { MlGetTrainedModelsRequest } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { type ElserVersion } from '@kbn/ml-trained-models-utils'; import type { CloudSetup } from '@kbn/cloud-plugin/server'; +import type { ElserVersion } from '@kbn/ml-trained-models-utils'; import { isDefined } from '@kbn/ml-is-defined'; -import { ML_INTERNAL_BASE_PATH } from '../../common/constants/app'; -import type { MlFeatures, RouteInitialization } from '../types'; +import { type MlFeatures, ML_INTERNAL_BASE_PATH } from '../../common/constants/app'; +import type { RouteInitialization } from '../types'; import { wrapError } from '../client/error_wrapper'; import { deleteTrainedModelQuerySchema, @@ -101,7 +100,7 @@ export function trainedModelsRoutes( ...getTrainedModelsRequestParams, ...(modelId ? { model_id: modelId } : {}), size: DEFAULT_TRAINED_MODELS_PAGE_SIZE, - } as MlGetTrainedModelsRequest); + } as estypes.MlGetTrainedModelsRequest); // model_type is missing // @ts-ignore const result = resp.trained_model_configs as TrainedModelConfigResponse[]; diff --git a/x-pack/plugins/ml/server/types.ts b/x-pack/plugins/ml/server/types.ts index 302df71df5f38..17f5c5a60a1a9 100644 --- a/x-pack/plugins/ml/server/types.ts +++ b/x-pack/plugins/ml/server/types.ts @@ -30,6 +30,7 @@ import type { CasesSetup } from '@kbn/cases-plugin/server'; import type { RouteGuard } from './lib/route_guard'; import type { ResolveMlCapabilities } from '../common/types/capabilities'; import type { MlLicense } from '../common/license'; +import type { MlFeatures } from '../common/constants/app'; export interface LicenseCheckResult { isAvailable: boolean; @@ -82,5 +83,3 @@ export interface RouteInitialization { routeGuard: RouteGuard; getEnabledFeatures: () => MlFeatures; } - -export type MlFeatures = Record<'ad' | 'dfa' | 'nlp', boolean>; From 750c4bedbfcd220a6b43551213c3ae9fc7adf0af Mon Sep 17 00:00:00 2001 From: Julian Gernun <17549662+jcger@users.noreply.github.com> Date: Wed, 27 Sep 2023 10:36:36 +0200 Subject: [PATCH 2/3] [RAM] HTTP Versioning Rule Mute Alert (#165573) ## Summary Meta Issue: https://github.com/elastic/kibana/issues/157883 Add HTTP versioning to mute alert endpoint --- .../routes/rule/apis/mute_alert/index.ts | 12 ++++++ .../rule/apis/mute_alert/schemas/latest.ts | 7 +++ .../routes/rule/apis/mute_alert/schemas/v1.ts | 13 ++++++ .../rule/apis/mute_alert/types/latest.ts | 8 ++++ .../routes/rule/apis/mute_alert/types/v1.ts | 10 +++++ .../rule/methods/mute_alert}/mute_instance.ts | 43 +++++++++++-------- .../rule/methods/mute_alert/schemas/index.ts | 7 +++ .../schemas/mute_alert_params_schema.ts | 12 ++++++ .../rule/methods/mute_alert/types/index.ts | 8 ++++ .../mute_alert/types/mute_alert_params.ts | 11 +++++ .../plugins/alerting/server/routes/index.ts | 2 +- .../apis/mute_alert}/mute_alert.test.ts | 10 ++--- .../{ => rule/apis/mute_alert}/mute_alert.ts | 33 +++++--------- .../rule/apis/mute_alert/transforms/index.ts | 8 ++++ .../latest.ts | 8 ++++ .../v1.ts | 17 ++++++++ .../server/rules_client/rules_client.ts | 9 ++-- .../alerting/server/rules_client/types.ts | 1 + 18 files changed, 170 insertions(+), 49 deletions(-) create mode 100644 x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/index.ts create mode 100644 x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/schemas/latest.ts create mode 100644 x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/schemas/v1.ts create mode 100644 x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/types/latest.ts create mode 100644 x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/types/v1.ts rename x-pack/plugins/alerting/server/{rules_client/methods => application/rule/methods/mute_alert}/mute_instance.ts (61%) create mode 100644 x-pack/plugins/alerting/server/application/rule/methods/mute_alert/schemas/index.ts create mode 100644 x-pack/plugins/alerting/server/application/rule/methods/mute_alert/schemas/mute_alert_params_schema.ts create mode 100644 x-pack/plugins/alerting/server/application/rule/methods/mute_alert/types/index.ts create mode 100644 x-pack/plugins/alerting/server/application/rule/methods/mute_alert/types/mute_alert_params.ts rename x-pack/plugins/alerting/server/routes/{ => rule/apis/mute_alert}/mute_alert.test.ts (87%) rename x-pack/plugins/alerting/server/routes/{ => rule/apis/mute_alert}/mute_alert.ts (62%) create mode 100644 x-pack/plugins/alerting/server/routes/rule/apis/mute_alert/transforms/index.ts create mode 100644 x-pack/plugins/alerting/server/routes/rule/apis/mute_alert/transforms/transform_request_params_to_application/latest.ts create mode 100644 x-pack/plugins/alerting/server/routes/rule/apis/mute_alert/transforms/transform_request_params_to_application/v1.ts diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/index.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/index.ts new file mode 100644 index 0000000000000..26d14465471cf --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/index.ts @@ -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. + */ + +export { muteAlertParamsSchema } from './schemas/latest'; +export { muteAlertParamsSchema as muteAlertParamsSchemaV1 } from './schemas/v1'; + +export type { MuteAlertRequestParams } from './types/latest'; +export type { MuteAlertRequestParams as MuteAlertRequestParamsV1 } from './types/v1'; diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/schemas/latest.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/schemas/latest.ts new file mode 100644 index 0000000000000..eee4b4b4e34b3 --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/schemas/latest.ts @@ -0,0 +1,7 @@ +/* + * 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. + */ +export { muteAlertParamsSchema } from './v1'; diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/schemas/v1.ts new file mode 100644 index 0000000000000..3cfe34de957e2 --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/schemas/v1.ts @@ -0,0 +1,13 @@ +/* + * 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 { schema } from '@kbn/config-schema'; + +export const muteAlertParamsSchema = schema.object({ + rule_id: schema.string(), + alert_id: schema.string(), +}); diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/types/latest.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/types/latest.ts new file mode 100644 index 0000000000000..0313b54c87630 --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/types/latest.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export type { MuteAlertRequestParams } from './v1'; diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/types/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/types/v1.ts new file mode 100644 index 0000000000000..af3832641d530 --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_alert/types/v1.ts @@ -0,0 +1,10 @@ +/* + * 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 { TypeOf } from '@kbn/config-schema'; +import { muteAlertParamsSchemaV1 } from '..'; + +export type MuteAlertRequestParams = TypeOf; diff --git a/x-pack/plugins/alerting/server/rules_client/methods/mute_instance.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_alert/mute_instance.ts similarity index 61% rename from x-pack/plugins/alerting/server/rules_client/methods/mute_instance.ts rename to x-pack/plugins/alerting/server/application/rule/methods/mute_alert/mute_instance.ts index 5f37988b7b718..8adbdf7ae58c9 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/mute_instance.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_alert/mute_instance.ts @@ -5,28 +5,37 @@ * 2.0. */ -import { Rule } from '../../types'; -import { WriteOperations, AlertingAuthorizationEntity } from '../../authorization'; -import { retryIfConflicts } from '../../lib/retry_if_conflicts'; -import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; -import { MuteOptions } from '../types'; -import { RulesClientContext } from '../types'; -import { updateMeta } from '../lib'; +import Boom from '@hapi/boom'; +import { updateRuleSo } from '../../../../data/rule/methods/update_rule_so'; +import { muteAlertParamsSchema } from './schemas'; +import type { MuteAlertParams } from './types'; +import { Rule } from '../../../../types'; +import { WriteOperations, AlertingAuthorizationEntity } from '../../../../authorization'; +import { retryIfConflicts } from '../../../../lib/retry_if_conflicts'; +import { ruleAuditEvent, RuleAuditAction } from '../../../../rules_client/common/audit_events'; +import { RulesClientContext } from '../../../../rules_client/types'; +import { updateMeta } from '../../../../rules_client/lib'; export async function muteInstance( context: RulesClientContext, - { alertId, alertInstanceId }: MuteOptions + params: MuteAlertParams ): Promise { + try { + muteAlertParamsSchema.validate(params); + } catch (error) { + throw Boom.badRequest(`Failed to validate params: ${error.message}`); + } + return await retryIfConflicts( context.logger, - `rulesClient.muteInstance('${alertId}')`, - async () => await muteInstanceWithOCC(context, { alertId, alertInstanceId }) + `rulesClient.muteInstance('${params.alertId}')`, + async () => await muteInstanceWithOCC(context, params) ); } async function muteInstanceWithOCC( context: RulesClientContext, - { alertId, alertInstanceId }: MuteOptions + { alertId, alertInstanceId }: MuteAlertParams ) { const { attributes, version } = await context.unsecuredSavedObjectsClient.get( 'alert', @@ -68,15 +77,15 @@ async function muteInstanceWithOCC( const mutedInstanceIds = attributes.mutedInstanceIds || []; if (!attributes.muteAll && !mutedInstanceIds.includes(alertInstanceId)) { mutedInstanceIds.push(alertInstanceId); - await context.unsecuredSavedObjectsClient.update( - 'alert', - alertId, - updateMeta(context, { + await updateRuleSo({ + savedObjectsClient: context.unsecuredSavedObjectsClient, + savedObjectsUpdateOptions: { version }, + id: alertId, + updateRuleAttributes: updateMeta(context, { mutedInstanceIds, updatedBy: await context.getUserName(), updatedAt: new Date().toISOString(), }), - { version } - ); + }); } } diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_alert/schemas/index.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_alert/schemas/index.ts new file mode 100644 index 0000000000000..e7148adf7eefe --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_alert/schemas/index.ts @@ -0,0 +1,7 @@ +/* + * 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. + */ +export { muteAlertParamsSchema } from './mute_alert_params_schema'; diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_alert/schemas/mute_alert_params_schema.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_alert/schemas/mute_alert_params_schema.ts new file mode 100644 index 0000000000000..6c8df8cb907da --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_alert/schemas/mute_alert_params_schema.ts @@ -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. + */ +import { schema } from '@kbn/config-schema'; + +export const muteAlertParamsSchema = schema.object({ + alertId: schema.string(), + alertInstanceId: schema.string(), +}); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_alert/types/index.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_alert/types/index.ts new file mode 100644 index 0000000000000..8b72247e15649 --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_alert/types/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export type { MuteAlertParams } from './mute_alert_params'; diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_alert/types/mute_alert_params.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_alert/types/mute_alert_params.ts new file mode 100644 index 0000000000000..f94f454f1f78c --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_alert/types/mute_alert_params.ts @@ -0,0 +1,11 @@ +/* + * 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 { TypeOf } from '@kbn/config-schema'; +import { muteAlertParamsSchema } from '../schemas'; + +export type MuteAlertParams = TypeOf; diff --git a/x-pack/plugins/alerting/server/routes/index.ts b/x-pack/plugins/alerting/server/routes/index.ts index a93803ed6d585..5086cb56279ed 100644 --- a/x-pack/plugins/alerting/server/routes/index.ts +++ b/x-pack/plugins/alerting/server/routes/index.ts @@ -32,7 +32,7 @@ import { healthRoute } from './health'; import { resolveRuleRoute } from './resolve_rule'; import { ruleTypesRoute } from './rule_types'; import { muteAllRuleRoute } from './mute_all_rule'; -import { muteAlertRoute } from './mute_alert'; +import { muteAlertRoute } from './rule/apis/mute_alert/mute_alert'; import { unmuteAllRuleRoute } from './unmute_all_rule'; import { unmuteAlertRoute } from './unmute_alert'; import { updateRuleApiKeyRoute } from './update_rule_api_key'; diff --git a/x-pack/plugins/alerting/server/routes/mute_alert.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/mute_alert/mute_alert.test.ts similarity index 87% rename from x-pack/plugins/alerting/server/routes/mute_alert.test.ts rename to x-pack/plugins/alerting/server/routes/rule/apis/mute_alert/mute_alert.test.ts index ef67a6d2ef3bf..440c040d74ff7 100644 --- a/x-pack/plugins/alerting/server/routes/mute_alert.test.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/mute_alert/mute_alert.test.ts @@ -7,13 +7,13 @@ import { muteAlertRoute } from './mute_alert'; import { httpServiceMock } from '@kbn/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { rulesClientMock } from '../rules_client.mock'; -import { RuleTypeDisabledError } from '../lib/errors/rule_type_disabled'; +import { licenseStateMock } from '../../../../lib/license_state.mock'; +import { mockHandlerArguments } from '../../../_mock_handler_arguments'; +import { rulesClientMock } from '../../../../rules_client.mock'; +import { RuleTypeDisabledError } from '../../../../lib'; const rulesClient = rulesClientMock.create(); -jest.mock('../lib/license_api_access', () => ({ +jest.mock('../../../../lib/license_api_access', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/mute_alert.ts b/x-pack/plugins/alerting/server/routes/rule/apis/mute_alert/mute_alert.ts similarity index 62% rename from x-pack/plugins/alerting/server/routes/mute_alert.ts rename to x-pack/plugins/alerting/server/routes/rule/apis/mute_alert/mute_alert.ts index 22d9a1670dde5..45a0a37f3164c 100644 --- a/x-pack/plugins/alerting/server/routes/mute_alert.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/mute_alert/mute_alert.ts @@ -4,26 +4,15 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - import { IRouter } from '@kbn/core/server'; -import { schema } from '@kbn/config-schema'; -import { ILicenseState, RuleTypeDisabledError } from '../lib'; -import { MuteOptions } from '../rules_client'; -import { RewriteRequestCase, verifyAccessAndContext } from './lib'; -import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types'; - -const paramSchema = schema.object({ - rule_id: schema.string(), - alert_id: schema.string(), -}); - -const rewriteParamsReq: RewriteRequestCase = ({ - rule_id: alertId, - alert_id: alertInstanceId, -}) => ({ - alertId, - alertInstanceId, -}); +import { transformRequestParamsToApplicationV1 } from './transforms'; +import { ILicenseState, RuleTypeDisabledError } from '../../../../lib'; +import { verifyAccessAndContext } from '../../../lib'; +import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../../../../types'; +import { + muteAlertParamsSchemaV1, + MuteAlertRequestParamsV1, +} from '../../../../../common/routes/rule/apis/mute_alert'; export const muteAlertRoute = ( router: IRouter, @@ -33,15 +22,15 @@ export const muteAlertRoute = ( { path: `${BASE_ALERTING_API_PATH}/rule/{rule_id}/alert/{alert_id}/_mute`, validate: { - params: paramSchema, + params: muteAlertParamsSchemaV1, }, }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { const rulesClient = (await context.alerting).getRulesClient(); - const params = rewriteParamsReq(req.params); + const params: MuteAlertRequestParamsV1 = req.params; try { - await rulesClient.muteInstance(params); + await rulesClient.muteInstance(transformRequestParamsToApplicationV1(params)); return res.noContent(); } catch (e) { if (e instanceof RuleTypeDisabledError) { diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/mute_alert/transforms/index.ts b/x-pack/plugins/alerting/server/routes/rule/apis/mute_alert/transforms/index.ts new file mode 100644 index 0000000000000..21a7250aed4e2 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/rule/apis/mute_alert/transforms/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ +export { transformRequestParamsToApplication } from './transform_request_params_to_application/latest'; +export { transformRequestParamsToApplication as transformRequestParamsToApplicationV1 } from './transform_request_params_to_application/v1'; diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/mute_alert/transforms/transform_request_params_to_application/latest.ts b/x-pack/plugins/alerting/server/routes/rule/apis/mute_alert/transforms/transform_request_params_to_application/latest.ts new file mode 100644 index 0000000000000..5983069f0d8fd --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/rule/apis/mute_alert/transforms/transform_request_params_to_application/latest.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { transformRequestParamsToApplication } from './v1'; diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/mute_alert/transforms/transform_request_params_to_application/v1.ts b/x-pack/plugins/alerting/server/routes/rule/apis/mute_alert/transforms/transform_request_params_to_application/v1.ts new file mode 100644 index 0000000000000..37966060dba02 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/rule/apis/mute_alert/transforms/transform_request_params_to_application/v1.ts @@ -0,0 +1,17 @@ +/* + * 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 { MuteAlertParams } from '../../../../../../application/rule/methods/mute_alert/types'; +import { RewriteRequestCase } from '../../../../../lib'; + +export const transformRequestParamsToApplication: RewriteRequestCase = ({ + rule_id: alertId, + alert_id: alertInstanceId, +}) => ({ + alertId, + alertInstanceId, +}); diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index 4c1138f23cb39..d7a576ac99d0b 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -5,9 +5,10 @@ * 2.0. */ +import { MuteAlertParams } from '../application/rule/methods/mute_alert/types'; import { SanitizedRule, RuleTypeParams } from '../types'; import { parseDuration } from '../../common/parse_duration'; -import { RulesClientContext, BulkOptions, MuteOptions } from './types'; +import { RulesClientContext, BulkOptions } from './types'; import { clone, CloneArguments } from './methods/clone'; import { createRule, CreateRuleParams } from '../application/rule/methods/create'; import { get, GetParams } from './methods/get'; @@ -52,9 +53,9 @@ import { disable } from './methods/disable'; import { snooze, SnoozeParams } from './methods/snooze'; import { unsnooze, UnsnoozeParams } from './methods/unsnooze'; import { clearExpiredSnoozes } from './methods/clear_expired_snoozes'; +import { muteInstance } from '../application/rule/methods/mute_alert/mute_instance'; import { muteAll } from './methods/mute_all'; import { unmuteAll } from './methods/unmute_all'; -import { muteInstance } from './methods/mute_instance'; import { unmuteInstance } from './methods/unmute_instance'; import { runSoon } from './methods/run_soon'; import { listRuleTypes } from './methods/list_rule_types'; @@ -163,8 +164,8 @@ export class RulesClient { public muteAll = (options: { id: string }) => muteAll(this.context, options); public unmuteAll = (options: { id: string }) => unmuteAll(this.context, options); - public muteInstance = (options: MuteOptions) => muteInstance(this.context, options); - public unmuteInstance = (options: MuteOptions) => unmuteInstance(this.context, options); + public muteInstance = (options: MuteAlertParams) => muteInstance(this.context, options); + public unmuteInstance = (options: MuteAlertParams) => unmuteInstance(this.context, options); public runSoon = (options: { id: string }) => runSoon(this.context, options); diff --git a/x-pack/plugins/alerting/server/rules_client/types.ts b/x-pack/plugins/alerting/server/rules_client/types.ts index c755651f5524d..87ae594976246 100644 --- a/x-pack/plugins/alerting/server/rules_client/types.ts +++ b/x-pack/plugins/alerting/server/rules_client/types.ts @@ -121,6 +121,7 @@ export interface IndexType { [key: string]: unknown; } +// TODO: remove once all mute endpoints have been migrated to RuleMuteAlertOptions export interface MuteOptions extends IndexType { alertId: string; alertInstanceId: string; From 05c637f31619f76a37438ea2845d62bd3ee97a03 Mon Sep 17 00:00:00 2001 From: Yngrid Coello Date: Wed, 27 Sep 2023 10:38:58 +0200 Subject: [PATCH 3/3] [Logs onboarding] fixing type issues (#167340) `OBSERVABILITY_ONBOARDING_LOCATOR` and `ObservabilityOnboardingLocatorParams` were removed from `observability_onboarding` plugin in [#165962](https://github.com/elastic/kibana/pull/165962) and are now part of `@kbn/deeplinks-observability/locators`. `datsetName` was transformed into an optional variable in the wizard state in [#166654](https://github.com/elastic/kibana/pull/166654) which makes sense for first step of custom logs onboarding but it's required in onboarding saved object. --- .../app/custom_logs/install_elastic_agent.tsx | 13 ++++++++++--- .../observability_onboarding/public/index.ts | 3 --- .../locators/onboarding_locator/get_location.ts | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/observability_onboarding/public/components/app/custom_logs/install_elastic_agent.tsx b/x-pack/plugins/observability_onboarding/public/components/app/custom_logs/install_elastic_agent.tsx index 5b6db7f900b79..922736ea107e5 100644 --- a/x-pack/plugins/observability_onboarding/public/components/app/custom_logs/install_elastic_agent.tsx +++ b/x-pack/plugins/observability_onboarding/public/components/app/custom_logs/install_elastic_agent.tsx @@ -41,6 +41,8 @@ import { BackButton } from './back_button'; import { WindowsInstallStep } from '../../shared/windows_install_step'; import { TroubleshootingLink } from '../../shared/troubleshooting_link'; +const defaultDatasetName = ''; + export function InstallElasticAgent() { const { services: { share }, @@ -63,7 +65,8 @@ export function InstallElasticAgent() { useState('linux-tar'); const enforcedDatasetName = - (integration === dataset ? dataset : `${integration}.${dataset}`) || ''; + (integration === dataset ? dataset : `${integration}.${dataset}`) ?? + defaultDatasetName; async function onContinue() { await singleDatasetLocator!.navigate({ @@ -109,11 +112,15 @@ export function InstallElasticAgent() { customConfigurations, logFilePaths, } = getState(); - if (!hasAlreadySavedFlow(getState()) && monitoringRole?.hasPrivileges) { + if ( + !hasAlreadySavedFlow(getState()) && + monitoringRole?.hasPrivileges && + datasetName + ) { return callApi('POST /internal/observability_onboarding/logs/flow', { params: { body: { - name: datasetName || '', + name: datasetName, type: 'logFiles', state: { datasetName, diff --git a/x-pack/plugins/observability_onboarding/public/index.ts b/x-pack/plugins/observability_onboarding/public/index.ts index 2b8271bb5c8ed..9454626e80dc8 100644 --- a/x-pack/plugins/observability_onboarding/public/index.ts +++ b/x-pack/plugins/observability_onboarding/public/index.ts @@ -17,9 +17,6 @@ import { ObservabilityOnboardingPluginStart, } from './plugin'; -export type { OBSERVABILITY_ONBOARDING_LOCATOR } from './locators/onboarding_locator/locator_definition'; -export type { ObservabilityOnboardingLocatorParams } from './locators/onboarding_locator/types'; - export interface ConfigSchema { ui: { enabled: boolean; diff --git a/x-pack/plugins/observability_onboarding/public/locators/onboarding_locator/get_location.ts b/x-pack/plugins/observability_onboarding/public/locators/onboarding_locator/get_location.ts index 93bb3861740cd..9ebd263847f36 100644 --- a/x-pack/plugins/observability_onboarding/public/locators/onboarding_locator/get_location.ts +++ b/x-pack/plugins/observability_onboarding/public/locators/onboarding_locator/get_location.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ObservabilityOnboardingLocatorParams } from './types'; +import type { ObservabilityOnboardingLocatorParams } from '@kbn/deeplinks-observability/locators'; import { PLUGIN_ID } from '../../../common'; export function getLocation(params: ObservabilityOnboardingLocatorParams) {