From c18513e23da91e64a1b0802af185551b590862c6 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 27 Sep 2023 09:32:17 +0100 Subject: [PATCH 01/12] [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 02/12] [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 03/12] [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) { From 03017759d265deae10ce7fba1e3650a74dfa5d34 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 27 Sep 2023 11:46:43 +0300 Subject: [PATCH 04/12] [Sample data] Fixes TSDB dashboard (#167045) ## Summary Closes https://github.com/elastic/kibana/issues/162270 Fixes the broken TSDB dashboard image To test, uninstall the sample TSDB dataset and reinstall --- .../data_sets/logs_tsdb/saved_objects.ts | 223 +++++------------- .../translations/translations/fr-FR.json | 3 - .../translations/translations/ja-JP.json | 3 - .../translations/translations/zh-CN.json | 3 - 4 files changed, 59 insertions(+), 173 deletions(-) diff --git a/src/plugins/home/server/services/sample_data/data_sets/logs_tsdb/saved_objects.ts b/src/plugins/home/server/services/sample_data/data_sets/logs_tsdb/saved_objects.ts index 7f9b5e337cbb3..02a2ec4edb220 100644 --- a/src/plugins/home/server/services/sample_data/data_sets/logs_tsdb/saved_objects.ts +++ b/src/plugins/home/server/services/sample_data/data_sets/logs_tsdb/saved_objects.ts @@ -16,14 +16,15 @@ export const getSavedObjects = (): SavedObject[] => [ type: 'visualization', updated_at: '2021-10-28T15:07:36.622Z', version: '1', - coreMigrationVersion: '8.0.0', - migrationVersion: { visualization: '8.0.0' }, + coreMigrationVersion: '8.8.0', + typeMigrationVersion: '8.0.0', + managed: false, attributes: { title: i18n.translate('home.sampleData.logsTsdbSpec.visitorsMapTitle', { defaultMessage: '[Logs TSDB] Visitors Map', }), visState: - '{"title":"[Logs TSDB] Visitors Map","type":"vega","aggs":[],"params":{"spec":"{\\n $schema: https://vega.github.io/schema/vega/v5.json\\n config: {\\n kibana: {type: \\"map\\", latitude: 30, longitude: -120, zoom: 3}\\n }\\n data: [\\n {\\n name: table\\n url: {\\n index: kibana_sample_data_logstsdb\\n %context%: true\\n %timefield%: timestamp\\n body: {\\n size: 0\\n aggs: {\\n gridSplit: {\\n geotile_grid: {field: \\"geo.coordinates\\", precision: 5, size: 10000}\\n aggs: {\\n gridCentroid: {\\n geo_centroid: {\\n field: \\"geo.coordinates\\"\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n format: {property: \\"aggregations.gridSplit.buckets\\"}\\n transform: [\\n {\\n type: geopoint\\n projection: projection\\n fields: [\\n gridCentroid.location.lon\\n gridCentroid.location.lat\\n ]\\n }\\n ]\\n }\\n ]\\n scales: [\\n {\\n name: gridSize\\n type: linear\\n domain: {data: \\"table\\", field: \\"doc_count\\"}\\n range: [\\n 50\\n 1000\\n ]\\n }\\n {\\n name: bubbleColor\\n type: linear\\n domain: {\\n data: table\\n field: doc_count\\n }\\n range: [\\"rgb(249, 234, 197)\\",\\"rgb(243, 200, 154)\\",\\"rgb(235, 166, 114)\\", \\"rgb(231, 102, 76)\\"]\\n }\\n ]\\n marks: [\\n {\\n name: gridMarker\\n type: symbol\\n from: {data: \\"table\\"}\\n encode: {\\n update: {\\n fill: {\\n scale: bubbleColor\\n field: doc_count\\n }\\n size: {scale: \\"gridSize\\", field: \\"doc_count\\"}\\n xc: {signal: \\"datum.x\\"}\\n yc: {signal: \\"datum.y\\"}\\n tooltip: {\\n signal: \\"{flights: datum.doc_count}\\"\\n }\\n }\\n }\\n }\\n ]\\n}"}}', + '{"title":"[Logs] Visitors Map","type":"vega","aggs":[],"params":{"spec":"{\\n $schema: https://vega.github.io/schema/vega/v5.json\\n config: {\\n kibana: {type: \\"map\\", latitude: 30, longitude: -120, zoom: 3}\\n }\\n data: [\\n {\\n name: table\\n url: {\\n index: kibana_sample_data_logs\\n %context%: true\\n %timefield%: timestamp\\n body: {\\n size: 0\\n aggs: {\\n gridSplit: {\\n geotile_grid: {field: \\"geo.coordinates\\", precision: 5, size: 10000}\\n aggs: {\\n gridCentroid: {\\n geo_centroid: {\\n field: \\"geo.coordinates\\"\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n format: {property: \\"aggregations.gridSplit.buckets\\"}\\n transform: [\\n {\\n type: geopoint\\n projection: projection\\n fields: [\\n gridCentroid.location.lon\\n gridCentroid.location.lat\\n ]\\n }\\n ]\\n }\\n ]\\n scales: [\\n {\\n name: gridSize\\n type: linear\\n domain: {data: \\"table\\", field: \\"doc_count\\"}\\n range: [\\n 50\\n 1000\\n ]\\n }\\n {\\n name: bubbleColor\\n type: linear\\n domain: {\\n data: table\\n field: doc_count\\n }\\n range: [\\"rgb(249, 234, 197)\\",\\"rgb(243, 200, 154)\\",\\"rgb(235, 166, 114)\\", \\"rgb(231, 102, 76)\\"]\\n }\\n ]\\n marks: [\\n {\\n name: gridMarker\\n type: symbol\\n from: {data: \\"table\\"}\\n encode: {\\n update: {\\n fill: {\\n scale: bubbleColor\\n field: doc_count\\n }\\n size: {scale: \\"gridSize\\", field: \\"doc_count\\"}\\n xc: {signal: \\"datum.x\\"}\\n yc: {signal: \\"datum.y\\"}\\n tooltip: {\\n signal: \\"{flights: datum.doc_count}\\"\\n }\\n }\\n }\\n }\\n ]\\n}"}}', uiStateJSON: '{}', description: '', version: 1, @@ -45,9 +46,9 @@ export const getSavedObjects = (): SavedObject[] => [ type: 'visualization', updated_at: '2021-07-21T21:33:42.541Z', version: '1', - migrationVersion: { - visualization: '7.14.0', - }, + coreMigrationVersion: '8.8.0', + typeMigrationVersion: '7.14.0', + managed: false, attributes: { title: i18n.translate('home.sampleData.logsTsdbSpec.heatmapTitle', { defaultMessage: '[Logs TSDB] Unique Destination Heatmap', @@ -59,81 +60,21 @@ export const getSavedObjects = (): SavedObject[] => [ uiStateJSON: '{}', version: 1, visState: - '{"title":"[Logs TSDB] Unique Destination Heatmap","type":"vega","aggs":[],"params":{"spec":"{\\n $schema: https://vega.github.io/schema/vega-lite/v5.json\\n data: {\\n url: {\\n %context%: true\\n %timefield%: @timestamp\\n index: kibana_sample_data_logstsdb\\n body: {\\n aggs: {\\n countries: {\\n terms: {\\n field: geo.dest\\n size: 25\\n }\\n aggs: {\\n hours: {\\n histogram: {\\n field: hour_of_day\\n interval: 1\\n }\\n aggs: {\\n unique: {\\n cardinality: {\\n field: clientip\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n size: 0\\n }\\n }\\n format: {property: \\"aggregations.countries.buckets\\"}\\n }\\n \\n transform: [\\n {\\n flatten: [\\"hours.buckets\\"],\\n as: [\\"buckets\\"]\\n },\\n {\\n filter: \\"datum.buckets.unique.value > 0\\"\\n }\\n ]\\n\\n mark: {\\n type: rect\\n tooltip: {\\n expr: \\"{\\\\\\"Unique Visitors\\\\\\": datum.buckets.unique.value,\\\\\\"geo.src\\\\\\": datum.key,\\\\\\"Hour\\\\\\": datum.buckets.key}\\"\\n }\\n }\\n\\n encoding: {\\n x: {\\n field: buckets.key\\n type: nominal\\n scale: {\\n domain: {\\n expr: \\"sequence(0, 24)\\"\\n }\\n }\\n axis: {\\n title: false\\n labelAngle: 0\\n }\\n }\\n y: {\\n field: key\\n type: nominal\\n sort: {\\n field: -buckets.unique.value\\n }\\n axis: {title: false}\\n }\\n color: {\\n field: buckets.unique.value\\n type: quantitative\\n axis: {title: false}\\n scale: {\\n scheme: blues\\n }\\n }\\n }\\n}\\n"}}', + '{"title":"[Logs] Unique Destination Heatmap","type":"vega","aggs":[],"params":{"spec":"{\\n $schema: https://vega.github.io/schema/vega-lite/v5.json\\n data: {\\n url: {\\n %context%: true\\n %timefield%: @timestamp\\n index: kibana_sample_data_logs\\n body: {\\n aggs: {\\n countries: {\\n terms: {\\n field: geo.dest\\n size: 25\\n }\\n aggs: {\\n hours: {\\n histogram: {\\n field: hour_of_day\\n interval: 1\\n }\\n aggs: {\\n unique: {\\n cardinality: {\\n field: clientip\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n size: 0\\n }\\n }\\n format: {property: \\"aggregations.countries.buckets\\"}\\n }\\n \\n transform: [\\n {\\n flatten: [\\"hours.buckets\\"],\\n as: [\\"buckets\\"]\\n },\\n {\\n filter: \\"datum.buckets.unique.value > 0\\"\\n }\\n ]\\n\\n mark: {\\n type: rect\\n tooltip: {\\n expr: \\"{\\\\\\"Unique Visitors\\\\\\": datum.buckets.unique.value,\\\\\\"geo.src\\\\\\": datum.key,\\\\\\"Hour\\\\\\": datum.buckets.key}\\"\\n }\\n }\\n\\n encoding: {\\n x: {\\n field: buckets.key\\n type: nominal\\n scale: {\\n domain: {\\n expr: \\"sequence(0, 24)\\"\\n }\\n }\\n axis: {\\n title: false\\n labelAngle: 0\\n }\\n }\\n y: {\\n field: key\\n type: nominal\\n sort: {\\n field: -buckets.unique.value\\n }\\n axis: {title: false}\\n }\\n color: {\\n field: buckets.unique.value\\n type: quantitative\\n axis: {title: false}\\n scale: {\\n scheme: blues\\n }\\n }\\n }\\n}\\n"}}', }, references: [], }, { - id: '4eb6e500-e1c7-11e7-b6d5-4dc382ef8f5b', - type: 'visualization', - updated_at: '2021-07-21T18:52:13.586Z', - version: '2', - migrationVersion: { - visualization: '7.14.0', - }, - attributes: { - title: i18n.translate('home.sampleData.logsTsdbSpec.hostVisitsBytesTableTitle', { - defaultMessage: '[Logs TSDB] Host, Visits and Bytes Table', - }), - visState: - '{"title":"[Logs TSDB] Host, Visits and Bytes Table","type":"metrics","aggs":[],"params":{"time_range_mode":"last_value","id":"61ca57f0-469d-11e7-af02-69e470af7417","type":"table","series":[{"id":"bd09d600-e5b1-11e7-bfc2-a1f7e71965a1","color":"#68BC00","split_mode":"everything","metrics":[{"id":"bd09d601-e5b1-11e7-bfc2-a1f7e71965a1","type":"sum","field":"bytes"},{"sigma":"","id":"c9514c90-e5b1-11e7-bfc2-a1f7e71965a1","type":"sum_bucket","field":"bd09d601-e5b1-11e7-bfc2-a1f7e71965a1"}],"seperate_axis":0,"axis_position":"right","formatter":"bytes","chart_type":"line","line_width":1,"point_size":1,"fill":0.5,"stacked":"none","color_rules":[{"id":"c0c668d0-e5b1-11e7-bfc2-a1f7e71965a1"}],"label":"Bytes (Total)","split_color_mode":"gradient"},{"id":"b7672c30-a6df-11e8-8b18-1da1dfc50975","color":"#68BC00","split_mode":"everything","metrics":[{"id":"b7672c31-a6df-11e8-8b18-1da1dfc50975","type":"sum","field":"bytes"}],"seperate_axis":0,"axis_position":"right","formatter":"bytes","chart_type":"line","line_width":1,"point_size":1,"fill":0.5,"stacked":"none","color_rules":[{"id":"c0c668d0-e5b1-11e7-bfc2-a1f7e71965a1"}],"label":"Bytes (Last Hour)","split_color_mode":"gradient","trend_arrows":1},{"id":"f2c20700-a6df-11e8-8b18-1da1dfc50975","color":"#68BC00","split_mode":"everything","metrics":[{"id":"f2c20701-a6df-11e8-8b18-1da1dfc50975","type":"cardinality","field":"clientip"},{"sigma":"","id":"f46333e0-a6df-11e8-8b18-1da1dfc50975","type":"sum_bucket","field":"f2c20701-a6df-11e8-8b18-1da1dfc50975"}],"seperate_axis":0,"axis_position":"right","formatter":"number","chart_type":"line","line_width":1,"point_size":1,"fill":0.5,"stacked":"none","label":"Unique Visits (Total)","color_rules":[{"value":1000,"id":"2e963080-a6e0-11e8-8b18-1da1dfc50975","text":"rgba(211,49,21,1)","operator":"lt"},{"value":1000,"id":"3d4fb880-a6e0-11e8-8b18-1da1dfc50975","text":"rgba(252,196,0,1)","operator":"gte"},{"value":1500,"id":"435f8a20-a6e0-11e8-8b18-1da1dfc50975","text":"rgba(104,188,0,1)","operator":"gte"}],"offset_time":"","value_template":"","trend_arrows":0,"split_color_mode":"gradient"},{"id":"46fd7fc0-e5b1-11e7-bfc2-a1f7e71965a1","color":"#68BC00","split_mode":"everything","metrics":[{"id":"46fd7fc1-e5b1-11e7-bfc2-a1f7e71965a1","type":"cardinality","field":"ip"}],"seperate_axis":0,"axis_position":"right","formatter":"number","chart_type":"line","line_width":1,"point_size":1,"fill":0.5,"stacked":"none","label":"Unique Visits (Last Hour)","color_rules":[{"value":10,"id":"4e90aeb0-a6e0-11e8-8b18-1da1dfc50975","text":"rgba(211,49,21,1)","operator":"lt"},{"value":10,"id":"6d59b1c0-a6e0-11e8-8b18-1da1dfc50975","text":"rgba(252,196,0,1)","operator":"gte"},{"value":25,"id":"77578670-a6e0-11e8-8b18-1da1dfc50975","text":"rgba(104,188,0,1)","operator":"gte"}],"offset_time":"","value_template":"","trend_arrows":1,"split_color_mode":"gradient"}],"time_field":"timestamp","use_kibana_indexes":true,"interval":"1h","axis_position":"left","axis_formatter":"number","show_legend":1,"show_grid":1,"bar_color_rules":[{"id":"e9b4e490-e1c6-11e7-b4f6-0f68c45f7387"}],"pivot_id":"extension.keyword","pivot_label":"Type","drilldown_url":"","axis_scale":"normal","hide_last_value_indicator":false,"tooltip_mode":"show_all","drop_last_bucket":0,"isModelInvalid":false,"index_pattern_ref_name":"metrics_0_index_pattern"}}', - uiStateJSON: '{}', - description: '', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: '{"query":{"query":"","language":"kuery"},"filter":[]}', - }, - }, - references: [ - { - id: '90943e30-9a47-11e8-b64d-95841ca0c247', - type: 'index-pattern', - name: 'metrics_0_index_pattern', - }, - ], - }, - { - id: '69a34b00-9ee8-11e7-8711-e7a007dcff99', - type: 'visualization', - updated_at: '2021-10-28T14:38:21.435Z', - version: '2', - coreMigrationVersion: '8.0.0', - migrationVersion: { visualization: '8.0.0' }, - attributes: { - title: i18n.translate('home.sampleData.logsTsdbSpec.goalsTitle', { - defaultMessage: '[Logs TSDB] Goals', - }), - visState: - '{"title":"[Logs TSDB] Goals","type":"gauge","params":{"type":"gauge","addTooltip":true,"addLegend":false,"gauge":{"extendRange":true,"percentageMode":false,"gaugeType":"Arc","gaugeStyle":"Full","backStyle":"Full","orientation":"vertical","colorSchema":"Green to Red","gaugeColorMode":"Labels","colorsRange":[{"from":0,"to":500},{"from":500,"to":1000},{"from":1000,"to":1500}],"invertColors":true,"labels":{"show":false,"color":"black"},"scale":{"show":true,"labels":false,"color":"#333"},"type":"meter","style":{"bgWidth":0.9,"width":0.9,"mask":false,"bgMask":false,"maskBars":50,"bgFill":"#eee","bgColor":false,"subText":"visitors","fontSize":60,"labelColor":true},"alignment":"horizontal"},"isDisplayWarning":false},"aggs":[{"id":"1","enabled":true,"type":"cardinality","schema":"metric","params":{"field":"clientip","customLabel":"Unique Visitors"}}]}', - uiStateJSON: - '{"vis":{"defaultColors":{"0 - 500":"rgb(165,0,38)","500 - 1000":"rgb(255,255,190)","1000 - 1500":"rgb(0,104,55)"},"colors":{"75 - 100":"#629E51","50 - 75":"#EAB839","0 - 50":"#E24D42","0 - 100":"#E24D42","200 - 300":"#7EB26D","500 - 1000":"#E5AC0E","0 - 500":"#E24D42","1000 - 1500":"#7EB26D"},"legendOpen":true}}', - description: '', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: - '{"filter":[],"query":{"query":"","language":"kuery"},"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}', - }, - }, - references: [ - { - id: '90943e30-9a47-11e8-b64d-95841ca0c247', - name: 'kibanaSavedObjectMeta.searchSourceJSON.index', - type: 'index-pattern', - }, - ], - }, - { - id: '7cbd2350-2223-11e8-b802-5bcf64c2dfb4', + id: '7cbd2350-2223-11e8-b802-5bcf64c2cfb4', type: 'visualization', updated_at: '2018-08-29T13:22:17.617Z', version: '1', - migrationVersion: {}, attributes: { title: i18n.translate('home.sampleData.logsTsdbSpec.sourceAndDestinationSankeyChartTitle', { defaultMessage: '[Logs TSDB] Machine OS and Destination Sankey Chart', }), visState: - '{"title":"[Logs TSDB] Machine OS and Destination Sankey Chart","type":"vega","params":{"spec":"{ \\n $schema: https://vega.github.io/schema/vega/v5.json\\n data: [\\n\\t{\\n \\t// query ES based on the currently selected time range and filter string\\n \\tname: rawData\\n \\turl: {\\n \\t%context%: true\\n \\t%timefield%: timestamp\\n \\tindex: kibana_sample_data_logstsdb\\n \\tbody: {\\n \\tsize: 0\\n \\taggs: {\\n \\ttable: {\\n \\tcomposite: {\\n \\tsize: 10000\\n \\tsources: [\\n \\t{\\n \\tstk1: {\\n \\tterms: {field: \\"machine.os.keyword\\"}\\n \\t}\\n \\t}\\n \\t{\\n \\tstk2: {\\n \\tterms: {field: \\"geo.dest\\"}\\n \\t}\\n \\t}\\n \\t]\\n \\t}\\n \\t}\\n \\t}\\n \\t}\\n \\t}\\n \\t// From the result, take just the data we are interested in\\n \\tformat: {property: \\"aggregations.table.buckets\\"}\\n \\t// Convert key.stk1 -> stk1 for simpler access below\\n \\ttransform: [\\n \\t{type: \\"formula\\", expr: \\"datum.key.stk1\\", as: \\"stk1\\"}\\n \\t{type: \\"formula\\", expr: \\"datum.key.stk2\\", as: \\"stk2\\"}\\n \\t{type: \\"formula\\", expr: \\"datum.doc_count\\", as: \\"size\\"}\\n \\t]\\n\\t}\\n\\t{\\n \\tname: nodes\\n \\tsource: rawData\\n \\ttransform: [\\n \\t// when a country is selected, filter out unrelated data\\n \\t{\\n \\ttype: filter\\n \\texpr: !groupSelector || groupSelector.stk1 == datum.stk1 || groupSelector.stk2 == datum.stk2\\n \\t}\\n \\t// Set new key for later lookups - identifies each node\\n \\t{type: \\"formula\\", expr: \\"datum.stk1+datum.stk2\\", as: \\"key\\"}\\n \\t// instead of each table row, create two new rows,\\n \\t// one for the source (stack=stk1) and one for destination node (stack=stk2).\\n \\t// The country code stored in stk1 and stk2 fields is placed into grpId field.\\n \\t{\\n \\ttype: fold\\n \\tfields: [\\"stk1\\", \\"stk2\\"]\\n \\tas: [\\"stack\\", \\"grpId\\"]\\n \\t}\\n \\t// Create a sortkey, different for stk1 and stk2 stacks.\\n \\t{\\n \\ttype: formula\\n \\texpr: datum.stack == \'stk1\' ? datum.stk1+datum.stk2 : datum.stk2+datum.stk1\\n \\tas: sortField\\n \\t}\\n \\t// Calculate y0 and y1 positions for stacking nodes one on top of the other,\\n \\t// independently for each stack, and ensuring they are in the proper order,\\n \\t// alphabetical from the top (reversed on the y axis)\\n \\t{\\n \\ttype: stack\\n \\tgroupby: [\\"stack\\"]\\n \\tsort: {field: \\"sortField\\", order: \\"descending\\"}\\n \\tfield: size\\n \\t}\\n \\t// calculate vertical center point for each node, used to draw edges\\n \\t{type: \\"formula\\", expr: \\"(datum.y0+datum.y1)/2\\", as: \\"yc\\"}\\n \\t]\\n\\t}\\n\\t{\\n \\tname: groups\\n \\tsource: nodes\\n \\ttransform: [\\n \\t// combine all nodes into country groups, summing up the doc counts\\n \\t{\\n \\ttype: aggregate\\n \\tgroupby: [\\"stack\\", \\"grpId\\"]\\n \\tfields: [\\"size\\"]\\n \\tops: [\\"sum\\"]\\n \\tas: [\\"total\\"]\\n \\t}\\n \\t// re-calculate the stacking y0,y1 values\\n \\t{\\n \\ttype: stack\\n \\tgroupby: [\\"stack\\"]\\n \\tsort: {field: \\"grpId\\", order: \\"descending\\"}\\n \\tfield: total\\n \\t}\\n \\t// project y0 and y1 values to screen coordinates\\n \\t// doing it once here instead of doing it several times in marks\\n \\t{type: \\"formula\\", expr: \\"scale(\'y\', datum.y0)\\", as: \\"scaledY0\\"}\\n \\t{type: \\"formula\\", expr: \\"scale(\'y\', datum.y1)\\", as: \\"scaledY1\\"}\\n \\t// boolean flag if the label should be on the right of the stack\\n \\t{type: \\"formula\\", expr: \\"datum.stack == \'stk1\'\\", as: \\"rightLabel\\"}\\n \\t// Calculate traffic percentage for this country using \\"y\\" scale\\n \\t// domain upper bound, which represents the total traffic\\n \\t{\\n \\ttype: formula\\n \\texpr: datum.total/domain(\'y\')[1]\\n \\tas: percentage\\n \\t}\\n \\t]\\n\\t}\\n\\t{\\n \\t// This is a temp lookup table with all the \'stk2\' stack nodes\\n \\tname: destinationNodes\\n \\tsource: nodes\\n \\ttransform: [\\n \\t{type: \\"filter\\", expr: \\"datum.stack == \'stk2\'\\"}\\n \\t]\\n\\t}\\n\\t{\\n \\tname: edges\\n \\tsource: nodes\\n \\ttransform: [\\n \\t// we only want nodes from the left stack\\n \\t{type: \\"filter\\", expr: \\"datum.stack == \'stk1\'\\"}\\n \\t// find corresponding node from the right stack, keep it as \\"target\\"\\n \\t{\\n \\ttype: lookup\\n \\tfrom: destinationNodes\\n \\tkey: key\\n \\tfields: [\\"key\\"]\\n \\tas: [\\"target\\"]\\n \\t}\\n \\t// calculate SVG link path between stk1 and stk2 stacks for the node pair\\n \\t{\\n \\ttype: linkpath\\n \\torient: horizontal\\n \\tshape: diagonal\\n \\tsourceY: {expr: \\"scale(\'y\', datum.yc)\\"}\\n \\tsourceX: {expr: \\"scale(\'x\', \'stk1\') + bandwidth(\'x\')\\"}\\n \\ttargetY: {expr: \\"scale(\'y\', datum.target.yc)\\"}\\n \\ttargetX: {expr: \\"scale(\'x\', \'stk2\')\\"}\\n \\t}\\n \\t// A little trick to calculate the thickness of the line.\\n \\t// The value needs to be the same as the hight of the node, but scaling\\n \\t// size to screen\'s height gives inversed value because screen\'s Y\\n \\t// coordinate goes from the top to the bottom, whereas the graph\'s Y=0\\n \\t// is at the bottom. So subtracting scaled doc count from screen height\\n \\t// (which is the \\"lower\\" bound of the \\"y\\" scale) gives us the right value\\n \\t{\\n \\ttype: formula\\n \\texpr: range(\'y\')[0]-scale(\'y\', datum.size)\\n \\tas: strokeWidth\\n \\t}\\n \\t// Tooltip needs individual link\'s percentage of all traffic\\n \\t{\\n \\ttype: formula\\n \\texpr: datum.size/domain(\'y\')[1]\\n \\tas: percentage\\n \\t}\\n \\t]\\n\\t}\\n ]\\n scales: [\\n\\t{\\n \\t// calculates horizontal stack positioning\\n \\tname: x\\n \\ttype: band\\n \\trange: width\\n \\tdomain: [\\"stk1\\", \\"stk2\\"]\\n \\tpaddingOuter: 0.05\\n \\tpaddingInner: 0.95\\n\\t}\\n\\t{\\n \\t// this scale goes up as high as the highest y1 value of all nodes\\n \\tname: y\\n \\ttype: linear\\n \\trange: height\\n \\tdomain: {data: \\"nodes\\", field: \\"y1\\"}\\n\\t}\\n\\t{\\n \\t// use rawData to ensure the colors stay the same when clicking.\\n \\tname: color\\n \\ttype: ordinal\\n \\trange: category\\n \\tdomain: {data: \\"rawData\\", field: \\"stk1\\"}\\n\\t}\\n\\t{\\n \\t// this scale is used to map internal ids (stk1, stk2) to stack names\\n \\tname: stackNames\\n \\ttype: ordinal\\n \\trange: [\\"Source\\", \\"Destination\\"]\\n \\tdomain: [\\"stk1\\", \\"stk2\\"]\\n\\t}\\n ]\\n axes: [\\n\\t{\\n \\t// x axis should use custom label formatting to print proper stack names\\n \\torient: bottom\\n \\tscale: x\\n \\tencode: {\\n \\tlabels: {\\n \\tupdate: {\\n \\ttext: {scale: \\"stackNames\\", field: \\"value\\"}\\n \\t}\\n \\t}\\n \\t}\\n\\t}\\n\\t{orient: \\"left\\", scale: \\"y\\"}\\n ]\\n marks: [\\n\\t{\\n \\t// draw the connecting line between stacks\\n \\ttype: path\\n \\tname: edgeMark\\n \\tfrom: {data: \\"edges\\"}\\n \\t// this prevents some autosizing issues with large strokeWidth for paths\\n \\tclip: true\\n \\tencode: {\\n \\tupdate: {\\n \\t// By default use color of the left node, except when showing traffic\\n \\t// from just one country, in which case use destination color.\\n \\tstroke: [\\n \\t{\\n \\ttest: groupSelector && groupSelector.stack==\'stk1\'\\n \\tscale: color\\n \\tfield: stk2\\n \\t}\\n \\t{scale: \\"color\\", field: \\"stk1\\"}\\n \\t]\\n \\tstrokeWidth: {field: \\"strokeWidth\\"}\\n \\tpath: {field: \\"path\\"}\\n \\t// when showing all traffic, and hovering over a country,\\n \\t// highlight the traffic from that country.\\n \\tstrokeOpacity: {\\n \\tsignal: !groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 0.9 : 0.3\\n \\t}\\n \\t// Ensure that the hover-selected edges show on top\\n \\tzindex: {\\n \\tsignal: !groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 1 : 0\\n \\t}\\n \\t// format tooltip string\\n \\ttooltip: {\\n \\tsignal: datum.stk1 + \' → \' + datum.stk2 + \'\\t\' + format(datum.size, \',.0f\') + \' (\' + format(datum.percentage, \'.1%\') + \')\'\\n \\t}\\n \\t}\\n \\t// Simple mouseover highlighting of a single line\\n \\thover: {\\n \\tstrokeOpacity: {value: 1}\\n \\t}\\n \\t}\\n\\t}\\n\\t{\\n \\t// draw stack groups (countries)\\n \\ttype: rect\\n \\tname: groupMark\\n \\tfrom: {data: \\"groups\\"}\\n \\tencode: {\\n \\tenter: {\\n \\tfill: {scale: \\"color\\", field: \\"grpId\\"}\\n \\twidth: {scale: \\"x\\", band: 1}\\n \\t}\\n \\tupdate: {\\n \\tx: {scale: \\"x\\", field: \\"stack\\"}\\n \\ty: {field: \\"scaledY0\\"}\\n \\ty2: {field: \\"scaledY1\\"}\\n \\tfillOpacity: {value: 0.6}\\n \\ttooltip: {\\n \\tsignal: datum.grpId + \' \' + format(datum.total, \',.0f\') + \' (\' + format(datum.percentage, \'.1%\') + \')\'\\n \\t}\\n \\t}\\n \\thover: {\\n \\tfillOpacity: {value: 1}\\n \\t}\\n \\t}\\n\\t}\\n\\t{\\n \\t// draw country code labels on the inner side of the stack\\n \\ttype: text\\n \\tfrom: {data: \\"groups\\"}\\n \\t// don\'t process events for the labels - otherwise line mouseover is unclean\\n \\tinteractive: false\\n \\tencode: {\\n \\tupdate: {\\n \\t// depending on which stack it is, position x with some padding\\n \\tx: {\\n \\tsignal: scale(\'x\', datum.stack) + (datum.rightLabel ? bandwidth(\'x\') + 8 : -8)\\n \\t}\\n \\t// middle of the group\\n \\tyc: {signal: \\"(datum.scaledY0 + datum.scaledY1)/2\\"}\\n \\talign: {signal: \\"datum.rightLabel ? \'left\' : \'right\'\\"}\\n \\tbaseline: {value: \\"middle\\"}\\n \\tfontWeight: {value: \\"bold\\"}\\n \\t// only show text label if the group\'s height is large enough\\n \\ttext: {signal: \\"abs(datum.scaledY0-datum.scaledY1) > 13 ? datum.grpId : \'\'\\"}\\n \\t}\\n \\t}\\n\\t}\\n\\t{\\n \\t// Create a \\"show all\\" button. Shown only when a country is selected.\\n \\ttype: group\\n \\tdata: [\\n \\t// We need to make the button show only when groupSelector signal is true.\\n \\t// Each mark is drawn as many times as there are elements in the backing data.\\n \\t// Which means that if values list is empty, it will not be drawn.\\n \\t// Here I create a data source with one empty object, and filter that list\\n \\t// based on the signal value. This can only be done in a group.\\n \\t{\\n \\tname: dataForShowAll\\n \\tvalues: [{}]\\n \\ttransform: [{type: \\"filter\\", expr: \\"groupSelector\\"}]\\n \\t}\\n \\t]\\n \\t// Set button size and positioning\\n \\tencode: {\\n \\tenter: {\\n \\txc: {signal: \\"width/2\\"}\\n \\ty: {value: 30}\\n \\twidth: {value: 80}\\n \\theight: {value: 30}\\n \\t}\\n \\t}\\n \\tmarks: [\\n \\t{\\n \\t// This group is shown as a button with rounded corners.\\n \\ttype: group\\n \\t// mark name allows signal capturing\\n \\tname: groupReset\\n \\t// Only shows button if dataForShowAll has values.\\n \\tfrom: {data: \\"dataForShowAll\\"}\\n \\tencode: {\\n \\tenter: {\\n \\tcornerRadius: {value: 6}\\n \\tfill: {value: \\"#F5F7FA\\"}\\n \\tstroke: {value: \\"#c1c1c1\\"}\\n \\tstrokeWidth: {value: 2}\\n \\t// use parent group\'s size\\n \\theight: {\\n \\tfield: {group: \\"height\\"}\\n \\t}\\n \\twidth: {\\n \\tfield: {group: \\"width\\"}\\n \\t}\\n \\t}\\n \\tupdate: {\\n \\t// groups are transparent by default\\n \\topacity: {value: 1}\\n \\t}\\n \\thover: {\\n \\topacity: {value: 0.7}\\n \\t}\\n \\t}\\n \\tmarks: [\\n \\t{\\n \\ttype: text\\n \\t// if true, it will prevent clicking on the button when over text.\\n \\tinteractive: false\\n \\tencode: {\\n \\tenter: {\\n \\t// center text in the paren group\\n \\txc: {\\n \\tfield: {group: \\"width\\"}\\n \\tmult: 0.5\\n \\t}\\n \\tyc: {\\n \\tfield: {group: \\"height\\"}\\n \\tmult: 0.5\\n \\toffset: 2\\n \\t}\\n \\talign: {value: \\"center\\"}\\n \\tbaseline: {value: \\"middle\\"}\\n \\tfontWeight: {value: \\"bold\\"}\\n \\ttext: {value: \\"Show All\\"}\\n \\t}\\n \\t}\\n \\t}\\n \\t]\\n \\t}\\n \\t]\\n\\t}\\n ]\\n signals: [\\n\\t{\\n \\t// used to highlight traffic to/from the same country\\n \\tname: groupHover\\n \\tvalue: {}\\n \\ton: [\\n \\t{\\n \\tevents: @groupMark:mouseover\\n \\tupdate: \\"{stk1:datum.stack==\'stk1\' && datum.grpId, stk2:datum.stack==\'stk2\' && datum.grpId}\\"\\n \\t}\\n \\t{events: \\"mouseout\\", update: \\"{}\\"}\\n \\t]\\n\\t}\\n\\t// used to filter only the data related to the selected country\\n\\t{\\n \\tname: groupSelector\\n \\tvalue: false\\n \\ton: [\\n \\t{\\n \\t// Clicking groupMark sets this signal to the filter values\\n \\tevents: @groupMark:click!\\n \\tupdate: \\"{stack:datum.stack, stk1:datum.stack==\'stk1\' && datum.grpId, stk2:datum.stack==\'stk2\' && datum.grpId}\\"\\n \\t}\\n \\t{\\n \\t// Clicking \\"show all\\" button, or double-clicking anywhere resets it\\n \\tevents: [\\n \\t{type: \\"click\\", markname: \\"groupReset\\"}\\n \\t{type: \\"dblclick\\"}\\n \\t]\\n \\tupdate: \\"false\\"\\n \\t}\\n \\t]\\n\\t}\\n ]\\n}\\n"},"aggs":[]}', + '{"title":"[Logs] Machine OS and Destination Sankey Chart","type":"vega","params":{"spec":"{ \\n $schema: https://vega.github.io/schema/vega/v5.json\\n data: [\\n\\t{\\n \\t// query ES based on the currently selected time range and filter string\\n \\tname: rawData\\n \\turl: {\\n \\t%context%: true\\n \\t%timefield%: timestamp\\n \\tindex: kibana_sample_data_logs\\n \\tbody: {\\n \\tsize: 0\\n \\taggs: {\\n \\ttable: {\\n \\tcomposite: {\\n \\tsize: 10000\\n \\tsources: [\\n \\t{\\n \\tstk1: {\\n \\tterms: {field: \\"machine.os.keyword\\"}\\n \\t}\\n \\t}\\n \\t{\\n \\tstk2: {\\n \\tterms: {field: \\"geo.dest\\"}\\n \\t}\\n \\t}\\n \\t]\\n \\t}\\n \\t}\\n \\t}\\n \\t}\\n \\t}\\n \\t// From the result, take just the data we are interested in\\n \\tformat: {property: \\"aggregations.table.buckets\\"}\\n \\t// Convert key.stk1 -> stk1 for simpler access below\\n \\ttransform: [\\n \\t{type: \\"formula\\", expr: \\"datum.key.stk1\\", as: \\"stk1\\"}\\n \\t{type: \\"formula\\", expr: \\"datum.key.stk2\\", as: \\"stk2\\"}\\n \\t{type: \\"formula\\", expr: \\"datum.doc_count\\", as: \\"size\\"}\\n \\t]\\n\\t}\\n\\t{\\n \\tname: nodes\\n \\tsource: rawData\\n \\ttransform: [\\n \\t// when a country is selected, filter out unrelated data\\n \\t{\\n \\ttype: filter\\n \\texpr: !groupSelector || groupSelector.stk1 == datum.stk1 || groupSelector.stk2 == datum.stk2\\n \\t}\\n \\t// Set new key for later lookups - identifies each node\\n \\t{type: \\"formula\\", expr: \\"datum.stk1+datum.stk2\\", as: \\"key\\"}\\n \\t// instead of each table row, create two new rows,\\n \\t// one for the source (stack=stk1) and one for destination node (stack=stk2).\\n \\t// The country code stored in stk1 and stk2 fields is placed into grpId field.\\n \\t{\\n \\ttype: fold\\n \\tfields: [\\"stk1\\", \\"stk2\\"]\\n \\tas: [\\"stack\\", \\"grpId\\"]\\n \\t}\\n \\t// Create a sortkey, different for stk1 and stk2 stacks.\\n \\t{\\n \\ttype: formula\\n \\texpr: datum.stack == \'stk1\' ? datum.stk1+datum.stk2 : datum.stk2+datum.stk1\\n \\tas: sortField\\n \\t}\\n \\t// Calculate y0 and y1 positions for stacking nodes one on top of the other,\\n \\t// independently for each stack, and ensuring they are in the proper order,\\n \\t// alphabetical from the top (reversed on the y axis)\\n \\t{\\n \\ttype: stack\\n \\tgroupby: [\\"stack\\"]\\n \\tsort: {field: \\"sortField\\", order: \\"descending\\"}\\n \\tfield: size\\n \\t}\\n \\t// calculate vertical center point for each node, used to draw edges\\n \\t{type: \\"formula\\", expr: \\"(datum.y0+datum.y1)/2\\", as: \\"yc\\"}\\n \\t]\\n\\t}\\n\\t{\\n \\tname: groups\\n \\tsource: nodes\\n \\ttransform: [\\n \\t// combine all nodes into country groups, summing up the doc counts\\n \\t{\\n \\ttype: aggregate\\n \\tgroupby: [\\"stack\\", \\"grpId\\"]\\n \\tfields: [\\"size\\"]\\n \\tops: [\\"sum\\"]\\n \\tas: [\\"total\\"]\\n \\t}\\n \\t// re-calculate the stacking y0,y1 values\\n \\t{\\n \\ttype: stack\\n \\tgroupby: [\\"stack\\"]\\n \\tsort: {field: \\"grpId\\", order: \\"descending\\"}\\n \\tfield: total\\n \\t}\\n \\t// project y0 and y1 values to screen coordinates\\n \\t// doing it once here instead of doing it several times in marks\\n \\t{type: \\"formula\\", expr: \\"scale(\'y\', datum.y0)\\", as: \\"scaledY0\\"}\\n \\t{type: \\"formula\\", expr: \\"scale(\'y\', datum.y1)\\", as: \\"scaledY1\\"}\\n \\t// boolean flag if the label should be on the right of the stack\\n \\t{type: \\"formula\\", expr: \\"datum.stack == \'stk1\'\\", as: \\"rightLabel\\"}\\n \\t// Calculate traffic percentage for this country using \\"y\\" scale\\n \\t// domain upper bound, which represents the total traffic\\n \\t{\\n \\ttype: formula\\n \\texpr: datum.total/domain(\'y\')[1]\\n \\tas: percentage\\n \\t}\\n \\t]\\n\\t}\\n\\t{\\n \\t// This is a temp lookup table with all the \'stk2\' stack nodes\\n \\tname: destinationNodes\\n \\tsource: nodes\\n \\ttransform: [\\n \\t{type: \\"filter\\", expr: \\"datum.stack == \'stk2\'\\"}\\n \\t]\\n\\t}\\n\\t{\\n \\tname: edges\\n \\tsource: nodes\\n \\ttransform: [\\n \\t// we only want nodes from the left stack\\n \\t{type: \\"filter\\", expr: \\"datum.stack == \'stk1\'\\"}\\n \\t// find corresponding node from the right stack, keep it as \\"target\\"\\n \\t{\\n \\ttype: lookup\\n \\tfrom: destinationNodes\\n \\tkey: key\\n \\tfields: [\\"key\\"]\\n \\tas: [\\"target\\"]\\n \\t}\\n \\t// calculate SVG link path between stk1 and stk2 stacks for the node pair\\n \\t{\\n \\ttype: linkpath\\n \\torient: horizontal\\n \\tshape: diagonal\\n \\tsourceY: {expr: \\"scale(\'y\', datum.yc)\\"}\\n \\tsourceX: {expr: \\"scale(\'x\', \'stk1\') + bandwidth(\'x\')\\"}\\n \\ttargetY: {expr: \\"scale(\'y\', datum.target.yc)\\"}\\n \\ttargetX: {expr: \\"scale(\'x\', \'stk2\')\\"}\\n \\t}\\n \\t// A little trick to calculate the thickness of the line.\\n \\t// The value needs to be the same as the hight of the node, but scaling\\n \\t// size to screen\'s height gives inversed value because screen\'s Y\\n \\t// coordinate goes from the top to the bottom, whereas the graph\'s Y=0\\n \\t// is at the bottom. So subtracting scaled doc count from screen height\\n \\t// (which is the \\"lower\\" bound of the \\"y\\" scale) gives us the right value\\n \\t{\\n \\ttype: formula\\n \\texpr: range(\'y\')[0]-scale(\'y\', datum.size)\\n \\tas: strokeWidth\\n \\t}\\n \\t// Tooltip needs individual link\'s percentage of all traffic\\n \\t{\\n \\ttype: formula\\n \\texpr: datum.size/domain(\'y\')[1]\\n \\tas: percentage\\n \\t}\\n \\t]\\n\\t}\\n ]\\n scales: [\\n\\t{\\n \\t// calculates horizontal stack positioning\\n \\tname: x\\n \\ttype: band\\n \\trange: width\\n \\tdomain: [\\"stk1\\", \\"stk2\\"]\\n \\tpaddingOuter: 0.05\\n \\tpaddingInner: 0.95\\n\\t}\\n\\t{\\n \\t// this scale goes up as high as the highest y1 value of all nodes\\n \\tname: y\\n \\ttype: linear\\n \\trange: height\\n \\tdomain: {data: \\"nodes\\", field: \\"y1\\"}\\n\\t}\\n\\t{\\n \\t// use rawData to ensure the colors stay the same when clicking.\\n \\tname: color\\n \\ttype: ordinal\\n \\trange: category\\n \\tdomain: {data: \\"rawData\\", field: \\"stk1\\"}\\n\\t}\\n\\t{\\n \\t// this scale is used to map internal ids (stk1, stk2) to stack names\\n \\tname: stackNames\\n \\ttype: ordinal\\n \\trange: [\\"Source\\", \\"Destination\\"]\\n \\tdomain: [\\"stk1\\", \\"stk2\\"]\\n\\t}\\n ]\\n axes: [\\n\\t{\\n \\t// x axis should use custom label formatting to print proper stack names\\n \\torient: bottom\\n \\tscale: x\\n \\tencode: {\\n \\tlabels: {\\n \\tupdate: {\\n \\ttext: {scale: \\"stackNames\\", field: \\"value\\"}\\n \\t}\\n \\t}\\n \\t}\\n\\t}\\n\\t{orient: \\"left\\", scale: \\"y\\"}\\n ]\\n marks: [\\n\\t{\\n \\t// draw the connecting line between stacks\\n \\ttype: path\\n \\tname: edgeMark\\n \\tfrom: {data: \\"edges\\"}\\n \\t// this prevents some autosizing issues with large strokeWidth for paths\\n \\tclip: true\\n \\tencode: {\\n \\tupdate: {\\n \\t// By default use color of the left node, except when showing traffic\\n \\t// from just one country, in which case use destination color.\\n \\tstroke: [\\n \\t{\\n \\ttest: groupSelector && groupSelector.stack==\'stk1\'\\n \\tscale: color\\n \\tfield: stk2\\n \\t}\\n \\t{scale: \\"color\\", field: \\"stk1\\"}\\n \\t]\\n \\tstrokeWidth: {field: \\"strokeWidth\\"}\\n \\tpath: {field: \\"path\\"}\\n \\t// when showing all traffic, and hovering over a country,\\n \\t// highlight the traffic from that country.\\n \\tstrokeOpacity: {\\n \\tsignal: !groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 0.9 : 0.3\\n \\t}\\n \\t// Ensure that the hover-selected edges show on top\\n \\tzindex: {\\n \\tsignal: !groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 1 : 0\\n \\t}\\n \\t// format tooltip string\\n \\ttooltip: {\\n \\tsignal: datum.stk1 + \' → \' + datum.stk2 + \'\\t\' + format(datum.size, \',.0f\') + \' (\' + format(datum.percentage, \'.1%\') + \')\'\\n \\t}\\n \\t}\\n \\t// Simple mouseover highlighting of a single line\\n \\thover: {\\n \\tstrokeOpacity: {value: 1}\\n \\t}\\n \\t}\\n\\t}\\n\\t{\\n \\t// draw stack groups (countries)\\n \\ttype: rect\\n \\tname: groupMark\\n \\tfrom: {data: \\"groups\\"}\\n \\tencode: {\\n \\tenter: {\\n \\tfill: {scale: \\"color\\", field: \\"grpId\\"}\\n \\twidth: {scale: \\"x\\", band: 1}\\n \\t}\\n \\tupdate: {\\n \\tx: {scale: \\"x\\", field: \\"stack\\"}\\n \\ty: {field: \\"scaledY0\\"}\\n \\ty2: {field: \\"scaledY1\\"}\\n \\tfillOpacity: {value: 0.6}\\n \\ttooltip: {\\n \\tsignal: datum.grpId + \' \' + format(datum.total, \',.0f\') + \' (\' + format(datum.percentage, \'.1%\') + \')\'\\n \\t}\\n \\t}\\n \\thover: {\\n \\tfillOpacity: {value: 1}\\n \\t}\\n \\t}\\n\\t}\\n\\t{\\n \\t// draw country code labels on the inner side of the stack\\n \\ttype: text\\n \\tfrom: {data: \\"groups\\"}\\n \\t// don\'t process events for the labels - otherwise line mouseover is unclean\\n \\tinteractive: false\\n \\tencode: {\\n \\tupdate: {\\n \\t// depending on which stack it is, position x with some padding\\n \\tx: {\\n \\tsignal: scale(\'x\', datum.stack) + (datum.rightLabel ? bandwidth(\'x\') + 8 : -8)\\n \\t}\\n \\t// middle of the group\\n \\tyc: {signal: \\"(datum.scaledY0 + datum.scaledY1)/2\\"}\\n \\talign: {signal: \\"datum.rightLabel ? \'left\' : \'right\'\\"}\\n \\tbaseline: {value: \\"middle\\"}\\n \\tfontWeight: {value: \\"bold\\"}\\n \\t// only show text label if the group\'s height is large enough\\n \\ttext: {signal: \\"abs(datum.scaledY0-datum.scaledY1) > 13 ? datum.grpId : \'\'\\"}\\n \\t}\\n \\t}\\n\\t}\\n\\t{\\n \\t// Create a \\"show all\\" button. Shown only when a country is selected.\\n \\ttype: group\\n \\tdata: [\\n \\t// We need to make the button show only when groupSelector signal is true.\\n \\t// Each mark is drawn as many times as there are elements in the backing data.\\n \\t// Which means that if values list is empty, it will not be drawn.\\n \\t// Here I create a data source with one empty object, and filter that list\\n \\t// based on the signal value. This can only be done in a group.\\n \\t{\\n \\tname: dataForShowAll\\n \\tvalues: [{}]\\n \\ttransform: [{type: \\"filter\\", expr: \\"groupSelector\\"}]\\n \\t}\\n \\t]\\n \\t// Set button size and positioning\\n \\tencode: {\\n \\tenter: {\\n \\txc: {signal: \\"width/2\\"}\\n \\ty: {value: 30}\\n \\twidth: {value: 80}\\n \\theight: {value: 30}\\n \\t}\\n \\t}\\n \\tmarks: [\\n \\t{\\n \\t// This group is shown as a button with rounded corners.\\n \\ttype: group\\n \\t// mark name allows signal capturing\\n \\tname: groupReset\\n \\t// Only shows button if dataForShowAll has values.\\n \\tfrom: {data: \\"dataForShowAll\\"}\\n \\tencode: {\\n \\tenter: {\\n \\tcornerRadius: {value: 6}\\n \\tfill: {value: \\"#F5F7FA\\"}\\n \\tstroke: {value: \\"#c1c1c1\\"}\\n \\tstrokeWidth: {value: 2}\\n \\t// use parent group\'s size\\n \\theight: {\\n \\tfield: {group: \\"height\\"}\\n \\t}\\n \\twidth: {\\n \\tfield: {group: \\"width\\"}\\n \\t}\\n \\t}\\n \\tupdate: {\\n \\t// groups are transparent by default\\n \\topacity: {value: 1}\\n \\t}\\n \\thover: {\\n \\topacity: {value: 0.7}\\n \\t}\\n \\t}\\n \\tmarks: [\\n \\t{\\n \\ttype: text\\n \\t// if true, it will prevent clicking on the button when over text.\\n \\tinteractive: false\\n \\tencode: {\\n \\tenter: {\\n \\t// center text in the paren group\\n \\txc: {\\n \\tfield: {group: \\"width\\"}\\n \\tmult: 0.5\\n \\t}\\n \\tyc: {\\n \\tfield: {group: \\"height\\"}\\n \\tmult: 0.5\\n \\toffset: 2\\n \\t}\\n \\talign: {value: \\"center\\"}\\n \\tbaseline: {value: \\"middle\\"}\\n \\tfontWeight: {value: \\"bold\\"}\\n \\ttext: {value: \\"Show All\\"}\\n \\t}\\n \\t}\\n \\t}\\n \\t]\\n \\t}\\n \\t]\\n\\t}\\n ]\\n signals: [\\n\\t{\\n \\t// used to highlight traffic to/from the same country\\n \\tname: groupHover\\n \\tvalue: {}\\n \\ton: [\\n \\t{\\n \\tevents: @groupMark:mouseover\\n \\tupdate: \\"{stk1:datum.stack==\'stk1\' && datum.grpId, stk2:datum.stack==\'stk2\' && datum.grpId}\\"\\n \\t}\\n \\t{events: \\"mouseout\\", update: \\"{}\\"}\\n \\t]\\n\\t}\\n\\t// used to filter only the data related to the selected country\\n\\t{\\n \\tname: groupSelector\\n \\tvalue: false\\n \\ton: [\\n \\t{\\n \\t// Clicking groupMark sets this signal to the filter values\\n \\tevents: @groupMark:click!\\n \\tupdate: \\"{stack:datum.stack, stk1:datum.stack==\'stk1\' && datum.grpId, stk2:datum.stack==\'stk2\' && datum.grpId}\\"\\n \\t}\\n \\t{\\n \\t// Clicking \\"show all\\" button, or double-clicking anywhere resets it\\n \\tevents: [\\n \\t{type: \\"click\\", markname: \\"groupReset\\"}\\n \\t{type: \\"dblclick\\"}\\n \\t]\\n \\tupdate: \\"false\\"\\n \\t}\\n \\t]\\n\\t}\\n ]\\n}\\n"},"aggs":[]}', uiStateJSON: '{}', description: '', version: 1, @@ -144,47 +85,13 @@ export const getSavedObjects = (): SavedObject[] => [ references: [], }, { - id: '314c6f60-2224-11e8-b802-5bcf64c2dfb4', - type: 'visualization', - updated_at: '2021-07-21T18:52:13.586Z', - version: '2', - migrationVersion: { - visualization: '7.14.0', - }, - attributes: { - title: i18n.translate('home.sampleData.logsTsdbSpec.responseCodesOverTimeTitle', { - defaultMessage: '[Logs TSDB] Response Codes Over Time + Annotations', - }), - visState: - '{"title":"[Logs TSDB] Response Codes Over Time + Annotations","type":"metrics","aggs":[],"params":{"time_range_mode":"entire_time_range","id":"61ca57f0-469d-11e7-af02-69e470af7417","type":"timeseries","series":[{"id":"61ca57f1-469d-11e7-af02-69e470af7417","color":"rgba(115,216,255,1)","split_mode":"filters","metrics":[{"id":"61ca57f2-469d-11e7-af02-69e470af7417","type":"count","field":"ip"}],"seperate_axis":0,"axis_position":"right","formatter":"percent","chart_type":"line","line_width":"2","point_size":"0","fill":"0.5","stacked":"percent","terms_field":"response.keyword","terms_order_by":"61ca57f2-469d-11e7-af02-69e470af7417","label":"Response Code Count","split_color_mode":"gradient","split_filters":[{"filter":{"query":"response.keyword >= 200 and response.keyword < 400","language":"kuery"},"label":"HTTP 2xx and 3xx","color":"rgba(84,179,153,1)","id":"96b6ffe0-ea54-11eb-ad09-9f2ab44412fb"},{"filter":{"query":"response.keyword >= 400 and response.keyword < 500","language":"kuery"},"label":"HTTP 4xx","color":"rgba(214,191,87,1)","id":"9e41b1b0-ea54-11eb-ad09-9f2ab44412fb"},{"filter":{"query":"response.keyword >= 500","language":"kuery"},"label":"HTTP 5xx","color":"rgba(211,96,134,1)","id":"a6772270-ea54-11eb-ad09-9f2ab44412fb"}],"type":"timeseries"}],"time_field":"timestamp","use_kibana_indexes":true,"interval":">=4h","axis_position":"left","axis_formatter":"number","show_legend":1,"show_grid":1,"annotations":[{"fields":"geo.src, host","template":"Security Error from {{geo.src}} on {{host}}","query_string":{"query":"tags:error AND tags:security","language":"lucene"},"id":"bd7548a0-2223-11e8-832f-d5027f3c8a47","color":"rgba(211,49,21,1)","time_field":"timestamp","icon":"fa-asterisk","ignore_global_filters":1,"ignore_panel_filters":1,"index_pattern_ref_name":"metrics_1_index_pattern"}],"legend_position":"bottom","axis_scale":"normal","drop_last_bucket":0,"tooltip_mode":"show_all","index_pattern_ref_name":"metrics_0_index_pattern"}}', - uiStateJSON: '{}', - description: '', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: '{"query":{"query":"","language":"kuery"},"filter":[]}', - }, - }, - references: [ - { - id: '90943e30-9a47-11e8-b64d-95841ca0c247', - name: 'metrics_0_index_pattern', - type: 'index-pattern', - }, - { - id: '90943e30-9a47-11e8-b64d-95841ca0c247', - name: 'metrics_1_index_pattern', - type: 'index-pattern', - }, - ], - }, - { - id: '16b1d7d0-ea71-11eb-8b4b-f7b600de1f7d', + id: '16b1d7d0-ea71-11eb-8b4b-f7b600de0f7d', type: 'lens', updated_at: '2021-07-21T22:14:59.793Z', version: '1', - migrationVersion: { - lens: '7.14.0', - }, + coreMigrationVersion: '8.8.0', + typeMigrationVersion: '7.14.0', + managed: false, attributes: { title: i18n.translate('home.sampleData.logsTsdbSpec.bytesDistributionTitle', { defaultMessage: '[Logs TSDB] Bytes distribution', @@ -367,7 +274,6 @@ export const getSavedObjects = (): SavedObject[] => [ type: 'index-pattern', updated_at: '2018-08-29T13:22:17.617Z', version: '1', - migrationVersion: {}, attributes: { title: 'kibana_sample_data_logstsdb', name: 'Kibana Sample Data Logs (TSDB)', @@ -382,27 +288,10 @@ export const getSavedObjects = (): SavedObject[] => [ id: 'edf84fe0-e1a0-11e7-b6d5-4dc382ef8f5b', type: 'dashboard', namespaces: ['default'], - updated_at: '2022-09-26T16:24:51.698Z', - version: 'WzE1NTIsMV0=', + updated_at: '2023-03-23T16:25:27.102Z', + created_at: '2023-03-23T16:25:27.102Z', + version: 'WzEzMjAsMV0=', attributes: { - title: i18n.translate('home.sampleData.logsTsdbSpec.webTrafficTitle', { - defaultMessage: '[Logs TSDB] Web Traffic', - }), - hits: 0, - description: i18n.translate('home.sampleData.logsTsdbSpec.webTrafficDescription', { - defaultMessage: "Analyze mock web traffic log data for Elastic's website", - }), - panelsJSON: - '[{"version":"8.6.0","type":"visualization","gridData":{"x":0,"y":14,"w":24,"h":18,"i":"4"},"panelIndex":"4","embeddableConfig":{"isLayerTOCOpen":false,"enhancements":{},"mapCenter":{"lat":42.16337,"lon":-88.92107,"zoom":3.64},"mapBuffer":{"minLon":-112.5,"minLat":21.94305,"maxLon":-45,"maxLat":55.77657},"openTOCDetails":[],"hiddenLayers":[]},"panelRefName":"panel_4"},{"version":"8.6.0","type":"visualization","gridData":{"x":0,"y":47,"w":24,"h":13,"i":"9"},"panelIndex":"9","embeddableConfig":{"mapCenter":[36.8092847020594,-96.94335937500001],"vis":{"params":{"sort":{"columnIndex":null,"direction":null}}},"enhancements":{}},"panelRefName":"panel_9"},{"version":"8.6.0","type":"visualization","gridData":{"x":36,"y":0,"w":12,"h":7,"i":"11"},"panelIndex":"11","embeddableConfig":{"vis":{"colors":{"0 - 500":"#BF1B00","1000 - 1500":"#7EB26D","500 - 1000":"#F2C96D"},"defaultColors":{"0 - 500":"rgb(165,0,38)","1000 - 1500":"rgb(0,104,55)","500 - 1000":"rgb(255,255,190)"},"legendOpen":false},"enhancements":{},"hidePanelTitles":true},"title":"","panelRefName":"panel_11"},{"version":"8.6.0","type":"visualization","gridData":{"x":24,"y":14,"w":24,"h":33,"i":"14"},"panelIndex":"14","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_14"},{"version":"8.6.0","type":"visualization","gridData":{"x":0,"y":7,"w":24,"h":7,"i":"15"},"panelIndex":"15","embeddableConfig":{"enhancements":{"dynamicActions":{"events":[]}}},"panelRefName":"panel_15"},{"version":"8.6.0","type":"visualization","gridData":{"x":0,"y":0,"w":24,"h":7,"i":"343f0bef-0b19-452e-b1c8-59beb18b6f0c"},"panelIndex":"343f0bef-0b19-452e-b1c8-59beb18b6f0c","embeddableConfig":{"savedVis":{"title":"[Logs] Markdown Instructions","description":"","type":"markdown","params":{"fontSize":12,"openLinksInNewTab":true,"markdown":"## Sample Logs Data\\nThis dashboard contains sample data for you to play with. You can view it, search it, and interact with the visualizations. For more information about Kibana, check our [docs](https://www.elastic.co/guide/en/kibana/current/index.html)."},"uiState":{},"data":{"aggs":[],"searchSource":{"query":{"query":"","language":"kuery"},"filter":[]}}},"enhancements":{},"hidePanelTitles":true}},{"version":"8.6.0","type":"lens","gridData":{"x":24,"y":0,"w":12,"h":7,"i":"bb94016e-f4a6-49ca-87a9-296a2869d570"},"panelIndex":"bb94016e-f4a6-49ca-87a9-296a2869d570","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsLegacyMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"483defd2-775b-4a62-bdef-496c819bb8ed":{"columns":{"37430d12-7452-4cc9-b035-5cfd4061edf0":{"label":"Visits","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true}},"columnOrder":["37430d12-7452-4cc9-b035-5cfd4061edf0"],"incompleteColumns":{}}}}},"visualization":{"layerId":"483defd2-775b-4a62-bdef-496c819bb8ed","accessor":"37430d12-7452-4cc9-b035-5cfd4061edf0","layerType":"data","textAlign":"center","titlePosition":"bottom","size":"xl"},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-layer-483defd2-775b-4a62-bdef-496c819bb8ed","type":"index-pattern"}]},"enhancements":{}}},{"version":"8.6.0","type":"lens","gridData":{"x":24,"y":7,"w":12,"h":7,"i":"01d8e435-91c0-484f-a11e-856747050b0a"},"panelIndex":"01d8e435-91c0-484f-a11e-856747050b0a","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsLegacyMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"f3793bb7-3971-4753-866d-4008e77a9f9a":{"columns":{"71c076a6-e782-4866-b8df-5fd85a41f08bX0":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","filter":{"query":"response.keyword >= 400 and response.keyword < 500","language":"kuery"},"customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08bX1":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08bX2":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1"],"location":{"min":0,"max":73},"text":"count(kql=\'response.keyword >= 400 and response.keyword < 500\') / count()"}},"references":["71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1"],"customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08b":{"label":"HTTP 4xx","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'response.keyword >= 400 and response.keyword < 500\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["71c076a6-e782-4866-b8df-5fd85a41f08bX2"],"customLabel":true}},"columnOrder":["71c076a6-e782-4866-b8df-5fd85a41f08b","71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1","71c076a6-e782-4866-b8df-5fd85a41f08bX2"],"incompleteColumns":{}}}}},"visualization":{"layerId":"f3793bb7-3971-4753-866d-4008e77a9f9a","accessor":"71c076a6-e782-4866-b8df-5fd85a41f08b","layerType":"data","textAlign":"center","titlePosition":"bottom","size":"xl"},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-layer-f3793bb7-3971-4753-866d-4008e77a9f9a","type":"index-pattern"}]},"enhancements":{}}},{"version":"8.6.0","type":"lens","gridData":{"x":36,"y":7,"w":12,"h":7,"i":"8c1456d4-1993-4ba2-b701-04aca02c9fef"},"panelIndex":"8c1456d4-1993-4ba2-b701-04aca02c9fef","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsLegacyMetric","state":{"datasourceStates":{"indexpattern":{"layers":{"f3793bb7-3971-4753-866d-4008e77a9f9a":{"columns":{"71c076a6-e782-4866-b8df-5fd85a41f08bX0":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","filter":{"query":"response.keyword >= 500","language":"kuery"},"customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08bX1":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08bX2":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1"],"location":{"min":0,"max":46},"text":"count(kql=\'response.keyword >= 500\') / count()"}},"references":["71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1"],"customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08b":{"label":"HTTP 5xx","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'response.keyword >= 500\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["71c076a6-e782-4866-b8df-5fd85a41f08bX2"],"customLabel":true}},"columnOrder":["71c076a6-e782-4866-b8df-5fd85a41f08b","71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1","71c076a6-e782-4866-b8df-5fd85a41f08bX2"],"incompleteColumns":{}}}}},"visualization":{"layerId":"f3793bb7-3971-4753-866d-4008e77a9f9a","accessor":"71c076a6-e782-4866-b8df-5fd85a41f08b","layerType":"data","textAlign":"center","titlePosition":"bottom","size":"xl"},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-layer-f3793bb7-3971-4753-866d-4008e77a9f9a","type":"index-pattern"}]},"enhancements":{}}},{"version":"8.6.0","type":"visualization","gridData":{"x":0,"y":32,"w":24,"h":15,"i":"8e59c7cf-6e42-4343-a113-c4a255fcf2ce"},"panelIndex":"8e59c7cf-6e42-4343-a113-c4a255fcf2ce","embeddableConfig":{"savedVis":{"title":"","description":"","type":"vega","params":{"spec":"{\\n $schema: https://vega.github.io/schema/vega-lite/v5.json\\n data: {\\n url: {\\n %context%: true\\n %timefield%: @timestamp\\n index: kibana_sample_data_logstsdb\\n body: {\\n aggs: {\\n countries: {\\n terms: {\\n field: geo.src\\n size: 25\\n }\\n aggs: {\\n hours: {\\n histogram: {\\n field: hour_of_day\\n interval: 1\\n }\\n aggs: {\\n unique: {\\n cardinality: {\\n field: clientip\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n size: 0\\n }\\n }\\n format: {property: \\"aggregations.countries.buckets\\"}\\n }\\n \\n transform: [\\n {\\n flatten: [\\"hours.buckets\\"],\\n as: [\\"buckets\\"]\\n }\\n ]\\n\\n mark: {\\n type: rect\\n tooltip: true\\n }\\n\\n encoding: {\\n x: {\\n field: buckets.key\\n type: ordinal\\n axis: {\\n title: false\\n labelAngle: 0\\n }\\n }\\n y: {\\n field: key\\n type: nominal\\n sort: {\\n field: -buckets.unique.value\\n }\\n axis: {title: false}\\n }\\n color: {\\n field: buckets.unique.value\\n type: quantitative\\n axis: {title: false}\\n scale: {\\n scheme: reds\\n }\\n }\\n }\\n}\\n"},"uiState":{},"data":{"aggs":[],"searchSource":{"query":{"query":"","language":"kuery"},"filter":[]}}},"enhancements":{}},"panelRefName":"panel_8e59c7cf-6e42-4343-a113-c4a255fcf2ce"},{"version":"8.6.0","type":"lens","gridData":{"x":24,"y":47,"w":24,"h":13,"i":"cbca842c-b9fa-4523-9ce0-14e350866e33"},"panelIndex":"cbca842c-b9fa-4523-9ce0-14e350866e33","embeddableConfig":{"hidePanelTitles":false,"enhancements":{}},"title":"[Logs] Bytes distribution","panelRefName":"panel_cbca842c-b9fa-4523-9ce0-14e350866e33"},{"version":"8.6.0","type":"lens","gridData":{"x":0,"y":60,"w":48,"h":19,"i":"1d5f0b3f-d9d2-4b26-997b-83bc5ca3090b"},"panelIndex":"1d5f0b3f-d9d2-4b26-997b-83bc5ca3090b","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsDatatable","state":{"datasourceStates":{"indexpattern":{"layers":{"c35dc8ee-50d1-4ef7-8b4b-9c21a7e7d3b0":{"columns":{"42783ad7-dbcf-4310-bc06-f21f4eaaac96":{"label":"URL","dataType":"string","operationType":"terms","scale":"ordinal","sourceField":"url.keyword","isBucketed":true,"params":{"size":1000,"orderBy":{"type":"column","columnId":"f7835375-4d5b-4839-95ea-41928192a319"},"orderDirection":"desc","otherBucket":true,"missingBucket":false},"customLabel":true},"f7835375-4d5b-4839-95ea-41928192a319":{"label":"Visits","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"07fc84ca-4147-4ba9-879e-d1b4e086e1daX0":{"label":"Part of HTTP 4xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","filter":{"query":"response.keyword >= 400 and response.keyword < 500","language":"kuery"},"customLabel":true},"07fc84ca-4147-4ba9-879e-d1b4e086e1daX1":{"label":"Part of HTTP 4xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"07fc84ca-4147-4ba9-879e-d1b4e086e1daX2":{"label":"Part of HTTP 4xx","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["07fc84ca-4147-4ba9-879e-d1b4e086e1daX0","07fc84ca-4147-4ba9-879e-d1b4e086e1daX1"],"location":{"min":0,"max":73},"text":"count(kql=\'response.keyword >= 400 and response.keyword < 500\') / count()"}},"references":["07fc84ca-4147-4ba9-879e-d1b4e086e1daX0","07fc84ca-4147-4ba9-879e-d1b4e086e1daX1"],"customLabel":true},"07fc84ca-4147-4ba9-879e-d1b4e086e1da":{"label":"HTTP 4xx","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'response.keyword >= 400 and response.keyword < 500\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["07fc84ca-4147-4ba9-879e-d1b4e086e1daX2"],"customLabel":true},"791d5a5b-a7ba-4e9e-b533-51b33c7d7747":{"label":"Unique","dataType":"number","operationType":"unique_count","scale":"ratio","sourceField":"clientip","isBucketed":false,"customLabel":true},"611e3509-e834-4fdd-b573-44e959e95d27":{"label":"95th percentile of bytes","dataType":"number","operationType":"percentile","sourceField":"bytes","isBucketed":false,"scale":"ratio","params":{"percentile":95,"format":{"id":"bytes","params":{"decimals":0}}}},"9f79ecca-123f-4098-a658-6b0e998da003":{"label":"Median of bytes","dataType":"number","operationType":"median","sourceField":"bytes","isBucketed":false,"scale":"ratio","params":{"format":{"id":"bytes","params":{"decimals":0}}}},"491285fd-0196-402c-9b7f-4660fdc1c22aX0":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","filter":{"query":"response.keyword >= 500","language":"kuery"},"customLabel":true},"491285fd-0196-402c-9b7f-4660fdc1c22aX1":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"491285fd-0196-402c-9b7f-4660fdc1c22aX2":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["491285fd-0196-402c-9b7f-4660fdc1c22aX0","491285fd-0196-402c-9b7f-4660fdc1c22aX1"],"location":{"min":0,"max":46},"text":"count(kql=\'response.keyword >= 500\') / count()"}},"references":["491285fd-0196-402c-9b7f-4660fdc1c22aX0","491285fd-0196-402c-9b7f-4660fdc1c22aX1"],"customLabel":true},"491285fd-0196-402c-9b7f-4660fdc1c22a":{"label":"HTTP 5xx","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'response.keyword >= 500\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["491285fd-0196-402c-9b7f-4660fdc1c22aX2"],"customLabel":true}},"columnOrder":["42783ad7-dbcf-4310-bc06-f21f4eaaac96","f7835375-4d5b-4839-95ea-41928192a319","791d5a5b-a7ba-4e9e-b533-51b33c7d7747","07fc84ca-4147-4ba9-879e-d1b4e086e1da","491285fd-0196-402c-9b7f-4660fdc1c22a","491285fd-0196-402c-9b7f-4660fdc1c22aX0","491285fd-0196-402c-9b7f-4660fdc1c22aX1","491285fd-0196-402c-9b7f-4660fdc1c22aX2","07fc84ca-4147-4ba9-879e-d1b4e086e1daX0","07fc84ca-4147-4ba9-879e-d1b4e086e1daX1","07fc84ca-4147-4ba9-879e-d1b4e086e1daX2","611e3509-e834-4fdd-b573-44e959e95d27","9f79ecca-123f-4098-a658-6b0e998da003"],"incompleteColumns":{}}}}},"visualization":{"layerId":"c35dc8ee-50d1-4ef7-8b4b-9c21a7e7d3b0","columns":[{"columnId":"42783ad7-dbcf-4310-bc06-f21f4eaaac96","width":650.6666666666666},{"columnId":"f7835375-4d5b-4839-95ea-41928192a319"},{"columnId":"491285fd-0196-402c-9b7f-4660fdc1c22a","isTransposed":false,"width":81.66666666666669,"colorMode":"cell","palette":{"name":"custom","type":"palette","params":{"steps":5,"stops":[{"color":"#fbddd6","stop":0.1},{"color":"#CC5642","stop":1}],"rangeType":"number","name":"custom","colorStops":[{"color":"#fbddd6","stop":0.05},{"color":"#CC5642","stop":0.1}],"rangeMin":0.05,"rangeMax":0.1}}},{"columnId":"07fc84ca-4147-4ba9-879e-d1b4e086e1da","isTransposed":false,"colorMode":"cell","palette":{"name":"custom","type":"palette","params":{"steps":5,"stops":[{"color":"#fbddd6","stop":0.1},{"color":"#cc5642","stop":1.1}],"name":"custom","colorStops":[{"color":"#fbddd6","stop":0.05},{"color":"#cc5642","stop":0.1}],"rangeType":"number","rangeMin":0.05,"rangeMax":0.1}}},{"columnId":"791d5a5b-a7ba-4e9e-b533-51b33c7d7747","isTransposed":false},{"columnId":"611e3509-e834-4fdd-b573-44e959e95d27","isTransposed":false},{"columnId":"9f79ecca-123f-4098-a658-6b0e998da003","isTransposed":false}],"sorting":{"columnId":"491285fd-0196-402c-9b7f-4660fdc1c22a","direction":"desc"},"layerType":"data","rowHeight":"single","rowHeightLines":1},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"indexpattern-datasource-layer-c35dc8ee-50d1-4ef7-8b4b-9c21a7e7d3b0","type":"index-pattern"}]},"enhancements":{"dynamicActions":{"events":[]}},"hidePanelTitles":false},"title":"[Logs] Errors by host"}]', - optionsJSON: '{"hidePanelTitles":false,"useMargins":true}', - version: 2, - timeRestore: true, - timeTo: 'now', - timeFrom: 'now-7d', - refreshInterval: { - pause: false, - value: 900000, - }, controlGroupInput: { controlStyle: 'oneLine', chainingSystem: 'HIERARCHICAL', @@ -412,9 +301,26 @@ export const getSavedObjects = (): SavedObject[] => [ '{"ignoreFilters":false,"ignoreQuery":false,"ignoreTimerange":false,"ignoreValidations":false}', }, kibanaSavedObjectMeta: { - searchSourceJSON: - '{"query":{"language":"kuery","query":""},"highlightAll":true,"version":true,"filter":[]}', + searchSourceJSON: '{"query":{"language":"kuery","query":""},"filter":[]}', + }, + description: i18n.translate('home.sampleData.logsTsdbSpec.webTrafficDescription', { + defaultMessage: "Analyze mock web traffic log data for Elastic's website", + }), + refreshInterval: { + pause: false, + value: 900000, }, + timeRestore: true, + optionsJSON: + '{"useMargins":true,"syncColors":false,"syncCursor":true,"syncTooltips":false,"hidePanelTitles":false}', + panelsJSON: + '[{"version":"8.8.0","type":"map","gridData":{"x":0,"y":14,"w":24,"h":18,"i":"4"},"panelIndex":"4","embeddableConfig":{"isLayerTOCOpen":false,"hiddenLayers":[],"mapCenter":{"lat":42.16337,"lon":-88.92107,"zoom":3.64},"openTOCDetails":[],"enhancements":{}},"panelRefName":"panel_4"},{"version":"8.8.0","type":"lens","gridData":{"x":36,"y":0,"w":12,"h":7,"i":"11"},"panelIndex":"11","embeddableConfig":{"attributes":{"title":"","visualizationType":"lnsMetric","type":"lens","references":[{"type":"index-pattern","id":"90943e30-9a47-11e8-b64d-95841ca0c247","name":"indexpattern-datasource-layer-28b89898-3feb-415a-8dd9-74d755ac7c2a"}],"state":{"visualization":{"layerId":"28b89898-3feb-415a-8dd9-74d755ac7c2a","layerType":"data","metricAccessor":"f92c482e-1eee-4c2a-9338-64fb3eec286a","palette":{"name":"custom","type":"palette","params":{"steps":3,"name":"custom","reverse":false,"rangeType":"number","rangeMin":0,"rangeMax":null,"progression":"fixed","stops":[{"color":"#D23115","stop":500},{"color":"#FCC400","stop":1000},{"color":"#68BC00","stop":1658}],"colorStops":[{"color":"#D23115","stop":0},{"color":"#FCC400","stop":500},{"color":"#68BC00","stop":1000}],"continuity":"above","maxSteps":5}}},"query":{"query":"","language":"kuery"},"filters":[],"datasourceStates":{"formBased":{"layers":{"28b89898-3feb-415a-8dd9-74d755ac7c2a":{"columns":{"f92c482e-1eee-4c2a-9338-64fb3eec286a":{"label":"Unique Visitors","dataType":"number","operationType":"unique_count","scale":"ratio","sourceField":"clientip","isBucketed":false,"params":{"emptyAsNull":true},"customLabel":true}},"columnOrder":["f92c482e-1eee-4c2a-9338-64fb3eec286a"],"incompleteColumns":{}}}},"textBased":{"layers":{}}},"internalReferences":[],"adHocDataViews":{}}},"hidePanelTitles":true,"enhancements":{}}},{"version":"8.8.0","type":"visualization","gridData":{"x":24,"y":14,"w":24,"h":33,"i":"14"},"panelIndex":"14","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_14"},{"version":"8.8.0","type":"lens","gridData":{"x":0,"y":7,"w":24,"h":7,"i":"15"},"panelIndex":"15","embeddableConfig":{"attributes":{"title":"[Logs] Response Codes Over Time + Annotations (converted)","visualizationType":"lnsXY","type":"lens","references":[{"type":"index-pattern","id":"90943e30-9a47-11e8-b64d-95841ca0c247","name":"indexpattern-datasource-layer-b38fe501-4b47-4de8-a423-6656d1162174"},{"type":"index-pattern","id":"90943e30-9a47-11e8-b64d-95841ca0c247","name":"xy-visualization-layer-f265e722-ae38-495c-903c-48aa7931fa82"}],"state":{"visualization":{"legend":{"isVisible":true,"showSingleSeries":true,"position":"bottom","shouldTruncate":true,"maxLines":1},"valueLabels":"hide","fittingFunction":"None","fillOpacity":0.5,"yLeftExtent":{"mode":"full"},"yRightExtent":{"mode":"full"},"yLeftScale":"linear","yRightScale":"linear","axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"labelsOrientation":{"x":0,"yLeft":0,"yRight":0},"gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"preferredSeriesType":"bar_stacked","layers":[{"seriesType":"area_percentage_stacked","layerType":"data","layerId":"b38fe501-4b47-4de8-a423-6656d1162174","accessors":["896c5eb2-81c5-44f1-a4a1-57344161ea62"],"yConfig":[{"forAccessor":"896c5eb2-81c5-44f1-a4a1-57344161ea62","color":"rgba(115,216,255,1)","axisMode":"left"}],"xAccessor":"8986e393-d24f-49b0-96ca-118fd66d75e5","splitAccessor":"43f5bb0f-c6da-43a0-8a0a-50e9838ed34b","palette":{"name":"default","type":"palette"}},{"layerId":"f265e722-ae38-495c-903c-48aa7931fa82","layerType":"annotations","ignoreGlobalFilters":true,"annotations":[{"type":"query","id":"bd7548a0-2223-11e8-832f-d5027f3c8a47","label":"Event","key":{"type":"point_in_time"},"color":"#D33115","timeField":"timestamp","icon":"asterisk","filter":{"type":"kibana_query","query":"tags:error AND tags:security","language":"lucene"},"extraFields":["geo.src"]}]}]},"query":{"query":"","language":"kuery"},"filters":[],"datasourceStates":{"formBased":{"layers":{"b38fe501-4b47-4de8-a423-6656d1162174":{"columns":{"8986e393-d24f-49b0-96ca-118fd66d75e5":{"label":"timestamp","dataType":"date","operationType":"date_histogram","sourceField":"timestamp","isBucketed":true,"scale":"interval","params":{"interval":"auto","includeEmptyRows":true,"dropPartials":false}},"43f5bb0f-c6da-43a0-8a0a-50e9838ed34b":{"label":"Filters","dataType":"string","operationType":"filters","scale":"ordinal","isBucketed":true,"params":{"filters":[{"input":{"query":"response.keyword >= 200 and response.keyword < 400","language":"kuery"},"label":"HTTP 2xx and 3xx"},{"input":{"query":"response.keyword >= 400 and response.keyword < 500","language":"kuery"},"label":"HTTP 4xx"},{"input":{"query":"response.keyword >= 500","language":"kuery"},"label":"HTTP 5xx"}]}},"896c5eb2-81c5-44f1-a4a1-57344161ea62":{"label":"Response Code Count","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","params":{"emptyAsNull":true},"customLabel":true}},"columnOrder":["8986e393-d24f-49b0-96ca-118fd66d75e5","43f5bb0f-c6da-43a0-8a0a-50e9838ed34b","896c5eb2-81c5-44f1-a4a1-57344161ea62"],"incompleteColumns":{}}}},"textBased":{"layers":{}}},"internalReferences":[],"adHocDataViews":{}}},"enhancements":{},"hidePanelTitles":false},"title":"[Logs] Response Codes Over Time + Annotations"},{"version":"8.8.0","type":"visualization","gridData":{"x":0,"y":0,"w":24,"h":7,"i":"343f0bef-0b19-452e-b1c8-59beb18b6f0c"},"panelIndex":"343f0bef-0b19-452e-b1c8-59beb18b6f0c","embeddableConfig":{"savedVis":{"title":"[Logs] Markdown Instructions","description":"","type":"markdown","params":{"fontSize":12,"openLinksInNewTab":true,"markdown":"## Sample Logs Data\\nThis dashboard contains sample data for you to play with. You can view it, search it, and interact with the visualizations. For more information about Kibana, check our [docs](https://www.elastic.co/guide/en/kibana/current/index.html)."},"uiState":{},"data":{"aggs":[],"searchSource":{"query":{"query":"","language":"kuery"},"filter":[]}}},"enhancements":{},"hidePanelTitles":true}},{"version":"8.8.0","type":"lens","gridData":{"x":24,"y":0,"w":12,"h":7,"i":"bb94016e-f4a6-49ca-87a9-296a2869d570"},"panelIndex":"bb94016e-f4a6-49ca-87a9-296a2869d570","embeddableConfig":{"attributes":{"title":"","visualizationType":"lnsMetric","type":"lens","references":[{"type":"index-pattern","id":"90943e30-9a47-11e8-b64d-95841ca0c247","name":"indexpattern-datasource-layer-483defd2-775b-4a62-bdef-496c819bb8ed"}],"state":{"visualization":{"layerId":"483defd2-775b-4a62-bdef-496c819bb8ed","layerType":"data","metricAccessor":"37430d12-7452-4cc9-b035-5cfd4061edf0"},"query":{"query":"","language":"kuery"},"filters":[],"datasourceStates":{"formBased":{"layers":{"483defd2-775b-4a62-bdef-496c819bb8ed":{"columns":{"37430d12-7452-4cc9-b035-5cfd4061edf0":{"label":"Visits","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true}},"columnOrder":["37430d12-7452-4cc9-b035-5cfd4061edf0"],"incompleteColumns":{}}}}},"internalReferences":[],"adHocDataViews":{}}},"enhancements":{}}},{"version":"8.8.0","type":"lens","gridData":{"x":36,"y":7,"w":12,"h":7,"i":"8c1456d4-1993-4ba2-b701-04aca02c9fef"},"panelIndex":"8c1456d4-1993-4ba2-b701-04aca02c9fef","embeddableConfig":{"attributes":{"title":"","visualizationType":"lnsMetric","type":"lens","references":[{"type":"index-pattern","id":"90943e30-9a47-11e8-b64d-95841ca0c247","name":"indexpattern-datasource-layer-f3793bb7-3971-4753-866d-4008e77a9f9a"}],"state":{"visualization":{"layerId":"f3793bb7-3971-4753-866d-4008e77a9f9a","layerType":"data","metricAccessor":"71c076a6-e782-4866-b8df-5fd85a41f08b"},"query":{"query":"","language":"kuery"},"filters":[],"datasourceStates":{"formBased":{"layers":{"f3793bb7-3971-4753-866d-4008e77a9f9a":{"columns":{"71c076a6-e782-4866-b8df-5fd85a41f08bX0":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","filter":{"query":"response.keyword >= 500","language":"kuery"},"params":{"emptyAsNull":false},"customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08bX1":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","params":{"emptyAsNull":false},"customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08bX2":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1"],"location":{"min":0,"max":46},"text":"count(kql=\'response.keyword >= 500\') / count()"}},"references":["71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1"],"customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08b":{"label":"HTTP 5xx","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'response.keyword >= 500\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["71c076a6-e782-4866-b8df-5fd85a41f08bX2"],"customLabel":true}},"columnOrder":["71c076a6-e782-4866-b8df-5fd85a41f08b","71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1","71c076a6-e782-4866-b8df-5fd85a41f08bX2"],"incompleteColumns":{}}}}},"internalReferences":[],"adHocDataViews":{}}},"enhancements":{}}},{"version":"8.8.0","type":"lens","gridData":{"x":24,"y":7,"w":12,"h":7,"i":"01d8e435-91c0-484f-a11e-856747050b0a"},"panelIndex":"01d8e435-91c0-484f-a11e-856747050b0a","embeddableConfig":{"attributes":{"title":"","visualizationType":"lnsMetric","type":"lens","references":[{"type":"index-pattern","id":"90943e30-9a47-11e8-b64d-95841ca0c247","name":"indexpattern-datasource-layer-f3793bb7-3971-4753-866d-4008e77a9f9a"}],"state":{"visualization":{"layerId":"f3793bb7-3971-4753-866d-4008e77a9f9a","layerType":"data","metricAccessor":"71c076a6-e782-4866-b8df-5fd85a41f08b"},"query":{"query":"","language":"kuery"},"filters":[],"datasourceStates":{"formBased":{"layers":{"f3793bb7-3971-4753-866d-4008e77a9f9a":{"columns":{"71c076a6-e782-4866-b8df-5fd85a41f08bX0":{"label":"Part of HTTP 4xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","filter":{"query":"response.keyword >= 400 and response.keyword < 500","language":"kuery"},"params":{"emptyAsNull":false},"customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08bX1":{"label":"Part of HTTP 4xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","params":{"emptyAsNull":false},"customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08bX2":{"label":"Part of HTTP 4xx","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1"],"location":{"min":0,"max":73},"text":"count(kql=\'response.keyword >= 400 and response.keyword < 500\') / count()"}},"references":["71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1"],"customLabel":true},"71c076a6-e782-4866-b8df-5fd85a41f08b":{"label":"HTTP 4xx","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'response.keyword >= 400 and response.keyword < 500\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["71c076a6-e782-4866-b8df-5fd85a41f08bX2"],"customLabel":true}},"columnOrder":["71c076a6-e782-4866-b8df-5fd85a41f08b","71c076a6-e782-4866-b8df-5fd85a41f08bX0","71c076a6-e782-4866-b8df-5fd85a41f08bX1","71c076a6-e782-4866-b8df-5fd85a41f08bX2"],"incompleteColumns":{}}}}},"internalReferences":[],"adHocDataViews":{}}},"enhancements":{}}},{"version":"8.8.0","type":"visualization","gridData":{"x":0,"y":32,"w":24,"h":15,"i":"8e59c7cf-6e42-4343-a113-c4a255fcf2ce"},"panelIndex":"8e59c7cf-6e42-4343-a113-c4a255fcf2ce","embeddableConfig":{"savedVis":{"title":"","description":"","type":"vega","params":{"spec":"{\\n $schema: https://vega.github.io/schema/vega-lite/v5.json\\n data: {\\n url: {\\n %context%: true\\n %timefield%: @timestamp\\n index: kibana_sample_data_logs\\n body: {\\n aggs: {\\n countries: {\\n terms: {\\n field: geo.src\\n size: 25\\n }\\n aggs: {\\n hours: {\\n histogram: {\\n field: hour_of_day\\n interval: 1\\n }\\n aggs: {\\n unique: {\\n cardinality: {\\n field: clientip\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n size: 0\\n }\\n }\\n format: {property: \\"aggregations.countries.buckets\\"}\\n }\\n \\n transform: [\\n {\\n flatten: [\\"hours.buckets\\"],\\n as: [\\"buckets\\"]\\n }\\n ]\\n\\n mark: {\\n type: rect\\n tooltip: true\\n }\\n\\n encoding: {\\n x: {\\n field: buckets.key\\n type: ordinal\\n axis: {\\n title: false\\n labelAngle: 0\\n }\\n }\\n y: {\\n field: key\\n type: nominal\\n sort: {\\n field: -buckets.unique.value\\n }\\n axis: {title: false}\\n }\\n color: {\\n field: buckets.unique.value\\n type: quantitative\\n axis: {title: false}\\n scale: {\\n scheme: reds\\n }\\n }\\n }\\n}\\n"},"uiState":{},"data":{"aggs":[],"searchSource":{"query":{"query":"","language":"kuery"},"filter":[]}}},"enhancements":{}},"panelRefName":"panel_8e59c7cf-6e42-4343-a113-c4a255fcf2ce"},{"version":"8.8.0","type":"lens","gridData":{"x":0,"y":47,"w":24,"h":13,"i":"21bb0939-ee09-4021-8848-6552b3a6a788"},"panelIndex":"21bb0939-ee09-4021-8848-6552b3a6a788","embeddableConfig":{"attributes":{"title":"","visualizationType":"lnsDatatable","type":"lens","references":[{"type":"index-pattern","id":"90943e30-9a47-11e8-b64d-95841ca0c247","name":"indexpattern-datasource-layer-c840e93e-2949-4723-ad35-6bdb2d724404"}],"state":{"visualization":{"columns":[{"columnId":"4e64d6d7-4f92-4d5e-abbb-13796604db30","isTransposed":false},{"columnId":"fb9a848d-76f3-4005-a067-4259a50b5621","isTransposed":false},{"columnId":"a2760bc2-9a6e-46a1-8595-86f61573c7cf","isTransposed":false},{"columnId":"2c8bd8d5-35ff-4386-8d27-3ba882b13e43","isTransposed":false,"colorMode":"text","palette":{"name":"custom","type":"palette","params":{"steps":5,"stops":[{"color":"#d23115","stop":1000},{"color":"#fcc400","stop":1500},{"color":"#68bc00","stop":1501}],"rangeType":"number","rangeMin":0,"rangeMax":null,"continuity":"above","colorStops":[{"color":"#d23115","stop":0},{"color":"#fcc400","stop":1000},{"color":"#68bc00","stop":1500}],"name":"custom"}}},{"columnId":"defa6f97-b874-4556-8438-056fb437787b","isTransposed":false,"colorMode":"text","palette":{"name":"custom","type":"palette","params":{"steps":5,"stops":[{"color":"#D23115","stop":10},{"color":"#FCC400","stop":25},{"color":"#68bc00","stop":26}],"rangeType":"number","rangeMin":0,"rangeMax":null,"continuity":"above","colorStops":[{"color":"#D23115","stop":0},{"color":"#FCC400","stop":10},{"color":"#68bc00","stop":25}],"name":"custom"}}}],"layerId":"c840e93e-2949-4723-ad35-6bdb2d724404","layerType":"data"},"query":{"query":"","language":"kuery"},"filters":[],"datasourceStates":{"formBased":{"layers":{"c840e93e-2949-4723-ad35-6bdb2d724404":{"columns":{"4e64d6d7-4f92-4d5e-abbb-13796604db30":{"label":"Type","dataType":"string","operationType":"terms","scale":"ordinal","sourceField":"extension.keyword","isBucketed":true,"params":{"size":10,"orderBy":{"type":"column","columnId":"fb9a848d-76f3-4005-a067-4259a50b5621"},"orderDirection":"desc","otherBucket":true,"missingBucket":false,"parentFormat":{"id":"terms"},"include":[],"exclude":[],"includeIsRegex":false,"excludeIsRegex":false},"customLabel":true},"fb9a848d-76f3-4005-a067-4259a50b5621":{"label":"Bytes (Total)","dataType":"number","operationType":"sum","sourceField":"bytes","isBucketed":false,"scale":"ratio","params":{"emptyAsNull":true,"format":{"id":"bytes","params":{"decimals":2}}},"customLabel":true},"a2760bc2-9a6e-46a1-8595-86f61573c7cf":{"label":"Bytes (Last Hour)","dataType":"number","operationType":"sum","sourceField":"bytes","isBucketed":false,"scale":"ratio","reducedTimeRange":"1h","params":{"emptyAsNull":true,"format":{"id":"bytes","params":{"decimals":2}}},"customLabel":true},"2c8bd8d5-35ff-4386-8d27-3ba882b13e43":{"label":"Unique Visits (Total)","dataType":"number","operationType":"unique_count","scale":"ratio","sourceField":"clientip","isBucketed":false,"params":{"emptyAsNull":true},"customLabel":true},"defa6f97-b874-4556-8438-056fb437787b":{"label":"Unique count of clientip","dataType":"number","operationType":"unique_count","scale":"ratio","sourceField":"clientip","isBucketed":false,"reducedTimeRange":"1h","params":{"emptyAsNull":true}}},"columnOrder":["4e64d6d7-4f92-4d5e-abbb-13796604db30","fb9a848d-76f3-4005-a067-4259a50b5621","a2760bc2-9a6e-46a1-8595-86f61573c7cf","2c8bd8d5-35ff-4386-8d27-3ba882b13e43","defa6f97-b874-4556-8438-056fb437787b"],"sampling":1,"incompleteColumns":{}}}},"textBased":{"layers":{}}},"internalReferences":[],"adHocDataViews":{}}},"enhancements":{}}},{"version":"8.8.0","type":"lens","gridData":{"x":24,"y":47,"w":24,"h":13,"i":"cbca842c-b9fa-4523-9ce0-14e350866e33"},"panelIndex":"cbca842c-b9fa-4523-9ce0-14e350866e33","embeddableConfig":{"hidePanelTitles":false,"enhancements":{}},"title":"[Logs] Bytes distribution","panelRefName":"panel_cbca842c-b9fa-4523-9ce0-14e350866e33"},{"version":"8.8.0","type":"lens","gridData":{"x":0,"y":60,"w":48,"h":19,"i":"1d5f0b3f-d9d2-4b26-997b-83bc5ca3090b"},"panelIndex":"1d5f0b3f-d9d2-4b26-997b-83bc5ca3090b","embeddableConfig":{"attributes":{"title":"","type":"lens","visualizationType":"lnsDatatable","state":{"datasourceStates":{"formBased":{"layers":{"c35dc8ee-50d1-4ef7-8b4b-9c21a7e7d3b0":{"columns":{"42783ad7-dbcf-4310-bc06-f21f4eaaac96":{"label":"URL","dataType":"string","operationType":"terms","scale":"ordinal","sourceField":"url","isBucketed":true,"params":{"size":1000,"orderBy":{"type":"column","columnId":"f7835375-4d5b-4839-95ea-41928192a319"},"orderDirection":"desc","otherBucket":true,"missingBucket":false},"customLabel":true},"f7835375-4d5b-4839-95ea-41928192a319":{"label":"Visits","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"07fc84ca-4147-4ba9-879e-d1b4e086e1daX0":{"label":"Part of HTTP 4xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","filter":{"query":"response.keyword >= 400 and response.keyword < 500","language":"kuery"},"customLabel":true},"07fc84ca-4147-4ba9-879e-d1b4e086e1daX1":{"label":"Part of HTTP 4xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"07fc84ca-4147-4ba9-879e-d1b4e086e1daX2":{"label":"Part of HTTP 4xx","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["07fc84ca-4147-4ba9-879e-d1b4e086e1daX0","07fc84ca-4147-4ba9-879e-d1b4e086e1daX1"],"location":{"min":0,"max":73},"text":"count(kql=\'response.keyword >= 400 and response.keyword < 500\') / count()"}},"references":["07fc84ca-4147-4ba9-879e-d1b4e086e1daX0","07fc84ca-4147-4ba9-879e-d1b4e086e1daX1"],"customLabel":true},"07fc84ca-4147-4ba9-879e-d1b4e086e1da":{"label":"HTTP 4xx","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'response.keyword >= 400 and response.keyword < 500\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["07fc84ca-4147-4ba9-879e-d1b4e086e1daX2"],"customLabel":true},"791d5a5b-a7ba-4e9e-b533-51b33c7d7747":{"label":"Unique","dataType":"number","operationType":"unique_count","scale":"ratio","sourceField":"clientip","isBucketed":false,"customLabel":true},"611e3509-e834-4fdd-b573-44e959e95d27":{"label":"95th percentile of bytes","dataType":"number","operationType":"percentile","sourceField":"bytes","isBucketed":false,"scale":"ratio","params":{"percentile":95,"format":{"id":"bytes","params":{"decimals":0}}}},"9f79ecca-123f-4098-a658-6b0e998da003":{"label":"Median of bytes","dataType":"number","operationType":"median","sourceField":"bytes","isBucketed":false,"scale":"ratio","params":{"format":{"id":"bytes","params":{"decimals":0}}}},"491285fd-0196-402c-9b7f-4660fdc1c22aX0":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","filter":{"query":"response.keyword >= 500","language":"kuery"},"customLabel":true},"491285fd-0196-402c-9b7f-4660fdc1c22aX1":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"count","isBucketed":false,"scale":"ratio","sourceField":"___records___","customLabel":true},"491285fd-0196-402c-9b7f-4660fdc1c22aX2":{"label":"Part of HTTP 5xx","dataType":"number","operationType":"math","isBucketed":false,"scale":"ratio","params":{"tinymathAst":{"type":"function","name":"divide","args":["491285fd-0196-402c-9b7f-4660fdc1c22aX0","491285fd-0196-402c-9b7f-4660fdc1c22aX1"],"location":{"min":0,"max":46},"text":"count(kql=\'response.keyword >= 500\') / count()"}},"references":["491285fd-0196-402c-9b7f-4660fdc1c22aX0","491285fd-0196-402c-9b7f-4660fdc1c22aX1"],"customLabel":true},"491285fd-0196-402c-9b7f-4660fdc1c22a":{"label":"HTTP 5xx","dataType":"number","operationType":"formula","isBucketed":false,"scale":"ratio","params":{"formula":"count(kql=\'response.keyword >= 500\') / count()","isFormulaBroken":false,"format":{"id":"percent","params":{"decimals":1}}},"references":["491285fd-0196-402c-9b7f-4660fdc1c22aX2"],"customLabel":true}},"columnOrder":["42783ad7-dbcf-4310-bc06-f21f4eaaac96","f7835375-4d5b-4839-95ea-41928192a319","791d5a5b-a7ba-4e9e-b533-51b33c7d7747","07fc84ca-4147-4ba9-879e-d1b4e086e1da","491285fd-0196-402c-9b7f-4660fdc1c22a","491285fd-0196-402c-9b7f-4660fdc1c22aX0","491285fd-0196-402c-9b7f-4660fdc1c22aX1","491285fd-0196-402c-9b7f-4660fdc1c22aX2","07fc84ca-4147-4ba9-879e-d1b4e086e1daX0","07fc84ca-4147-4ba9-879e-d1b4e086e1daX1","07fc84ca-4147-4ba9-879e-d1b4e086e1daX2","611e3509-e834-4fdd-b573-44e959e95d27","9f79ecca-123f-4098-a658-6b0e998da003"],"incompleteColumns":{}}}}},"visualization":{"layerId":"c35dc8ee-50d1-4ef7-8b4b-9c21a7e7d3b0","columns":[{"columnId":"42783ad7-dbcf-4310-bc06-f21f4eaaac96","width":650.6666666666666},{"columnId":"f7835375-4d5b-4839-95ea-41928192a319"},{"columnId":"491285fd-0196-402c-9b7f-4660fdc1c22a","isTransposed":false,"width":81.66666666666669,"colorMode":"cell","palette":{"name":"custom","type":"palette","params":{"steps":5,"stops":[{"color":"#fbddd6","stop":0.1},{"color":"#CC5642","stop":1}],"rangeType":"number","name":"custom","colorStops":[{"color":"#fbddd6","stop":0.05},{"color":"#CC5642","stop":0.1}],"rangeMin":0.05,"rangeMax":0.1}}},{"columnId":"07fc84ca-4147-4ba9-879e-d1b4e086e1da","isTransposed":false,"colorMode":"cell","palette":{"name":"custom","type":"palette","params":{"steps":5,"stops":[{"color":"#fbddd6","stop":0.1},{"color":"#cc5642","stop":1.1}],"name":"custom","colorStops":[{"color":"#fbddd6","stop":0.05},{"color":"#cc5642","stop":0.1}],"rangeType":"number","rangeMin":0.05,"rangeMax":0.1}}},{"columnId":"791d5a5b-a7ba-4e9e-b533-51b33c7d7747","isTransposed":false},{"columnId":"611e3509-e834-4fdd-b573-44e959e95d27","isTransposed":false},{"columnId":"9f79ecca-123f-4098-a658-6b0e998da003","isTransposed":false}],"sorting":{"columnId":"491285fd-0196-402c-9b7f-4660fdc1c22a","direction":"desc"},"layerType":"data","rowHeight":"single","rowHeightLines":1},"query":{"query":"","language":"kuery"},"filters":[]},"references":[{"id":"90943e30-9a47-11e8-b64d-95841ca0c247","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"90943e30-9a47-11e8-b64d-95841ca0c247","name":"indexpattern-datasource-layer-c35dc8ee-50d1-4ef7-8b4b-9c21a7e7d3b0","type":"index-pattern"}]},"enhancements":{"dynamicActions":{"events":[]}},"hidePanelTitles":false},"title":"[Logs] Errors by host"}]', + timeFrom: 'now-7d/d', + title: i18n.translate('home.sampleData.logsTsdbSpec.webTrafficTitle', { + defaultMessage: '[Logs TSDB] Web Traffic', + }), + timeTo: 'now', + version: 1, }, references: [ { @@ -423,14 +329,9 @@ export const getSavedObjects = (): SavedObject[] => [ id: '06cf9c40-9ee8-11e7-8711-e7a007dcef99', }, { - name: '9:panel_9', - type: 'visualization', - id: '4eb6e500-e1c7-11e7-b6d5-4dc382ef7f5b', - }, - { - name: '11:panel_11', - type: 'visualization', - id: '69a34b00-9ee8-11e7-8711-e7a007dcef99', + type: 'index-pattern', + id: '90943e30-9a47-11e8-b64d-95841ca0c247', + name: '11:indexpattern-datasource-layer-28b89898-3feb-415a-8dd9-74d755ac7c2a', }, { name: '14:panel_14', @@ -438,45 +339,40 @@ export const getSavedObjects = (): SavedObject[] => [ id: '7cbd2350-2223-11e8-b802-5bcf64c2cfb4', }, { - name: '15:panel_15', - type: 'visualization', - id: '314c6f60-2224-11e8-b802-5bcf64c2cfb4', + type: 'index-pattern', + id: '90943e30-9a47-11e8-b64d-95841ca0c247', + name: '15:indexpattern-datasource-layer-b38fe501-4b47-4de8-a423-6656d1162174', }, { - id: '90943e30-9a47-11e8-b64d-95841ca0c247', - name: 'bb94016e-f4a6-49ca-87a9-296a2869d570:indexpattern-datasource-current-indexpattern', type: 'index-pattern', + id: '90943e30-9a47-11e8-b64d-95841ca0c247', + name: '15:xy-visualization-layer-f265e722-ae38-495c-903c-48aa7931fa82', }, { + type: 'index-pattern', id: '90943e30-9a47-11e8-b64d-95841ca0c247', name: 'bb94016e-f4a6-49ca-87a9-296a2869d570:indexpattern-datasource-layer-483defd2-775b-4a62-bdef-496c819bb8ed', - type: 'index-pattern', }, { - id: '90943e30-9a47-11e8-b64d-95841ca0c247', - name: '01d8e435-91c0-484f-a11e-856747050b0a:indexpattern-datasource-current-indexpattern', type: 'index-pattern', - }, - { id: '90943e30-9a47-11e8-b64d-95841ca0c247', - name: '01d8e435-91c0-484f-a11e-856747050b0a:indexpattern-datasource-layer-f3793bb7-3971-4753-866d-4008e77a9f9a', - type: 'index-pattern', + name: '8c1456d4-1993-4ba2-b701-04aca02c9fef:indexpattern-datasource-layer-f3793bb7-3971-4753-866d-4008e77a9f9a', }, { - id: '90943e30-9a47-11e8-b64d-95841ca0c247', - name: '8c1456d4-1993-4ba2-b701-04aca02c9fef:indexpattern-datasource-current-indexpattern', type: 'index-pattern', - }, - { id: '90943e30-9a47-11e8-b64d-95841ca0c247', - name: '8c1456d4-1993-4ba2-b701-04aca02c9fef:indexpattern-datasource-layer-f3793bb7-3971-4753-866d-4008e77a9f9a', - type: 'index-pattern', + name: '01d8e435-91c0-484f-a11e-856747050b0a:indexpattern-datasource-layer-f3793bb7-3971-4753-866d-4008e77a9f9a', }, { name: '8e59c7cf-6e42-4343-a113-c4a255fcf2ce:panel_8e59c7cf-6e42-4343-a113-c4a255fcf2ce', type: 'visualization', id: 'cb099a20-ea66-11eb-9425-113343a037e3', }, + { + type: 'index-pattern', + id: '90943e30-9a47-11e8-b64d-95841ca0c247', + name: '21bb0939-ee09-4021-8848-6552b3a6a788:indexpattern-datasource-layer-c840e93e-2949-4723-ad35-6bdb2d724404', + }, { name: 'cbca842c-b9fa-4523-9ce0-14e350866e33:panel_cbca842c-b9fa-4523-9ce0-14e350866e33', type: 'lens', @@ -508,19 +404,18 @@ export const getSavedObjects = (): SavedObject[] => [ id: '90943e30-9a47-11e8-b64d-95841ca0c247', }, ], - migrationVersion: { - dashboard: '8.5.0', - }, - coreMigrationVersion: '8.6.0', + coreMigrationVersion: '8.8.0', + typeMigrationVersion: '8.7.0', + managed: false, }, { - id: '2f360f30-ea74-11eb-b4c6-3d2afc1cc389', + id: '2f360f30-ea74-11eb-b4c6-3d2afc1cb389', type: 'search', updated_at: '2021-07-21T22:37:09.415Z', version: '1', - migrationVersion: { - search: '7.9.3', - }, + coreMigrationVersion: '8.8.0', + typeMigrationVersion: '7.9.3', + managed: false, attributes: { title: i18n.translate('home.sampleData.logsSpec.discoverTitle', { defaultMessage: '[Logs] Visits', diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 1e8ea9f239326..af5ab8709ddd7 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -3508,10 +3508,7 @@ "home.sampleData.logsSpecDescription": "Exemple de données, de visualisations et de tableaux de bord pour le monitoring des logs Internet.", "home.sampleData.logsSpecTitle": "Exemple de logs Internet", "home.sampleData.logsTsdbSpec.bytesDistributionTitle": "[Logs TSDB] Distribution des octets", - "home.sampleData.logsTsdbSpec.goalsTitle": "[Logs TSDB] Objectifs", "home.sampleData.logsTsdbSpec.heatmapTitle": "[Logs TSDB] Carte thermique de destination unique", - "home.sampleData.logsTsdbSpec.hostVisitsBytesTableTitle": "[Logs TSDB] Tableau des hôtes, visites et octets", - "home.sampleData.logsTsdbSpec.responseCodesOverTimeTitle": "[Logs TSDB] Codes de réponse sur la durée + annotations", "home.sampleData.logsTsdbSpec.sourceAndDestinationSankeyChartTitle": "[Logs TSDB] Diagramme de Sankey de système d'exploitation source et destination", "home.sampleData.logsTsdbSpec.visitorsMapTitle": "[Logs TSDB] Carte des visiteurs", "home.sampleData.logsTsdbSpec.webTrafficDescription": "Analyser des données de log factices relatives au trafic Internet du site d'Elastic", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 73d492e7031d1..62471657fa93b 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3523,10 +3523,7 @@ "home.sampleData.logsSpecDescription": "Web ログを監視するサンプルデータ、ビジュアライゼーション、ダッシュボードです。", "home.sampleData.logsSpecTitle": "サンプル Web ログ", "home.sampleData.logsTsdbSpec.bytesDistributionTitle": "[Logs TSDB] バイト分布", - "home.sampleData.logsTsdbSpec.goalsTitle": "[Logs TSDB] 目標", "home.sampleData.logsTsdbSpec.heatmapTitle": "[Logs TSDB] 一意のターゲットヒートマップ", - "home.sampleData.logsTsdbSpec.hostVisitsBytesTableTitle": "[Logs TSDB] ホスト、訪問数、バイト表", - "home.sampleData.logsTsdbSpec.responseCodesOverTimeTitle": "[Logs TSDB] 一定期間の応答コードと注釈", "home.sampleData.logsTsdbSpec.sourceAndDestinationSankeyChartTitle": "[Logs TSDB] コンピューターOSとターゲットサンキーダイアグラム", "home.sampleData.logsTsdbSpec.visitorsMapTitle": "[Logs TSDB] 訪問者マップ", "home.sampleData.logsTsdbSpec.webTrafficDescription": "Elastic Web サイトのサンプル Webトラフィックログデータを分析します", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 3a87fc2238ee0..79d0a35c8851d 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3522,10 +3522,7 @@ "home.sampleData.logsSpecDescription": "用于监测 Web 日志的样例数据、可视化和仪表板。", "home.sampleData.logsSpecTitle": "样例 Web 日志", "home.sampleData.logsTsdbSpec.bytesDistributionTitle": "[日志 TSDB] 字节分布", - "home.sampleData.logsTsdbSpec.goalsTitle": "[日志 TSDB] 目标", "home.sampleData.logsTsdbSpec.heatmapTitle": "[日志 TSDB] 唯一目标热图", - "home.sampleData.logsTsdbSpec.hostVisitsBytesTableTitle": "[日志 TSDB] 主机、访问和字节表", - "home.sampleData.logsTsdbSpec.responseCodesOverTimeTitle": "[日志 TSDB] 时移响应代码 + 注释", "home.sampleData.logsTsdbSpec.sourceAndDestinationSankeyChartTitle": "[日志 TSDB] 机器 OS 和目标 Sankey 图", "home.sampleData.logsTsdbSpec.visitorsMapTitle": "[日志 TSDB] 访客地图", "home.sampleData.logsTsdbSpec.webTrafficDescription": "分析 Elastic 网站的模拟 Web 流量日志数据", From 94bd19d747581fd5eec2eddcd1eb7275cca8ba33 Mon Sep 17 00:00:00 2001 From: Vadim Kibana <82822460+vadimkibana@users.noreply.github.com> Date: Wed, 27 Sep 2023 10:49:31 +0200 Subject: [PATCH 05/12] use EUI components for tutorial params (#167014) ## Summary Partially addresses https://github.com/elastic/kibana/issues/46410 Stops using `kuiTextInput` CSS class name in `number_parameter.js` and `string_paramter.js` components in the `home` plugin. How to test? I don't know if these parameters are still used, so to test this apply this patch: ```diff diff --git a/src/plugins/home/public/application/components/tutorial/instruction_set.js b/src/plugins/home/public/application/components/tutorial/instruction_set.js index 651212f062c..7f2077a322d 100644 --- a/src/plugins/home/public/application/components/tutorial/instruction_set.js +++ b/src/plugins/home/public/application/components/tutorial/instruction_set.js @@ -261,14 +261,20 @@ class InstructionSetUi extends React.Component { render() { let paramsForm; - if (this.props.params && this.state.isParamFormVisible) { + if (true) { paramsForm = ( <> + PARAMETER FORM { + console.log('setParameter', id, value); + }} /> ); ``` And go to `/app/home#/tutorial/apm` page, you will see the new parameter input look there: image Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/tutorial/number_parameter.js | 18 ++++-------------- .../components/tutorial/string_parameter.js | 10 ++++------ 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/plugins/home/public/application/components/tutorial/number_parameter.js b/src/plugins/home/public/application/components/tutorial/number_parameter.js index 41ad9767ab003..b2ac171393929 100644 --- a/src/plugins/home/public/application/components/tutorial/number_parameter.js +++ b/src/plugins/home/public/application/components/tutorial/number_parameter.js @@ -8,6 +8,7 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { EuiFormRow, EuiFieldNumber } from '@elastic/eui'; export function NumberParameter({ id, label, value, setParameter }) { const handleChange = (evt) => { @@ -15,20 +16,9 @@ export function NumberParameter({ id, label, value, setParameter }) { }; return ( -
- -
- -
-
+ + + ); } diff --git a/src/plugins/home/public/application/components/tutorial/string_parameter.js b/src/plugins/home/public/application/components/tutorial/string_parameter.js index 459a0ab8c1a53..4c8ad157f10fe 100644 --- a/src/plugins/home/public/application/components/tutorial/string_parameter.js +++ b/src/plugins/home/public/application/components/tutorial/string_parameter.js @@ -8,6 +8,7 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { EuiFormRow, EuiFieldText } from '@elastic/eui'; export function StringParameter({ id, label, value, setParameter }) { const handleChange = (evt) => { @@ -15,12 +16,9 @@ export function StringParameter({ id, label, value, setParameter }) { }; return ( -
- -
- -
-
+ + + ); } From cd962314d740acc7811b619fdbf147eee9cf2f52 Mon Sep 17 00:00:00 2001 From: Brad White Date: Wed, 27 Sep 2023 03:29:02 -0600 Subject: [PATCH 06/12] Pick enterprise search frontend from #166813 (#167350) ## Summary We're breaking https://github.com/elastic/kibana/pull/166813 up into smaller PRs in the interest of getting PRs through sooner for type fixes. These are the changes for Enterprise Search Frontend. --------- Co-authored-by: Alex Szabo --- x-pack/plugins/enterprise_search/server/plugin.ts | 2 +- x-pack/plugins/serverless_search/public/layout/nav.tsx | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/x-pack/plugins/enterprise_search/server/plugin.ts b/x-pack/plugins/enterprise_search/server/plugin.ts index 5dd5e56b2c3a1..02444bd6efa72 100644 --- a/x-pack/plugins/enterprise_search/server/plugin.ts +++ b/x-pack/plugins/enterprise_search/server/plugin.ts @@ -94,7 +94,7 @@ interface PluginsSetup { usageCollection?: UsageCollectionSetup; } -interface PluginsStart { +export interface PluginsStart { data: DataPluginStart; security: SecurityPluginStart; spaces?: SpacesPluginStart; diff --git a/x-pack/plugins/serverless_search/public/layout/nav.tsx b/x-pack/plugins/serverless_search/public/layout/nav.tsx index 10d963100f89c..047b490fcb137 100644 --- a/x-pack/plugins/serverless_search/public/layout/nav.tsx +++ b/x-pack/plugins/serverless_search/public/layout/nav.tsx @@ -16,14 +16,6 @@ import { i18n } from '@kbn/i18n'; import type { ServerlessPluginStart } from '@kbn/serverless/public'; import type { CloudStart } from '@kbn/cloud-plugin/public'; -// Hiding this until page is in a better space -const _connectorItem = { - link: 'serverlessConnectors', - title: i18n.translate('xpack.serverlessSearch.nav.connectors', { - defaultMessage: 'Connectors', - }), -}; - const navigationTree: NavigationTreeDefinition = { body: [ { type: 'recentlyAccessed' }, From 077e87b51471afc20aabcaede5be0d9566a52124 Mon Sep 17 00:00:00 2001 From: Brad White Date: Wed, 27 Sep 2023 03:42:54 -0600 Subject: [PATCH 07/12] Pick Infra UI from #166813 (#167348) ## Summary We're breaking https://github.com/elastic/kibana/pull/166813 up into smaller PRs in the interest of getting PRs through sooner for type fixes. These are the changes for Infra UI. --- x-pack/plugins/asset_manager/server/routes/assets/hosts.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/plugins/asset_manager/server/routes/assets/hosts.ts b/x-pack/plugins/asset_manager/server/routes/assets/hosts.ts index 2b1088990334c..68ed4baaeb5c8 100644 --- a/x-pack/plugins/asset_manager/server/routes/assets/hosts.ts +++ b/x-pack/plugins/asset_manager/server/routes/assets/hosts.ts @@ -47,12 +47,15 @@ export function hostsRoutes({ async (context, req, res) => { const { from = 'now-24h', to = 'now' } = req.query || {}; const esClient = await getEsClientFromContext(context); + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; try { const response = await assetAccessor.getHosts({ from: datemath.parse(from)!.toISOString(), to: datemath.parse(to)!.toISOString(), esClient, + soClient, }); return res.ok({ body: response }); From f477c97ccfe9e4204b012230853848c92295861e Mon Sep 17 00:00:00 2001 From: Konrad Szwarc Date: Wed, 27 Sep 2023 11:50:49 +0200 Subject: [PATCH 08/12] [Defend Workflows] Artifact rollout UIUX changes (#167040) This PR addresses requested changes to the UI/UX part of Protection Updates tab on policy detail view. Previously we were automatically saving `global_manifest_version` on "toggle on" state. With these changes a bottom bar, mimicking Policy settings tab, with a save button that is active whenever changes were made was introduced. https://github.com/elastic/kibana/assets/29123534/98c43bbd-1ee2-4fbd-8105-2bb5fcdbc4af --- .../cypress/e2e/policy/policy_details.cy.ts | 27 +- .../protection_updates_bottom_bar.tsx | 94 +++++++ .../hooks/use_post_protection_updates_note.ts | 2 +- .../protection_updates_layout.tsx | 261 +++++++++--------- 4 files changed, 246 insertions(+), 138 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/components/protection_updates_bottom_bar.tsx diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/policy/policy_details.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/policy/policy_details.cy.ts index 6e95c1ad73557..c8db6ff223ff4 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/policy/policy_details.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/policy/policy_details.cy.ts @@ -61,6 +61,7 @@ describe( cy.getByTestSubj('protection-updates-manifest-switch'); cy.getByTestSubj('protection-updates-manifest-name-title'); cy.getByTestSubj('protection-updates-manifest-name'); + cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); cy.getByTestSubj('protection-updates-manifest-switch').click(); @@ -72,17 +73,18 @@ describe( }); cy.getByTestSubj('protection-updates-manifest-name-note-title'); cy.getByTestSubj('protection-updates-manifest-note'); - cy.getByTestSubj('policyDetailsSaveButton'); + cy.getByTestSubj('protectionUpdatesSaveButton').should('be.enabled'); }); it('should successfully update the manifest version to custom date', () => { loadProtectionUpdatesUrl(policy.id); + cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); cy.getByTestSubj('protection-updates-manifest-switch').click(); cy.getByTestSubj('protection-updates-manifest-note').type(testNote); cy.intercept('PUT', `/api/fleet/package_policies/${policy.id}`).as('policy'); - cy.intercept('POST', `/api/endpoint/protection_updates_note/*`).as('note'); - cy.getByTestSubj('policyDetailsSaveButton').click(); + cy.intercept('POST', `/api/endpoint/protection_updates_note/${policy.id}`).as('note'); + cy.getByTestSubj('protectionUpdatesSaveButton').click(); cy.wait('@policy').then(({ request, response }) => { expect(request.body.inputs[0].config.policy.value.global_manifest_version).to.equal( today.format('YYYY-MM-DD') @@ -98,6 +100,7 @@ describe( cy.getByTestSubj('protectionUpdatesSuccessfulMessage'); cy.getByTestSubj('protection-updates-deployed-version').contains(formattedToday); cy.getByTestSubj('protection-updates-manifest-note').contains(testNote); + cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); }); }); @@ -131,9 +134,13 @@ describe( it('should update manifest version to latest when enabling automatic updates', () => { loadProtectionUpdatesUrl(policy.id); cy.getByTestSubj('protection-updates-manifest-outdated'); - cy.intercept('PUT', `/api/fleet/package_policies/${policy.id}`).as('policy_latest'); + cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); cy.getByTestSubj('protection-updates-manifest-switch').click(); + cy.intercept('PUT', `/api/fleet/package_policies/${policy.id}`).as('policy_latest'); + + cy.getByTestSubj('protectionUpdatesSaveButton').click(); + cy.wait('@policy_latest').then(({ request, response }) => { expect(request.body.inputs[0].config.policy.value.global_manifest_version).to.equal( 'latest' @@ -142,6 +149,7 @@ describe( }); cy.getByTestSubj('protectionUpdatesSuccessfulMessage'); cy.getByTestSubj('protection-updates-automatic-updates-enabled'); + cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); }); }); @@ -175,18 +183,23 @@ describe( it('should update note on save', () => { loadProtectionUpdatesUrl(policy.id); + cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); + cy.getByTestSubj('protection-updates-manifest-note').contains(testNote); cy.getByTestSubj('protection-updates-manifest-note').clear(); cy.getByTestSubj('protection-updates-manifest-note').type(updatedTestNote); - cy.intercept('POST', `/api/endpoint/protection_updates_note/*`).as('note_updated'); - cy.getByTestSubj('policyDetailsSaveButton').click(); + cy.intercept('POST', `/api/endpoint/protection_updates_note/${policy.id}`).as( + 'note_updated' + ); + cy.getByTestSubj('protectionUpdatesSaveButton').click(); cy.wait('@note_updated').then(({ request, response }) => { expect(request.body.note).to.equal(updatedTestNote); expect(response?.statusCode).to.equal(200); }); cy.getByTestSubj('protectionUpdatesSuccessfulMessage'); cy.getByTestSubj('protection-updates-manifest-note').contains(updatedTestNote); + cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); }); }); @@ -238,7 +251,7 @@ describe( cy.getByTestSubj('protection-updates-manifest-name-note-title'); cy.getByTestSubj('protection-updates-manifest-note').should('not.exist'); cy.getByTestSubj('protection-updates-manifest-note-view-mode').contains(testNote); - cy.getByTestSubj('policyDetailsSaveButton').should('be.disabled'); + cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); }); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/components/protection_updates_bottom_bar.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/components/protection_updates_bottom_bar.tsx new file mode 100644 index 0000000000000..da9ec04799d6f --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/components/protection_updates_bottom_bar.tsx @@ -0,0 +1,94 @@ +/* + * 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 { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiPageTemplate, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React, { useEffect, useMemo, useState } from 'react'; +import { useLocation } from 'react-router-dom'; +import type { ApplicationStart } from '@kbn/core-application-browser'; +import { useNavigateToAppEventHandler } from '../../../../../../common/hooks/endpoint/use_navigate_to_app_event_handler'; +import { APP_UI_ID } from '../../../../../../../common'; +import { getPoliciesPath } from '../../../../../common/routing'; +import type { PolicyDetailsRouteState } from '../../../../../../../common/endpoint/types'; + +interface ProtectionUpdatesBottomBarProps { + saveButtonDisabled: boolean; + isUpdating: boolean; + onSave: () => void; +} + +export const ProtectionUpdatesBottomBar = React.memo( + ({ isUpdating, onSave, saveButtonDisabled }) => { + const { state: locationRouteState } = useLocation(); + const [routeState, setRouteState] = useState(); + const routingOnCancelNavigateTo = routeState?.onCancelNavigateTo; + + useEffect(() => { + if (!routeState && locationRouteState) { + setRouteState(locationRouteState); + } + }, [locationRouteState, routeState]); + + const navigateToAppArguments = useMemo((): Parameters => { + if (routingOnCancelNavigateTo) { + return routingOnCancelNavigateTo; + } + + return [ + APP_UI_ID, + { + path: getPoliciesPath(), + }, + ]; + }, [routingOnCancelNavigateTo]); + + const handleCancelOnClick = useNavigateToAppEventHandler(...navigateToAppArguments); + + return ( + + + + + + + + + + + + + + + ); + } +); + +ProtectionUpdatesBottomBar.displayName = 'ProtectionUpdatesBottomBar'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/hooks/use_post_protection_updates_note.ts b/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/hooks/use_post_protection_updates_note.ts index 16b6ee66f07ca..abe21b420dc83 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/hooks/use_post_protection_updates_note.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/hooks/use_post_protection_updates_note.ts @@ -32,7 +32,7 @@ export const useCreateProtectionUpdatesNote = ({ >( (payload) => http.post( - resolvePathVariables(PROTECTION_UPDATES_NOTE_ROUTE, { policy_id: packagePolicyId }), + resolvePathVariables(PROTECTION_UPDATES_NOTE_ROUTE, { package_policy_id: packagePolicyId }), { version: '2023-10-31', body: JSON.stringify(payload), diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/protection_updates_layout.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/protection_updates_layout.tsx index eaac2595c931b..54445f6ff5fdd 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/protection_updates_layout.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/protection_updates_layout.tsx @@ -7,7 +7,6 @@ import type { EuiSwitchEvent } from '@elastic/eui'; import { - EuiButton, EuiCallOut, EuiDatePicker, EuiFlexGroup, @@ -30,6 +29,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import type { Moment } from 'moment'; import moment from 'moment'; import { cloneDeep } from 'lodash'; +import { ProtectionUpdatesBottomBar } from './components/protection_updates_bottom_bar'; import { useCreateProtectionUpdatesNote } from './hooks/use_post_protection_updates_note'; import { useGetProtectionUpdatesNote } from './hooks/use_get_protection_updates_note'; import { useUserPrivileges } from '../../../../../common/components/user_privileges'; @@ -95,70 +95,80 @@ export const ProtectionUpdatesLayout = React.memo( ? AUTOMATIC_UPDATES_CHECKBOX_LABEL : AUTOMATIC_UPDATES_OFF_CHECKBOX_LABEL; - const onSave = useCallback( - (version: string) => { - const update = cloneDeep(policy); - update.inputs[0].config.policy.value.global_manifest_version = version; - sendPolicyUpdate({ policy: update }) - .then(({ item: policyItem }) => { - toasts.addSuccess({ - 'data-test-subj': 'protectionUpdatesSuccessfulMessage', - title: i18n.translate( - 'xpack.securitySolution.endpoint.protectionUpdates.updateSuccessTitle', - { - defaultMessage: 'Success!', - } - ), - text: i18n.translate( - 'xpack.securitySolution.endpoint.protectionUpdates.updateSuccessMessage', - { - defaultMessage: 'Manifest updates successfully saved', - } - ), - }); + const saveButtonEnabled = + (fetchedNote ? note !== fetchedNote.note : note !== '') || + manifestVersion !== deployedVersion; - // Since the 'policyItem' is stored in a store and fetched as a result of an action on urlChange, we still need to dispatch an action even though Redux was removed from this component. - dispatch({ - type: 'serverReturnedPolicyDetailsData', - payload: { - policyItem, - }, - }); - }) - .catch((err) => { - toasts.addDanger({ - 'data-test-subj': 'protectionUpdatesFailureMessage', - title: i18n.translate( - 'xpack.securitySolution.endpoint.protectionUpdates.updateErrorTitle', - { - defaultMessage: 'Failed!', - } - ), - text: err.message, - }); + const onSave = useCallback(() => { + const update = cloneDeep(policy); + update.inputs[0].config.policy.value.global_manifest_version = manifestVersion; + sendPolicyUpdate({ policy: update }) + .then(({ item: policyItem }) => { + toasts.addSuccess({ + 'data-test-subj': 'protectionUpdatesSuccessfulMessage', + title: i18n.translate( + 'xpack.securitySolution.endpoint.protectionUpdates.updateSuccessTitle', + { + defaultMessage: 'Success!', + } + ), + text: i18n.translate( + 'xpack.securitySolution.endpoint.protectionUpdates.updateSuccessMessage', + { + defaultMessage: 'Manifest updates successfully saved', + } + ), }); - if ((!fetchedNote && note !== '') || (fetchedNote && note !== fetchedNote.note)) { - createNote( - { note }, - { - onError: (error) => { - toasts.addDanger({ - 'data-test-subj': 'protectionUpdatesNoteUpdateFailureMessage', - title: i18n.translate( - 'xpack.securitySolution.endpoint.protectionUpdates.noteUpdateErrorTitle', - { - defaultMessage: 'Note update failed!', - } - ), - text: error.body.message, - }); - }, - } - ); - } - }, - [policy, sendPolicyUpdate, fetchedNote, note, toasts, dispatch, createNote] - ); + + // Since the 'policyItem' is stored in a store and fetched as a result of an action on urlChange, we still need to dispatch an action even though Redux was removed from this component. + dispatch({ + type: 'serverReturnedPolicyDetailsData', + payload: { + policyItem, + }, + }); + }) + .catch((err) => { + toasts.addDanger({ + 'data-test-subj': 'protectionUpdatesFailureMessage', + title: i18n.translate( + 'xpack.securitySolution.endpoint.protectionUpdates.updateErrorTitle', + { + defaultMessage: 'Failed!', + } + ), + text: err.message, + }); + }); + if ((!fetchedNote && note !== '') || (fetchedNote && note !== fetchedNote.note)) { + createNote( + { note }, + { + onError: (error) => { + toasts.addDanger({ + 'data-test-subj': 'protectionUpdatesNoteUpdateFailureMessage', + title: i18n.translate( + 'xpack.securitySolution.endpoint.protectionUpdates.noteUpdateErrorTitle', + { + defaultMessage: 'Note update failed!', + } + ), + text: error.body.message, + }); + }, + } + ); + } + }, [ + policy, + manifestVersion, + sendPolicyUpdate, + fetchedNote, + note, + toasts, + dispatch, + createNote, + ]); const toggleAutomaticUpdates = useCallback( (event: EuiSwitchEvent) => { @@ -170,15 +180,11 @@ export const ProtectionUpdatesLayout = React.memo( if (selectedDate !== today) { setSelectedDate(today); } - // We need to save the policy without the user clicking save button - if (deployedVersion !== 'latest') { - onSave('latest'); - } } else { setManifestVersion(selectedDate.format(internalDateFormat)); } }, - [automaticUpdatesEnabled, deployedVersion, onSave, selectedDate, today] + [automaticUpdatesEnabled, selectedDate, today] ); const renderVersionToDeployPicker = () => { @@ -333,7 +339,7 @@ export const ProtectionUpdatesLayout = React.memo( value={note} disabled={getNoteInProgress || createNoteInProgress} onChange={(e) => setNote(e.target.value)} - fullWidth={true} + fullWidth rows={3} placeholder={i18n.translate( 'xpack.securitySolution.endpoint.protectionUpdates.note.placeholder', @@ -346,75 +352,70 @@ export const ProtectionUpdatesLayout = React.memo( ) : ( {note} )} - - - onSave(manifestVersion)} - isLoading={isUpdating} - > - - ); }; return ( - - + - - -
- {i18n.translate('xpack.securitySolution.endpoint.protectionUpdates.manifestName', { - defaultMessage: 'Manifest name', - })} -
-
- - {'artifactsec'} - -
- - {canWritePolicyManagement ? ( - - ) : ( - - {viewModeSwitchLabel} + + + +
+ {i18n.translate( + 'xpack.securitySolution.endpoint.protectionUpdates.manifestName', + { + defaultMessage: 'Manifest name', + } + )} +
+
+ + {'artifactsec'} - )} -
-
+
+ + {canWritePolicyManagement ? ( + + ) : ( + + {viewModeSwitchLabel} + + )} + + - - -
- {renderManifestOutdatedCallOut()} - {renderContent()} -
- + + +
+ {renderManifestOutdatedCallOut()} + {renderContent()} +
+ + + ); } ); From ed72e89729bfc55482288168e08ecf73865995c4 Mon Sep 17 00:00:00 2001 From: Konrad Szwarc Date: Wed, 27 Sep 2023 11:53:04 +0200 Subject: [PATCH 09/12] [Osquery][Defend Workflows] Results table proper resize on fullscreen exit (#166634) https://github.com/elastic/kibana/issues/166046 Explicitly set table width on fullscreen exit https://github.com/elastic/kibana/assets/29123534/8a1f1d9c-8e5d-4dfc-b567-2d2d74115c90 --- .../osquery/public/results/results_table.tsx | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/osquery/public/results/results_table.tsx b/x-pack/plugins/osquery/public/results/results_table.tsx index 9f08cab70b064..c74ca9d607dc1 100644 --- a/x-pack/plugins/osquery/public/results/results_table.tsx +++ b/x-pack/plugins/osquery/public/results/results_table.tsx @@ -59,6 +59,11 @@ const euiProgressCss = { marginTop: '-2px', }; +const resultsTableContainerCss = { + width: '100%', + maxWidth: '1200px', +}; + export interface ResultsTableComponentProps { actionId: string; selectedAgent?: string; @@ -435,19 +440,21 @@ const ResultsTableComponent: React.FC = ({ ) : ( - +
+ +
)} From 2477e40c850f9de5eb0e3031361f4018d790d002 Mon Sep 17 00:00:00 2001 From: Konrad Szwarc Date: Wed, 27 Sep 2023 11:53:54 +0200 Subject: [PATCH 10/12] [Fleet][Kafka] Topics without quotes (#166236) Closes https://github.com/elastic/kibana/issues/166135 Depends on https://github.com/elastic/ingest-docs/pull/474 This PR adds: 1. Subtitle under Topic Processors linking to docs 2. Removes whitespace stripping from `value` in `key:value` topic pair. ![Screenshot 2023-09-25 at 18 21 27](https://github.com/elastic/kibana/assets/29123534/45d65502-1ebe-4b4d-bda7-6032e6f6dfd1) --- .../output_form_kafka_topics.tsx | 33 +++++++++++++++---- .../agent_policies/full_agent_policy.ts | 2 +- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_topics.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_topics.tsx index 9360a5c35bcd5..58cff89ac8c8f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_topics.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_topics.tsx @@ -18,9 +18,11 @@ import { EuiFormErrorText, EuiFormRow, EuiIcon, + EuiLink, EuiPanel, EuiSelect, EuiSpacer, + EuiText, EuiTitle, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -32,6 +34,8 @@ import styled, { useTheme } from 'styled-components'; import type { EuiTheme } from '@kbn/kibana-react-plugin/common'; +import { useStartServices } from '../../../../../../hooks'; + import type { KafkaTopicWhenType, ValueOf } from '../../../../../../../common/types'; import { kafkaTopicWhenType } from '../../../../../../../common/constants'; @@ -49,6 +53,7 @@ export const OutputFormKafkaTopics: React.FunctionComponent<{ inputs: OutputForm } = inputs.kafkaTopicsInput; const theme = useTheme() as EuiTheme; const [autoFocus, setAutoFocus] = useState(false); + const { docLinks } = useStartServices(); const indexedErrors = useMemo(() => { if (!errors) { @@ -214,10 +219,27 @@ export const OutputFormKafkaTopics: React.FunctionComponent<{ inputs: OutputForm + <> + + + + + documentation + + ), + }} + /> + + + } > {topics.length > 1 ? ( @@ -232,7 +254,6 @@ export const OutputFormKafkaTopics: React.FunctionComponent<{ inputs: OutputForm spacing="m" index={index} draggableId={`${id}${index}Draggable`} - // isDragDisabled={disabled} customDragHandle={true} style={{ paddingLeft: 0, @@ -348,7 +369,7 @@ export const OutputFormKafkaTopics: React.FunctionComponent<{ inputs: OutputForm const topicConditionErrors = indexedConditionErrors[index]; return ( <> - + diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts index 9107a334dad83..2cdd04483e108 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts @@ -363,7 +363,7 @@ export function transformOutputToFullPolicyOutput( topic: topicName, when: { [rest.when.type as string]: { - [keyName.replace(/\s/g, '')]: value.replace(/\s/g, ''), + [keyName.replace(/\s/g, '')]: value, }, }, }; From 32562aa53ff07ab151d77b9b7559c54a80abd633 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 27 Sep 2023 12:58:06 +0300 Subject: [PATCH 11/12] [ES|QL] Navigate to create new policy page from the editor (#167012) ## Summary When a user is writing am ES|QL query with the enrich command and doesn't have a policy then we want to navigate them from the editor to the index management page to create one. In the future the same action can open a flyout in order for the users to not change context but we don't have it atm. ![enrich](https://github.com/elastic/kibana/assets/17003240/178362e1-6778-4b40-98a7-2fca9bc35118) --------- Co-authored-by: Marco Liberati --- .../autocomplete_definitions/dynamic_commands.ts | 8 +++++++- .../kbn-monaco/src/esql/lib/autocomplete/types.ts | 2 +- .../src/text_based_languages_editor.tsx | 12 +++++++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/dynamic_commands.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/dynamic_commands.ts index 4a5a147ffcbde..f6348fe2c11e2 100644 --- a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/dynamic_commands.ts +++ b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/dynamic_commands.ts @@ -45,9 +45,15 @@ export const buildNoPoliciesAvailableDefinition = (): AutocompleteCommandDefinit insertText: '', kind: 26, detail: i18n.translate('monaco.esql.autocomplete.noPoliciesLabelsFound', { - defaultMessage: 'No policies found', + defaultMessage: 'Click to create', }), sortText: 'D', + command: { + id: 'esql.policies.create', + title: i18n.translate('monaco.esql.autocomplete.createNewPolicy', { + defaultMessage: 'Click to create', + }), + }, }, ]; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/types.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/types.ts index 0b64f0871b27a..fc22bae7bbdb9 100644 --- a/packages/kbn-monaco/src/esql/lib/autocomplete/types.ts +++ b/packages/kbn-monaco/src/esql/lib/autocomplete/types.ts @@ -32,5 +32,5 @@ export interface UserDefinedVariables { /** @internal **/ export type AutocompleteCommandDefinition = Pick< monaco.languages.CompletionItem, - 'label' | 'insertText' | 'kind' | 'detail' | 'documentation' | 'sortText' + 'label' | 'insertText' | 'kind' | 'detail' | 'documentation' | 'sortText' | 'command' >; diff --git a/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx b/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx index b1f1708262743..b84e459fd222d 100644 --- a/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx +++ b/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx @@ -131,7 +131,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ const language = getAggregateQueryMode(query); const queryString: string = query[language] ?? ''; const kibana = useKibana(); - const { dataViews, expressions, indexManagementApiService } = kibana.services; + const { dataViews, expressions, indexManagementApiService, application } = kibana.services; const [code, setCode] = useState(queryString ?? ''); const [codeOneLiner, setCodeOneLiner] = useState(''); const [editorHeight, setEditorHeight] = useState( @@ -148,6 +148,16 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ useState(); const policiesRef = useRef([]); + + // Registers a command to redirect users to the index management page + // to create a new policy. The command is called by the buildNoPoliciesAvailableDefinition + monaco.editor.registerCommand('esql.policies.create', (...args) => { + application?.navigateToApp('management', { + path: 'data/index_management/enrich_policies/create', + openInNewTab: true, + }); + }); + const styles = textBasedLanguagedEditorStyles( euiTheme, isCompactFocused, From 95f428f1e0dd94d847485e06fc2f7eab5f194783 Mon Sep 17 00:00:00 2001 From: Rudolf Meijering Date: Wed, 27 Sep 2023 12:48:33 +0200 Subject: [PATCH 12/12] Fix failing es promotion #160295 (#167359) ## Summary Fixes #160295 ### Checklist Delete any items that are not applicable to this PR. - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### Risk Matrix Delete this section if it is not applicable to this PR. Before closing this PR, invite QA, stakeholders, and other developers to identify risks that should be tested prior to the change/feature release. When forming the risk matrix, consider some of the following examples and how they may potentially impact the change: | Risk | Probability | Severity | Mitigation/Notes | |---------------------------|-------------|----------|-------------------------| | Multiple Spaces—unexpected behavior in non-default Kibana Space. | Low | High | Integration tests will verify that all features are still supported in non-default Kibana Space and when user switches between spaces. | | Multiple nodes—Elasticsearch polling might have race conditions when multiple Kibana nodes are polling for the same tasks. | High | Low | Tasks are idempotent, so executing them multiple times will not result in logical error, but will degrade performance. To test for this case we add plenty of unit tests around this logic and document manual testing procedure. | | Code should gracefully handle cases when feature X or plugin Y are disabled. | Medium | High | Unit tests will verify that any feature flag or plugin combination still results in our service operational. | | [See more potential risk examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) | ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --- .../migrations/group2/batch_size_bytes.test.ts | 7 +++---- .../migrations/group3/actions/actions_test_suite.ts | 13 +++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes.test.ts index ba1107dff6f8e..bea50cf32ce35 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes.test.ts @@ -55,8 +55,7 @@ async function fetchDocuments(esClient: ElasticsearchClient, index: string) { const assertMigratedDocuments = (arr: any[], target: any[]) => target.every((v) => arr.includes(v)); -// FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/160295 -describe.skip('migration v2', () => { +describe('migration v2', () => { let esServer: TestElasticsearchUtils; let root: Root; let startES: () => Promise; @@ -119,7 +118,7 @@ describe.skip('migration v2', () => { await root.preboot(); await root.setup(); await expect(root.start()).rejects.toMatchInlineSnapshot( - `[Error: Unable to complete saved object migrations for the [.kibana] index: The document with _id "canvas-workpad-template:workpad-template-061d7868-2b4e-4dc8-8bf7-3772b52926e5" is 1715318 bytes which exceeds the configured maximum batch size of 1015275 bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value.]` + `[Error: Unable to complete saved object migrations for the [.kibana] index: The document with _id "canvas-workpad-template:workpad-template-061d7868-2b4e-4dc8-8bf7-3772b52926e5" is 1715319 bytes which exceeds the configured maximum batch size of 1015275 bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value.]` ); await retryAsync( @@ -132,7 +131,7 @@ describe.skip('migration v2', () => { expect( records.find((rec) => rec.message.startsWith( - `Unable to complete saved object migrations for the [.kibana] index: The document with _id "canvas-workpad-template:workpad-template-061d7868-2b4e-4dc8-8bf7-3772b52926e5" is 1715318 bytes which exceeds the configured maximum batch size of 1015275 bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value.` + `Reason: Unable to complete saved object migrations for the [.kibana] index: The document with _id "canvas-workpad-template:workpad-template-061d7868-2b4e-4dc8-8bf7-3772b52926e5" is 1715319 bytes which exceeds the configured maximum batch size of 1015275 bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value.` ) ) ).toBeDefined(); diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions_test_suite.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions_test_suite.ts index 9df98deea3b89..bb509799c052d 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions_test_suite.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions_test_suite.ts @@ -756,8 +756,7 @@ export const runActionTestSuite = ({ // Reindex doesn't return any errors on it's own, so we have to test // together with waitForReindexTask - // Failing: See https://github.com/elastic/kibana/issues/166190 - describe.skip('reindex & waitForReindexTask', () => { + describe('reindex & waitForReindexTask', () => { it('resolves right when reindex succeeds without reindex script', async () => { const res = (await reindex({ client, @@ -1120,15 +1119,17 @@ export const runActionTestSuite = ({ `); }); it('resolves left wait_for_task_completion_timeout when the task does not finish within the timeout', async () => { - await waitForIndexStatus({ + const readyTaskRes = await waitForIndexStatus({ client, - index: '.kibana_1', + index: 'existing_index_with_docs', status: 'yellow', })(); + expect(Either.isRight(readyTaskRes)).toBe(true); + const res = (await reindex({ client, - sourceIndex: '.kibana_1', + sourceIndex: 'existing_index_with_docs', targetIndex: 'reindex_target', reindexScript: Option.none, requireAlias: false, @@ -1467,7 +1468,7 @@ export const runActionTestSuite = ({ it('resolves left wait_for_task_completion_timeout when the task does not complete within the timeout', async () => { const res = (await pickupUpdatedMappings( client, - '.kibana_1', + 'existing_index_with_docs', 1000 )()) as Either.Right;