From 9907601dd148ba59420bffda45ff584686f47b43 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Wed, 9 Oct 2024 10:14:06 +0200 Subject: [PATCH 1/8] [LogsUI] Add UI setting to hide Logs Stream and dashboard panel option (#194519) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📓 Summary Closes #193319 Closes #193320 This work is part of the effort to progressively deprecate the existing Logs Stream feature. Changes taken on this PR consist of: - Create a new uiSettings `observability:enableLogsStream` which defaults to `false` on the stateful/cloud deployments and is not available in serverless ones (still, defaults to `false` behind the scene). - When `observability:enableLogsStream` is `false`, the Logs Stream page route is not registered, and neither is its deep link for global search. - When `observability:enableLogsStream` is `false`, the panels list on Dashboard won't show anymore the option `Logs Stream (Deprecated)` to prevent usage of this embeddable in new dashboards. The embeddable is still registered for retro-compatibility with active dashboards, and it has now a callout explaining the status of this embeddable (unmaintained/deprecated). - Rename logs ML to "Logs Anomalies" and "Logs Categories". Other minor improvements regard: - Remove duplicate Xstate utils and use the relative package instead. - Remove the duplicated `useBoolean` hook used in the deprecation callout. - Sync deep links registration with available routes through a single `getLogsRoutes` util. ## 🎥 Recordings ### Logs Stream app removed https://github.com/user-attachments/assets/f4173294-8789-4abd-9972-29c9b7c197ed ### Logs Stream dashboard panel entry removed https://github.com/user-attachments/assets/7f99ca2a-c030-4867-b976-8fdc1df09d29 ### Logs Stream app removed from project nav https://github.com/user-attachments/assets/de51bdd6-820a-4c03-8b64-fb1a6ced0a12 ### Embeddable deprecation callout Screenshot 2024-10-02 at 10 22 09 ### Unavailable setting in serverless https://github.com/user-attachments/assets/91bf6c37-0845-4918-a485-b6250bbd96bf --------- Co-authored-by: Marco Antonio Ghiani Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Mike Birnstiehl <114418652+mdbirnstiehl@users.noreply.github.com> --- .../settings/setting_ids/index.ts | 1 + .../server/collectors/management/schema.ts | 4 + .../server/collectors/management/types.ts | 1 + src/plugins/telemetry/schema/oss_plugins.json | 6 + .../group1/create_and_add_embeddables.ts | 12 +- test/tsconfig.json | 1 + .../infra/common/ui_settings.ts | 30 +++ .../log_stream_react_embeddable.tsx | 55 +++++- .../components/logs_deprecation_callout.tsx | 37 +++- .../infra/public/hooks/use_boolean.ts | 36 ---- .../log_stream_page/state/src/selectors.ts | 2 +- .../state/src/state_machine.ts | 2 +- .../src/state_machine.ts | 2 +- .../src/state_machine.ts | 2 +- .../xstate_helpers/src/index.ts | 3 - .../src/notification_channel.ts | 41 ---- .../xstate_helpers/src/send_actions.test.ts | 67 ------- .../xstate_helpers/src/send_actions.ts | 36 ---- .../xstate_helpers/src/types.ts | 43 ---- .../public/pages/logs/log_entry_rate/page.tsx | 4 +- .../logs/log_entry_rate/page_content.tsx | 8 +- .../infra/public/pages/logs/page_content.tsx | 103 +++++----- .../infra/public/pages/logs/routes.ts | 56 ++++++ .../source_configuration_settings.tsx | 2 + .../pages/logs/stream/page_logs_content.tsx | 4 +- .../pages/logs/stream/page_providers.tsx | 2 +- .../waffle/waffle_group_by_controls.tsx | 2 +- .../infra/public/plugin.ts | 183 ++++++++++-------- .../infra/public/translations.ts | 6 +- .../lib/adapters/framework/adapter_types.ts | 2 + .../infra/server/plugin.ts | 4 + .../infra/tsconfig.json | 3 + .../observability/public/navigation_tree.ts | 6 + .../observability/tsconfig.json | 2 +- .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../functional/apps/infra/logs/log_stream.ts | 4 + .../apps/infra/logs/log_stream_date_nano.ts | 4 + .../infra/logs/logs_source_configuration.ts | 3 + .../functional/apps/infra/page_not_found.ts | 11 +- 41 files changed, 387 insertions(+), 406 deletions(-) create mode 100644 x-pack/plugins/observability_solution/infra/common/ui_settings.ts delete mode 100644 x-pack/plugins/observability_solution/infra/public/hooks/use_boolean.ts delete mode 100644 x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/notification_channel.ts delete mode 100644 x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/send_actions.test.ts delete mode 100644 x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/send_actions.ts delete mode 100644 x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/types.ts create mode 100644 x-pack/plugins/observability_solution/infra/public/pages/logs/routes.ts diff --git a/packages/kbn-management/settings/setting_ids/index.ts b/packages/kbn-management/settings/setting_ids/index.ts index cb32dbd4a4505..a7051804289bd 100644 --- a/packages/kbn-management/settings/setting_ids/index.ts +++ b/packages/kbn-management/settings/setting_ids/index.ts @@ -144,6 +144,7 @@ export const OBSERVABILITY_LOGS_EXPLORER_ALLOWED_DATA_VIEWS_ID = 'observability:logsExplorer:allowedDataViews'; export const OBSERVABILITY_ENTITY_CENTRIC_EXPERIENCE = 'observability:entityCentricExperience'; export const OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID = 'observability:logSources'; +export const OBSERVABILITY_ENABLE_LOGS_STREAM = 'observability:enableLogsStream'; export const OBSERVABILITY_AI_ASSISTANT_SIMULATED_FUNCTION_CALLING = 'observability:aiAssistantSimulatedFunctionCalling'; export const OBSERVABILITY_AI_ASSISTANT_SEARCH_CONNECTOR_INDEX_PATTERN = diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index 0ece5f004c23e..dc2d2ad2c5de2 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -510,6 +510,10 @@ export const stackManagementSchema: MakeSchemaFrom = { _meta: { description: 'Non-default value of setting.' }, }, }, + 'observability:enableLogsStream': { + type: 'boolean', + _meta: { description: 'Non-default value of setting.' }, + }, 'banners:placement': { type: 'keyword', _meta: { description: 'Non-default value of setting.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index ca1df3c95e87a..ef20ab223dfb6 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -55,6 +55,7 @@ export interface UsageStats { 'observability:apmEnableServiceInventoryTableSearchBar': boolean; 'observability:logsExplorer:allowedDataViews': string[]; 'observability:logSources': string[]; + 'observability:enableLogsStream': boolean; 'observability:aiAssistantSimulatedFunctionCalling': boolean; 'observability:aiAssistantSearchConnectorIndexPattern': string; 'visualization:heatmap:maxBuckets': number; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index f25c29e5f6952..958280d9eba00 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -10486,6 +10486,12 @@ } } }, + "observability:enableLogsStream": { + "type": "boolean", + "_meta": { + "description": "Non-default value of setting." + } + }, "banners:placement": { "type": "keyword", "_meta": { diff --git a/test/functional/apps/dashboard/group1/create_and_add_embeddables.ts b/test/functional/apps/dashboard/group1/create_and_add_embeddables.ts index 61929b19ff468..34e78f3e68632 100644 --- a/test/functional/apps/dashboard/group1/create_and_add_embeddables.ts +++ b/test/functional/apps/dashboard/group1/create_and_add_embeddables.ts @@ -8,7 +8,7 @@ */ import expect from '@kbn/expect'; - +import { OBSERVABILITY_ENABLE_LOGS_STREAM } from '@kbn/management-settings-ids'; import { VisualizeConstants } from '@kbn/visualizations-plugin/common/constants'; import { FtrProviderContext } from '../../../ftr_provider_context'; @@ -28,6 +28,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: true }); + }); + + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: false }); }); it('ensure toolbar popover closes on add', async () => { @@ -39,10 +45,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dashboardAddPanel.expectEditorMenuClosed(); }); - after(async () => { - await kibanaServer.savedObjects.cleanStandardList(); - }); - describe('add new visualization link', () => { before(async () => { await dashboard.navigateToApp(); diff --git a/test/tsconfig.json b/test/tsconfig.json index 8b0d946bded62..1d8c301c44a2b 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -76,5 +76,6 @@ "@kbn/default-nav-management", "@kbn/default-nav-devtools", "@kbn/core-saved-objects-import-export-server-internal", + "@kbn/management-settings-ids", ] } diff --git a/x-pack/plugins/observability_solution/infra/common/ui_settings.ts b/x-pack/plugins/observability_solution/infra/common/ui_settings.ts new file mode 100644 index 0000000000000..95f1ee0a44bae --- /dev/null +++ b/x-pack/plugins/observability_solution/infra/common/ui_settings.ts @@ -0,0 +1,30 @@ +/* + * 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. + */ + +/** + * uiSettings definitions for the logs_data_access plugin. + */ +import { schema } from '@kbn/config-schema'; +import { UiSettingsParams } from '@kbn/core-ui-settings-common'; +import { i18n } from '@kbn/i18n'; +import { OBSERVABILITY_ENABLE_LOGS_STREAM } from '@kbn/management-settings-ids'; + +export const uiSettings: Record = { + [OBSERVABILITY_ENABLE_LOGS_STREAM]: { + category: ['observability'], + name: i18n.translate('xpack.infra.enableLogsStream', { + defaultMessage: 'Logs Stream', + }), + value: false, + description: i18n.translate('xpack.infra.enableLogsStreamDescription', { + defaultMessage: 'Enables the legacy Logs Stream application and dashboard panel. ', + }), + type: 'boolean', + schema: schema.boolean(), + requiresPageReload: true, + }, +}; diff --git a/x-pack/plugins/observability_solution/infra/public/components/log_stream/log_stream_react_embeddable.tsx b/x-pack/plugins/observability_solution/infra/public/components/log_stream/log_stream_react_embeddable.tsx index bbb2e09d8660a..1193b81379219 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/log_stream/log_stream_react_embeddable.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/log_stream/log_stream_react_embeddable.tsx @@ -6,6 +6,8 @@ */ import React, { FC, PropsWithChildren, useEffect, useMemo, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiCallOut, EuiLink } from '@elastic/eui'; import { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; import { initializeTimeRange, @@ -17,6 +19,9 @@ import { AppMountParameters, CoreStart } from '@kbn/core/public'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { Query } from '@kbn/es-query'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; +import { euiThemeVars } from '@kbn/ui-theme'; +import useLocalStorage from 'react-use/lib/useLocalStorage'; +import { FormattedMessage } from '@kbn/i18n-react'; import type { LogStreamApi, LogStreamSerializedState, Services } from './types'; import { datemathToEpochMillis } from '../../utils/datemath'; import { LOG_STREAM_EMBEDDABLE } from './constants'; @@ -81,7 +86,7 @@ export function getLogStreamEmbeddableFactory(services: Services) { theme$={services.coreStart.theme.theme$} > -
+
+
@@ -101,6 +107,53 @@ export function getLogStreamEmbeddableFactory(services: Services) { return factory; } +const DISMISSAL_STORAGE_KEY = 'observability:logStreamEmbeddableDeprecationCalloutDismissed'; +const SAVED_SEARCH_DOCS_URL = + 'https://www.elastic.co/guide/en/kibana/current/save-open-search.html'; + +const DeprecationCallout = () => { + const [isDismissed, setDismissed] = useLocalStorage(DISMISSAL_STORAGE_KEY, false); + + if (isDismissed) { + return null; + } + + return ( + setDismissed(true)} + css={{ + position: 'absolute', + bottom: euiThemeVars.euiSizeM, + right: euiThemeVars.euiSizeM, + width: 'min(100%, 40ch)', + }} + > +

+ + {i18n.translate( + 'xpack.infra.logsStreamEmbeddable.deprecationWarningDescription.savedSearchesLinkLabel', + { defaultMessage: 'saved searches' } + )} + + ), + }} + /> +

+
+ ); +}; + export interface LogStreamEmbeddableProvidersProps { core: CoreStart; pluginStart: InfraClientStartExports; diff --git a/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx b/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx index 71ae9698ea3b9..63107f4a4d031 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx @@ -18,14 +18,38 @@ import { css } from '@emotion/css'; import { SharePublicStart } from '@kbn/share-plugin/public/plugin'; import { useKibanaContextForPlugin } from '../hooks/use_kibana'; -const DISMISSAL_STORAGE_KEY = 'log_stream_deprecation_callout_dismissed'; +const pageConfigurations = { + stream: { + dismissalStorageKey: 'log_stream_deprecation_callout_dismissed', + message: i18n.translate('xpack.infra.logsDeprecationCallout.p.theNewLogsExplorerLabel', { + defaultMessage: + 'The new Logs Explorer makes viewing and inspecting your logs easier with more features, better performance, and more intuitive navigation. We recommend switching to Logs Explorer, as it will replace Logs Stream in a future version.', + }), + }, + settings: { + dismissalStorageKey: 'log_settings_deprecation_callout_dismissed', + message: i18n.translate( + 'xpack.infra.logsSettingsDeprecationCallout.p.theNewLogsExplorerLabel', + { + defaultMessage: + 'These settings only apply to the legacy Logs Stream app, and we do not recommend configuring them. Instead, use Logs Explorer which makes viewing and inspecting your logs easier with more features, better performance, and more intuitive navigation.', + } + ), + }, +}; + +interface LogsDeprecationCalloutProps { + page: keyof typeof pageConfigurations; +} -export const LogsDeprecationCallout = () => { +export const LogsDeprecationCallout = ({ page }: LogsDeprecationCalloutProps) => { const { services: { share }, } = useKibanaContextForPlugin(); - const [isDismissed, setDismissed] = useLocalStorage(DISMISSAL_STORAGE_KEY, false); + const { dismissalStorageKey, message } = pageConfigurations[page]; + + const [isDismissed, setDismissed] = useLocalStorage(dismissalStorageKey, false); if (isDismissed) { return null; @@ -42,12 +66,7 @@ export const LogsDeprecationCallout = () => { onDismiss={() => setDismissed(true)} className={calloutStyle} > -

- {i18n.translate('xpack.infra.logsDeprecationCallout.p.theNewLogsExplorerLabel', { - defaultMessage: - 'The new Logs Explorer makes viewing and inspecting your logs easier with more features, better performance, and more intuitive navigation. We recommend switching to Logs Explorer, as it will replace Logs Stream in a future version.', - })} -

+

{message}

void; - -export type DispatchWithOptionalAction = (_arg?: Type | unknown) => void; - -export interface UseBooleanHandlers { - on: VoidHandler; - off: VoidHandler; - toggle: DispatchWithOptionalAction; -} - -export type UseBooleanResult = [boolean, UseBooleanHandlers]; - -export const useBoolean = (initialValue: boolean = false): UseBooleanResult => { - const [value, toggle] = useToggle(initialValue); - - const handlers = useMemo( - () => ({ - toggle, - on: () => toggle(true), - off: () => toggle(false), - }), - [toggle] - ); - - return [value, handlers]; -}; diff --git a/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_page/state/src/selectors.ts b/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_page/state/src/selectors.ts index c6bfafd020ab2..6f00ce32097e9 100644 --- a/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_page/state/src/selectors.ts +++ b/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_page/state/src/selectors.ts @@ -5,8 +5,8 @@ * 2.0. */ +import { MatchedStateFromActor } from '@kbn/xstate-utils'; import { LogStreamQueryActorRef } from '../../../log_stream_query_state'; -import { MatchedStateFromActor } from '../../../xstate_helpers'; import { LogStreamPageActorRef } from './state_machine'; type LogStreamPageStateWithLogViewIndices = diff --git a/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_page/state/src/state_machine.ts b/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_page/state/src/state_machine.ts index d2a71c65702eb..e2755b29d21e3 100644 --- a/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_page/state/src/state_machine.ts +++ b/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_page/state/src/state_machine.ts @@ -10,6 +10,7 @@ import { TimeRange } from '@kbn/es-query'; import { actions, ActorRefFrom, createMachine, EmittedFrom } from 'xstate'; import { DEFAULT_REFRESH_INTERVAL } from '@kbn/logs-shared-plugin/common'; import type { LogViewNotificationChannel } from '@kbn/logs-shared-plugin/public'; +import { OmitDeprecatedState } from '@kbn/xstate-utils'; import { datemathToEpochMillis } from '../../../../utils/datemath'; import { createLogStreamPositionStateMachine } from '../../../log_stream_position_state/src/state_machine'; import { @@ -17,7 +18,6 @@ import { DEFAULT_TIMERANGE, LogStreamQueryStateMachineDependencies, } from '../../../log_stream_query_state'; -import { OmitDeprecatedState } from '../../../xstate_helpers'; import { waitForInitialQueryParameters, waitForInitialPositionParameters, diff --git a/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_position_state/src/state_machine.ts b/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_position_state/src/state_machine.ts index 868f29a5c07e9..0cc26d3e6ed35 100644 --- a/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_position_state/src/state_machine.ts +++ b/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_position_state/src/state_machine.ts @@ -10,8 +10,8 @@ import { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; import { convertISODateToNanoPrecision } from '@kbn/logs-shared-plugin/common'; import moment from 'moment'; import { actions, ActorRefFrom, createMachine, EmittedFrom, SpecialTargets } from 'xstate'; +import { OmitDeprecatedState, sendIfDefined } from '@kbn/xstate-utils'; import { isSameTimeKey } from '../../../../common/time'; -import { OmitDeprecatedState, sendIfDefined } from '../../xstate_helpers'; import { DESIRED_BUFFER_PAGES, RELATIVE_END_UPDATE_DELAY } from './defaults'; import { LogStreamPositionNotificationEventSelectors } from './notifications'; import type { diff --git a/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_query_state/src/state_machine.ts b/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_query_state/src/state_machine.ts index 1c0de464121c8..5570faf16f3f8 100644 --- a/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_query_state/src/state_machine.ts +++ b/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_query_state/src/state_machine.ts @@ -15,7 +15,7 @@ import { EsQueryConfig } from '@kbn/es-query'; import { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; import { actions, ActorRefFrom, createMachine, SpecialTargets, send } from 'xstate'; import { DEFAULT_REFRESH_INTERVAL } from '@kbn/logs-shared-plugin/common'; -import { OmitDeprecatedState, sendIfDefined } from '../../xstate_helpers'; +import { OmitDeprecatedState, sendIfDefined } from '@kbn/xstate-utils'; import { logStreamQueryNotificationEventSelectors } from './notifications'; import { subscribeToFilterSearchBarChanges, diff --git a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/index.ts b/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/index.ts index 8e6f993a9755e..67b23e66b78e8 100644 --- a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/index.ts +++ b/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/index.ts @@ -6,7 +6,4 @@ */ export * from './invalid_state_callout'; -export * from './notification_channel'; -export * from './send_actions'; -export * from './types'; export * from './state_machine_playground'; diff --git a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/notification_channel.ts b/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/notification_channel.ts deleted file mode 100644 index 0108ab0225176..0000000000000 --- a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/notification_channel.ts +++ /dev/null @@ -1,41 +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 { ReplaySubject } from 'rxjs'; -import { ActionFunction, EventObject, Expr, Subscribable } from 'xstate'; - -export interface NotificationChannel { - createService: () => Subscribable; - notify: ( - eventExpr: Expr - ) => ActionFunction; -} - -export const createNotificationChannel = < - TContext, - TEvent extends EventObject, - TSentEvent ->(): NotificationChannel => { - const eventsSubject = new ReplaySubject(1); - - const createService = () => eventsSubject.asObservable(); - - const notify = - (eventExpr: Expr) => - (context: TContext, event: TEvent) => { - const eventToSend = eventExpr(context, event); - - if (eventToSend != null) { - eventsSubject.next(eventToSend); - } - }; - - return { - createService, - notify, - }; -}; diff --git a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/send_actions.test.ts b/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/send_actions.test.ts deleted file mode 100644 index cf446fec63b3f..0000000000000 --- a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/send_actions.test.ts +++ /dev/null @@ -1,67 +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 { actions, ActorRef, EventObject } from 'xstate'; -import { sendIfDefined } from './send_actions'; - -describe('function sendIfDefined', () => { - it('sends the events to the specified target', () => { - const actor = createMockActor(); - const createEvent = (context: {}) => ({ - type: 'testEvent', - }); - - const action = sendIfDefined(actor)(createEvent).get({}, { type: 'triggeringEvent' }); - - expect(action).toEqual([ - actions.send('testEvent', { - to: actor, - }), - ]); - }); - - it('sends the events created by the event expression', () => { - const actor = createMockActor(); - const createEvent = (context: {}) => ({ - type: 'testEvent', - payload: 'payload', - }); - - const action = sendIfDefined(actor)(createEvent).get({}, { type: 'triggeringEvent' }); - - expect(action).toEqual([ - actions.send( - { - type: 'testEvent', - payload: 'payload', - }, - { - to: actor, - } - ), - ]); - }); - - it("doesn't send anything when the event expression returns undefined", () => { - const actor = createMockActor(); - const createEvent = (context: {}) => undefined; - - const action = sendIfDefined(actor)(createEvent).get({}, { type: 'triggeringEvent' }); - - expect(action).toEqual(undefined); - }); -}); - -const createMockActor = (): ActorRef => ({ - getSnapshot: jest.fn(), - id: 'mockActor', - send: jest.fn(), - subscribe: jest.fn(), - [Symbol.observable]() { - return this; - }, -}); diff --git a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/send_actions.ts b/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/send_actions.ts deleted file mode 100644 index 375fd831b030f..0000000000000 --- a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/send_actions.ts +++ /dev/null @@ -1,36 +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 { - actions, - ActorRef, - AnyEventObject, - EventObject, - Expr, - PureAction, - SendActionOptions, -} from 'xstate'; - -export const sendIfDefined = - (target: string | ActorRef) => - ( - eventExpr: Expr, - options?: SendActionOptions - ): PureAction => { - return actions.pure((context, event) => { - const targetEvent = eventExpr(context, event); - - return targetEvent != null - ? [ - actions.send(targetEvent, { - ...options, - to: target, - }), - ] - : undefined; - }); - }; diff --git a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/types.ts b/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/types.ts deleted file mode 100644 index 05e75c5fe6e45..0000000000000 --- a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/types.ts +++ /dev/null @@ -1,43 +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 type { ActorRef, ActorRefWithDeprecatedState, EmittedFrom, State, StateValue } from 'xstate'; - -export type OmitDeprecatedState> = Omit< - T, - 'state' ->; - -export type MatchedState< - TState extends State, - TStateValue extends StateValue -> = TState extends State< - any, - infer TEvent, - infer TStateSchema, - infer TTypestate, - infer TResolvedTypesMeta -> - ? State< - (TTypestate extends any - ? { value: TStateValue; context: any } extends TTypestate - ? TTypestate - : never - : never)['context'], - TEvent, - TStateSchema, - TTypestate, - TResolvedTypesMeta - > & { - value: TStateValue; - } - : never; - -export type MatchedStateFromActor< - TActorRef extends ActorRef, - TStateValue extends StateValue -> = MatchedState, TStateValue>; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/page.tsx b/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/page.tsx index 50a4852c458c4..97841745ae13a 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/page.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/page.tsx @@ -10,13 +10,13 @@ import React from 'react'; import { LogEntryRatePageContent } from './page_content'; import { LogEntryRatePageProviders } from './page_providers'; import { useLogsBreadcrumbs } from '../../../hooks/use_logs_breadcrumbs'; -import { anomaliesTitle } from '../../../translations'; +import { logsAnomaliesTitle } from '../../../translations'; import { LogMlJobIdFormatsShimProvider } from '../shared/use_log_ml_job_id_formats_shim'; export const LogEntryRatePage = () => { useLogsBreadcrumbs([ { - text: anomaliesTitle, + text: logsAnomaliesTitle, }, ]); return ( diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/page_content.tsx b/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/page_content.tsx index e4dc0694c3f75..3ac8d7d9137d1 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/page_content.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/page_content.tsx @@ -36,7 +36,7 @@ import { useLogMlJobIdFormatsShimContext } from '../shared/use_log_ml_job_id_for const JOB_STATUS_POLLING_INTERVAL = 30000; -const anomaliesTitle = i18n.translate('xpack.infra.logs.anomaliesPageTitle', { +const logsAnomaliesTitle = i18n.translate('xpack.infra.logs.anomaliesPageTitle', { defaultMessage: 'Anomalies', }); @@ -101,7 +101,7 @@ export const LogEntryRatePageContent = memo(() => { ); @@ -137,7 +137,7 @@ export const LogEntryRatePageContent = memo(() => { ) { return ( <> - + ); @@ -172,7 +172,7 @@ const AnomaliesPageTemplate: React.FC = ({ rest.isEmptyState ? undefined : { - pageTitle: anomaliesTitle, + pageTitle: logsAnomaliesTitle, } } {...rest} diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/page_content.tsx b/x-pack/plugins/observability_solution/infra/public/pages/logs/page_content.tsx index 5b5965bb2d5ec..ecf5af5572b31 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/logs/page_content.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/page_content.tsx @@ -9,26 +9,36 @@ import { EuiFlexGroup, EuiFlexItem, EuiHeaderLink, EuiHeaderLinks } from '@elast import { i18n } from '@kbn/i18n'; import React, { useContext } from 'react'; import { Routes, Route } from '@kbn/shared-ux-router'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { useKibana, useUiSetting } from '@kbn/kibana-react-plugin/public'; import { HeaderMenuPortal, useLinkProps } from '@kbn/observability-shared-plugin/public'; import { SharePublicStart } from '@kbn/share-plugin/public/plugin'; import { ObservabilityOnboardingLocatorParams, OBSERVABILITY_ONBOARDING_LOCATOR, + AllDatasetsLocatorParams, + ALL_DATASETS_LOCATOR_ID, } from '@kbn/deeplinks-observability'; import { dynamic } from '@kbn/shared-ux-utility'; +import { isDevMode } from '@kbn/xstate-utils'; +import { OBSERVABILITY_ENABLE_LOGS_STREAM } from '@kbn/management-settings-ids'; import { LazyAlertDropdownWrapper } from '../../alerting/log_threshold'; import { HelpCenterContent } from '../../components/help_center_content'; import { useReadOnlyBadge } from '../../hooks/use_readonly_badge'; import { HeaderActionMenuContext } from '../../containers/header_action_menu_provider'; import { RedirectWithQueryParams } from '../../utils/redirect_with_query_params'; -import { LogEntryCategoriesPage } from './log_entry_categories'; -import { LogEntryRatePage } from './log_entry_rate'; -import { LogsSettingsPage } from './settings'; -import { StreamPage } from './stream'; -import { isDevMode } from '../../utils/dev_mode'; import { NotFoundPage } from '../404'; +import { getLogsAppRoutes } from './routes'; +const StreamPage = dynamic(() => import('./stream').then((mod) => ({ default: mod.StreamPage }))); +const LogEntryCategoriesPage = dynamic(() => + import('./log_entry_categories').then((mod) => ({ default: mod.LogEntryCategoriesPage })) +); +const LogEntryRatePage = dynamic(() => + import('./log_entry_rate').then((mod) => ({ default: mod.LogEntryRatePage })) +); +const LogsSettingsPage = dynamic(() => + import('./settings').then((mod) => ({ default: mod.LogsSettingsPage })) +); const StateMachinePlayground = dynamic(() => import('../../observability_logs/xstate_helpers').then((mod) => ({ default: mod.StateMachinePlayground, @@ -37,6 +47,9 @@ const StateMachinePlayground = dynamic(() => export const LogsPageContent: React.FunctionComponent = () => { const { application, share } = useKibana<{ share: SharePublicStart }>().services; + + const isLogsStreamEnabled: boolean = useUiSetting(OBSERVABILITY_ENABLE_LOGS_STREAM, false); + const uiCapabilities = application?.capabilities; const onboardingLocator = share?.url.locators.get( OBSERVABILITY_ONBOARDING_LOCATOR @@ -47,30 +60,7 @@ export const LogsPageContent: React.FunctionComponent = () => { useReadOnlyBadge(!uiCapabilities?.logs?.save); - // !! Need to be kept in sync with the deepLinks in x-pack/plugins/observability_solution/infra/public/plugin.ts - const streamTab = { - app: 'logs', - title: streamTabTitle, - pathname: '/stream', - }; - - const anomaliesTab = { - app: 'logs', - title: anomaliesTabTitle, - pathname: '/anomalies', - }; - - const logCategoriesTab = { - app: 'logs', - title: logCategoriesTabTitle, - pathname: '/log-categories', - }; - - const settingsTab = { - app: 'logs', - title: settingsTabTitle, - pathname: '/settings', - }; + const routes = getLogsAppRoutes({ isLogsStreamEnabled }); const settingsLinkProps = useLinkProps({ app: 'logs', @@ -104,25 +94,36 @@ export const LogsPageContent: React.FunctionComponent = () => { )} - - - - + {routes.stream ? ( + + ) : ( + { + share.url.locators + .get(ALL_DATASETS_LOCATOR_ID) + ?.navigate({}); + + return null; + }} + /> + )} + + + {enableDeveloperRoutes && ( )} - - - - ( - - )} + + + + + } /> ); @@ -132,18 +133,6 @@ const pageTitle = i18n.translate('xpack.infra.header.logsTitle', { defaultMessage: 'Logs', }); -const streamTabTitle = i18n.translate('xpack.infra.logs.index.streamTabTitle', { - defaultMessage: 'Stream', -}); - -const anomaliesTabTitle = i18n.translate('xpack.infra.logs.index.anomaliesTabTitle', { - defaultMessage: 'Anomalies', -}); - -const logCategoriesTabTitle = i18n.translate('xpack.infra.logs.index.logCategoriesBetaBadgeTitle', { - defaultMessage: 'Categories', -}); - const settingsTabTitle = i18n.translate('xpack.infra.logs.index.settingsTabTitle', { defaultMessage: 'Settings', }); diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/routes.ts b/x-pack/plugins/observability_solution/infra/public/pages/logs/routes.ts new file mode 100644 index 0000000000000..a5c38672a8bed --- /dev/null +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/routes.ts @@ -0,0 +1,56 @@ +/* + * 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 { + logsAnomaliesTitle, + logCategoriesTitle, + settingsTitle, + streamTitle, +} from '../../translations'; + +export interface LogsRoute { + id: string; + title: string; + path: string; +} + +export interface LogsAppRoutes { + logsAnomalies: LogsRoute; + logsCategories: LogsRoute; + settings: LogsRoute; + stream?: LogsRoute; +} + +export const getLogsAppRoutes = ({ isLogsStreamEnabled }: { isLogsStreamEnabled: boolean }) => { + const routes: LogsAppRoutes = { + logsAnomalies: { + id: 'anomalies', + title: logsAnomaliesTitle, + path: '/anomalies', + }, + logsCategories: { + id: 'log-categories', + title: logCategoriesTitle, + path: '/log-categories', + }, + settings: { + id: 'settings', + title: settingsTitle, + path: '/settings', + }, + }; + + if (isLogsStreamEnabled) { + routes.stream = { + id: 'stream', + title: streamTitle, + path: '/stream', + }; + } + + return routes; +}; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/source_configuration_settings.tsx b/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/source_configuration_settings.tsx index 557fe1cfab314..d1df2a5820dd3 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/source_configuration_settings.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/source_configuration_settings.tsx @@ -20,6 +20,7 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; import { Prompt } from '@kbn/observability-shared-plugin/public'; import { useTrackPageview } from '@kbn/observability-shared-plugin/public'; import { useLogViewContext } from '@kbn/logs-shared-plugin/public'; +import { LogsDeprecationCallout } from '../../../components/logs_deprecation_callout'; import { SourceLoadingPage } from '../../../components/source_loading_page'; import { useLogsBreadcrumbs } from '../../../hooks/use_logs_breadcrumbs'; import { settingsTitle } from '../../../translations'; @@ -98,6 +99,7 @@ export const LogsSettingsPage = () => { data-test-subj="sourceConfigurationContent" restrictWidth > + diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page_logs_content.tsx b/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page_logs_content.tsx index d40014ed4468b..f59d3c1f03fbf 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page_logs_content.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page_logs_content.tsx @@ -26,6 +26,7 @@ import { useSelector } from '@xstate/react'; import stringify from 'json-stable-stringify'; import React, { useCallback, useEffect, useMemo } from 'react'; import usePrevious from 'react-use/lib/usePrevious'; +import { MatchedStateFromActor } from '@kbn/xstate-utils'; import { LogsDeprecationCallout } from '../../../components/logs_deprecation_callout'; import { TimeKey } from '../../../../common/time'; import { AutoSizer } from '../../../components/auto_sizer'; @@ -45,7 +46,6 @@ import { useLogStreamPageStateContext, } from '../../../observability_logs/log_stream_page/state'; import { type ParsedQuery } from '../../../observability_logs/log_stream_query_state'; -import { MatchedStateFromActor } from '../../../observability_logs/xstate_helpers'; import { datemathToEpochMillis, isValidDatemath } from '../../../utils/datemath'; import { LogsToolbar } from './page_toolbar'; import { PageViewLogInContext } from './page_view_log_in_context'; @@ -234,7 +234,7 @@ export const StreamPageLogsContent = React.memo<{ return ( <> - + diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page_providers.tsx index a8fd0cecf448b..497329782d879 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page_providers.tsx @@ -15,6 +15,7 @@ import { useLogStreamContext, useLogViewContext, } from '@kbn/logs-shared-plugin/public'; +import { MatchedStateFromActor } from '@kbn/xstate-utils'; import { LogStreamPageActorRef, LogStreamPageCallbacks, @@ -22,7 +23,6 @@ import { import { LogEntryFlyoutProvider } from '../../../containers/logs/log_flyout'; import { LogViewConfigurationProvider } from '../../../containers/logs/log_view_configuration'; import { ViewLogInContextProvider } from '../../../containers/logs/view_log_in_context'; -import { MatchedStateFromActor } from '../../../observability_logs/xstate_helpers'; const ViewLogInContext: FC> = ({ children }) => { const { startTimestamp, endTimestamp } = useLogPositionStateContext(); diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx index 47e5f58a258df..bca1a3858f5c9 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx @@ -17,7 +17,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; import { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; import { css } from '@emotion/react'; -import { useBoolean } from '../../../../../hooks/use_boolean'; +import { useBoolean } from '@kbn/react-hooks'; import { InfraGroupByOptions } from '../../../../../common/inventory/types'; import { CustomFieldPanel } from './custom_field_panel'; import { SnapshotGroupBy } from '../../../../../../common/http_api/snapshot_api'; diff --git a/x-pack/plugins/observability_solution/infra/public/plugin.ts b/x-pack/plugins/observability_solution/infra/public/plugin.ts index 843a23bdfccc5..daaa3510e1660 100644 --- a/x-pack/plugins/observability_solution/infra/public/plugin.ts +++ b/x-pack/plugins/observability_solution/infra/public/plugin.ts @@ -33,6 +33,8 @@ import { type AssetDetailsLocatorParams, type InventoryLocatorParams, } from '@kbn/observability-shared-plugin/common'; +import { OBSERVABILITY_ENABLE_LOGS_STREAM } from '@kbn/management-settings-ids'; +import { NavigationEntry } from '@kbn/observability-shared-plugin/public'; import type { InfraPublicConfig } from '../common/plugin_config_types'; import { createInventoryMetricRuleType } from './alerting/inventory'; import { createLogThresholdRuleType } from './alerting/log_threshold'; @@ -53,7 +55,14 @@ import type { } from './types'; import { getLogsHasDataFetcher, getLogsOverviewDataFetcher } from './utils/logs_overview_fetchers'; import type { LogStreamSerializedState } from './components/log_stream/types'; -import { hostsTitle, inventoryTitle, metricsExplorerTitle, metricsTitle } from './translations'; +import { + hostsTitle, + inventoryTitle, + logsTitle, + metricsExplorerTitle, + metricsTitle, +} from './translations'; +import { LogsAppRoutes, LogsRoute, getLogsAppRoutes } from './pages/logs/routes'; export class Plugin implements InfraClientPluginClass { public config: InfraPublicConfig; @@ -77,6 +86,8 @@ export class Plugin implements InfraClientPluginClass { } setup(core: InfraClientCoreSetup, pluginsSetup: InfraClientSetupDeps) { + const isLogsStreamEnabled = core.uiSettings.get(OBSERVABILITY_ENABLE_LOGS_STREAM, false); + if (pluginsSetup.home) { registerFeatures(pluginsSetup.home); } @@ -125,6 +136,8 @@ export class Plugin implements InfraClientPluginClass { core.settings.client.get$(enableInfrastructureHostsView), ]); + const logRoutes = getLogsAppRoutes({ isLogsStreamEnabled }); + /** !! Need to be kept in sync with the deepLinks in x-pack/plugins/observability_solution/infra/public/plugin.ts */ pluginsSetup.observabilityShared.navigation.registerSections( startDep$AndHostViewFlag$.pipe( @@ -137,32 +150,18 @@ export class Plugin implements InfraClientPluginClass { ], isInfrastructureHostsViewEnabled, ]) => { - const { infrastructure, logs, discover, fleet } = capabilities; + const { infrastructure, logs } = capabilities; return [ ...(logs.show ? [ { - label: 'Logs', + label: logsTitle, sortKey: 200, - entries: [ - ...(discover?.show && fleet?.read - ? [ - { - label: 'Explorer', - app: 'observability-logs-explorer', - path: '/', - isBetaFeature: true, - }, - ] - : []), - ...(this.config.featureFlags.logsUIEnabled - ? [ - { label: 'Stream', app: 'logs', path: '/stream' }, - { label: 'Anomalies', app: 'logs', path: '/anomalies' }, - { label: 'Categories', app: 'logs', path: '/log-categories' }, - ] - : []), - ], + entries: getLogsNavigationEntries({ + capabilities, + config: this.config, + routes: logRoutes, + }), }, ] : []), @@ -230,37 +229,7 @@ export class Plugin implements InfraClientPluginClass { euiIconType: 'logoObservability', order: 8100, appRoute: '/app/logs', - // !! Need to be kept in sync with the routes in x-pack/plugins/observability_solution/infra/public/pages/logs/page_content.tsx - deepLinks: [ - { - id: 'stream', - title: i18n.translate('xpack.infra.logs.index.streamTabTitle', { - defaultMessage: 'Stream', - }), - path: '/stream', - }, - { - id: 'anomalies', - title: i18n.translate('xpack.infra.logs.index.anomaliesTabTitle', { - defaultMessage: 'Anomalies', - }), - path: '/anomalies', - }, - { - id: 'log-categories', - title: i18n.translate('xpack.infra.logs.index.logCategoriesBetaBadgeTitle', { - defaultMessage: 'Categories', - }), - path: '/log-categories', - }, - { - id: 'settings', - title: i18n.translate('xpack.infra.logs.index.settingsTabTitle', { - defaultMessage: 'Settings', - }), - path: '/settings', - }, - ], + deepLinks: Object.values(logRoutes), category: DEFAULT_APP_CATEGORIES.observability, mount: async (params: AppMountParameters) => { // mount callback should not use setup dependencies, get start dependencies instead @@ -384,44 +353,47 @@ export class Plugin implements InfraClientPluginClass { } start(core: InfraClientCoreStart, plugins: InfraClientStartDeps) { - const { http } = core; + const { http, uiSettings } = core; + const isLogsStreamEnabled = uiSettings.get(OBSERVABILITY_ENABLE_LOGS_STREAM, false); const inventoryViews = this.inventoryViews.start({ http }); const metricsExplorerViews = this.metricsExplorerViews?.start({ http }); const telemetry = this.telemetry.start(); - plugins.uiActions.registerAction({ - id: ADD_LOG_STREAM_ACTION_ID, - grouping: [COMMON_EMBEDDABLE_GROUPING.legacy], - order: 30, - getDisplayName: () => - i18n.translate('xpack.infra.logStreamEmbeddable.displayName', { - defaultMessage: 'Log stream (deprecated)', - }), - getDisplayNameTooltip: () => - i18n.translate('xpack.infra.logStreamEmbeddable.description', { - defaultMessage: - 'Add a table of live streaming logs. For a more efficient experience, we recommend using the Discover Page to create a saved search instead of using Log stream.', - }), - getIconType: () => 'logsApp', - isCompatible: async ({ embeddable }) => { - return apiCanAddNewPanel(embeddable); - }, - execute: async ({ embeddable }) => { - if (!apiCanAddNewPanel(embeddable)) throw new IncompatibleActionError(); - embeddable.addNewPanel( - { - panelType: LOG_STREAM_EMBEDDABLE, - initialState: { - title: i18n.translate('xpack.infra.logStreamEmbeddable.title', { - defaultMessage: 'Log stream', - }), + if (isLogsStreamEnabled) { + plugins.uiActions.registerAction({ + id: ADD_LOG_STREAM_ACTION_ID, + grouping: [COMMON_EMBEDDABLE_GROUPING.legacy], + order: 30, + getDisplayName: () => + i18n.translate('xpack.infra.logStreamEmbeddable.displayName', { + defaultMessage: 'Log stream (deprecated)', + }), + getDisplayNameTooltip: () => + i18n.translate('xpack.infra.logStreamEmbeddable.description', { + defaultMessage: + 'Add a table of live streaming logs. For a more efficient experience, we recommend using the Discover Page to create a saved search instead of using Log stream.', + }), + getIconType: () => 'logsApp', + isCompatible: async ({ embeddable }) => { + return apiCanAddNewPanel(embeddable); + }, + execute: async ({ embeddable }) => { + if (!apiCanAddNewPanel(embeddable)) throw new IncompatibleActionError(); + embeddable.addNewPanel( + { + panelType: LOG_STREAM_EMBEDDABLE, + initialState: { + title: i18n.translate('xpack.infra.logStreamEmbeddable.title', { + defaultMessage: 'Log stream', + }), + }, }, - }, - true - ); - }, - }); - plugins.uiActions.attachAction(ADD_PANEL_TRIGGER, ADD_LOG_STREAM_ACTION_ID); + true + ); + }, + }); + plugins.uiActions.attachAction(ADD_PANEL_TRIGGER, ADD_LOG_STREAM_ACTION_ID); + } const startContract: InfraClientStartExports = { inventoryViews, @@ -434,3 +406,42 @@ export class Plugin implements InfraClientPluginClass { stop() {} } + +const getLogsNavigationEntries = ({ + capabilities, + config, + routes, +}: { + capabilities: CoreStart['application']['capabilities']; + config: InfraPublicConfig; + routes: LogsAppRoutes; +}) => { + const entries: NavigationEntry[] = []; + + if (!config.featureFlags.logsUIEnabled) return entries; + + if (capabilities.discover?.show && capabilities.fleet?.read) { + entries.push({ + label: 'Explorer', + app: 'observability-logs-explorer', + path: '/', + isBetaFeature: true, + }); + } + + // Display Stream nav entry when Logs Stream is enabled + if (routes.stream) entries.push(createNavEntryFromRoute(routes.stream)); + // Display always Logs Anomalies and Logs Categories entries + entries.push(createNavEntryFromRoute(routes.logsAnomalies)); + entries.push(createNavEntryFromRoute(routes.logsCategories)); + // Display Logs Settings entry when Logs Stream is not enabled + if (!routes.stream) entries.push(createNavEntryFromRoute(routes.settings)); + + return entries; +}; + +const createNavEntryFromRoute = ({ path, title }: LogsRoute): NavigationEntry => ({ + app: 'logs', + label: title, + path, +}); diff --git a/x-pack/plugins/observability_solution/infra/public/translations.ts b/x-pack/plugins/observability_solution/infra/public/translations.ts index 2e9153ce171b9..ecb72b3df4b01 100644 --- a/x-pack/plugins/observability_solution/infra/public/translations.ts +++ b/x-pack/plugins/observability_solution/infra/public/translations.ts @@ -19,14 +19,14 @@ export const streamTitle = i18n.translate('xpack.infra.logs.index.streamTabTitle defaultMessage: 'Stream', }); -export const anomaliesTitle = i18n.translate('xpack.infra.logs.index.anomaliesTabTitle', { - defaultMessage: 'Anomalies', +export const logsAnomaliesTitle = i18n.translate('xpack.infra.logs.index.anomaliesTabTitle', { + defaultMessage: 'Logs Anomalies', }); export const logCategoriesTitle = i18n.translate( 'xpack.infra.logs.index.logCategoriesBetaBadgeTitle', { - defaultMessage: 'Categories', + defaultMessage: 'Logs Categories', } ); diff --git a/x-pack/plugins/observability_solution/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/observability_solution/infra/server/lib/adapters/framework/adapter_types.ts index 2cbf6b61623cf..b8dd11a17fb0b 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/adapters/framework/adapter_types.ts @@ -39,6 +39,7 @@ import { ApmDataAccessPluginStart, } from '@kbn/apm-data-access-plugin/server'; import { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/server'; +import { ServerlessPluginStart } from '@kbn/serverless/server'; import type { EntityManagerServerPluginStart, EntityManagerServerPluginSetup, @@ -60,6 +61,7 @@ export interface InfraServerPluginSetupDeps { metricsDataAccess: MetricsDataPluginSetup; profilingDataAccess?: ProfilingDataAccessPluginSetup; apmDataAccess: ApmDataAccessPluginSetup; + serverless?: ServerlessPluginStart; entityManager: EntityManagerServerPluginSetup; } diff --git a/x-pack/plugins/observability_solution/infra/server/plugin.ts b/x-pack/plugins/observability_solution/infra/server/plugin.ts index 73d49ed938546..b8becb916a4e3 100644 --- a/x-pack/plugins/observability_solution/infra/server/plugin.ts +++ b/x-pack/plugins/observability_solution/infra/server/plugin.ts @@ -59,6 +59,7 @@ import { } from './types'; import { UsageCollector } from './usage/usage_collector'; import { mapSourceToLogView } from './utils/map_source_to_log_view'; +import { uiSettings } from '../common/ui_settings'; export const config: PluginConfigDescriptor = { schema: schema.object({ @@ -211,6 +212,9 @@ export class InfraServerPlugin const inventoryViews = this.inventoryViews.setup(); const metricsExplorerViews = this.metricsExplorerViews?.setup(); + // Register uiSettings config + core.uiSettings.register(uiSettings); + // Register saved object types core.savedObjects.registerType(infraSourceConfigurationSavedObjectType); core.savedObjects.registerType(inventoryViewSavedObjectType); diff --git a/x-pack/plugins/observability_solution/infra/tsconfig.json b/x-pack/plugins/observability_solution/infra/tsconfig.json index e7aade296fa8a..fea285b3a794e 100644 --- a/x-pack/plugins/observability_solution/infra/tsconfig.json +++ b/x-pack/plugins/observability_solution/infra/tsconfig.json @@ -110,6 +110,9 @@ "@kbn/observability-alerting-rule-utils", "@kbn/core-application-browser", "@kbn/shared-ux-page-no-data-types", + "@kbn/xstate-utils", + "@kbn/management-settings-ids", + "@kbn/core-ui-settings-common", "@kbn/entityManager-plugin", "@kbn/observability-utils", "@kbn/entities-schema" diff --git a/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts b/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts index c661976fd0765..0c6ceede07561 100644 --- a/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts +++ b/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts @@ -366,6 +366,12 @@ export function createNavTree(pluginsStart: ObservabilityPublicPluginsStart) { defaultMessage: 'Logs categories', }), }, + { + link: 'logs:settings', + title: i18n.translate('xpack.observability.obltNav.otherTools.logsSettings', { + defaultMessage: 'Logs settings', + }), + }, { link: 'maps' }, { link: 'canvas' }, { link: 'graph' }, diff --git a/x-pack/plugins/observability_solution/observability/tsconfig.json b/x-pack/plugins/observability_solution/observability/tsconfig.json index d7a33cb6492cb..cc8cef2a9716a 100644 --- a/x-pack/plugins/observability_solution/observability/tsconfig.json +++ b/x-pack/plugins/observability_solution/observability/tsconfig.json @@ -113,7 +113,7 @@ "@kbn/io-ts-utils", "@kbn/core-ui-settings-server-mocks", "@kbn/es-types", - "@kbn/logging-mocks" + "@kbn/logging-mocks", ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index d65dddae39479..e47a52ec5df58 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -23213,7 +23213,6 @@ "xpack.infra.logs.highlights.highlightTermsFieldLabel": "Termes à mettre en surbrillance", "xpack.infra.logs.index.anomaliesTabTitle": "Anomalies", "xpack.infra.logs.index.logCategoriesBetaBadgeTitle": "Catégories", - "xpack.infra.logs.index.logsLabel": "Logs", "xpack.infra.logs.index.settingsTabTitle": "Paramètres", "xpack.infra.logs.index.streamTabTitle": "Flux", "xpack.infra.logs.logCategoriesTitle": "Catégories", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 3883a41164835..8e2125deaf18c 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -22963,7 +22963,6 @@ "xpack.infra.logs.highlights.highlightTermsFieldLabel": "ハイライトする用語", "xpack.infra.logs.index.anomaliesTabTitle": "異常", "xpack.infra.logs.index.logCategoriesBetaBadgeTitle": "カテゴリー", - "xpack.infra.logs.index.logsLabel": "ログ", "xpack.infra.logs.index.settingsTabTitle": "設定", "xpack.infra.logs.index.streamTabTitle": "ストリーム", "xpack.infra.logs.logCategoriesTitle": "カテゴリー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 823092906f7ba..3b7a338fab717 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -22994,7 +22994,6 @@ "xpack.infra.logs.highlights.highlightTermsFieldLabel": "要突出显示的词", "xpack.infra.logs.index.anomaliesTabTitle": "异常", "xpack.infra.logs.index.logCategoriesBetaBadgeTitle": "类别", - "xpack.infra.logs.index.logsLabel": "日志", "xpack.infra.logs.index.settingsTabTitle": "设置", "xpack.infra.logs.index.streamTabTitle": "流式传输", "xpack.infra.logs.logCategoriesTitle": "类别", diff --git a/x-pack/test/functional/apps/infra/logs/log_stream.ts b/x-pack/test/functional/apps/infra/logs/log_stream.ts index 8592287477826..16dcc038f7aab 100644 --- a/x-pack/test/functional/apps/infra/logs/log_stream.ts +++ b/x-pack/test/functional/apps/infra/logs/log_stream.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { OBSERVABILITY_ENABLE_LOGS_STREAM } from '@kbn/management-settings-ids'; import { URL } from 'url'; import { FtrProviderContext } from '../../../ftr_provider_context'; @@ -16,17 +17,20 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const retry = getService('retry'); const browser = getService('browser'); const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); describe('Log stream', function () { describe('Legacy URL handling', () => { describe('Correctly handles legacy versions of logFilter', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/infra/8.0.0/logs_and_metrics'); + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: true }); }); after(async () => { await esArchiver.unload( 'x-pack/test/functional/es_archives/infra/8.0.0/logs_and_metrics' ); + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: false }); }); it('Expression and kind', async () => { const location = { diff --git a/x-pack/test/functional/apps/infra/logs/log_stream_date_nano.ts b/x-pack/test/functional/apps/infra/logs/log_stream_date_nano.ts index ed1f85248b303..141d1bc38c3d3 100644 --- a/x-pack/test/functional/apps/infra/logs/log_stream_date_nano.ts +++ b/x-pack/test/functional/apps/infra/logs/log_stream_date_nano.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { OBSERVABILITY_ENABLE_LOGS_STREAM } from '@kbn/management-settings-ids'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { DATES } from '../constants'; @@ -14,6 +15,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const logsUi = getService('logsUi'); const find = getService('find'); + const kibanaServer = getService('kibanaServer'); const logFilter = { timeRange: { from: DATES.metricsAndLogs.stream.startWithData, @@ -24,9 +26,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('Log stream supports nano precision', function () { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/infra/logs_with_nano_date'); + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: true }); }); after(async () => { await esArchiver.unload('x-pack/test/functional/es_archives/infra/logs_with_nano_date'); + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: false }); }); it('should display logs entries containing date_nano timestamps properly ', async () => { diff --git a/x-pack/test/functional/apps/infra/logs/logs_source_configuration.ts b/x-pack/test/functional/apps/infra/logs/logs_source_configuration.ts index 4fdb4687faf6d..84158051021c3 100644 --- a/x-pack/test/functional/apps/infra/logs/logs_source_configuration.ts +++ b/x-pack/test/functional/apps/infra/logs/logs_source_configuration.ts @@ -10,6 +10,7 @@ import { ELASTIC_HTTP_VERSION_HEADER, X_ELASTIC_INTERNAL_ORIGIN_REQUEST, } from '@kbn/core-http-common'; +import { OBSERVABILITY_ENABLE_LOGS_STREAM } from '@kbn/management-settings-ids'; import { DATES } from '../constants'; import { FtrProviderContext } from '../../../ftr_provider_context'; @@ -31,9 +32,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('Logs Source Configuration', function () { before(async () => { await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: true }); }); after(async () => { await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: false }); }); describe('Allows indices configuration', () => { diff --git a/x-pack/test/functional/apps/infra/page_not_found.ts b/x-pack/test/functional/apps/infra/page_not_found.ts index 479d9979b918a..eb1fc77b4f9f9 100644 --- a/x-pack/test/functional/apps/infra/page_not_found.ts +++ b/x-pack/test/functional/apps/infra/page_not_found.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { OBSERVABILITY_ENABLE_LOGS_STREAM } from '@kbn/management-settings-ids'; import { FtrProviderContext } from '../../ftr_provider_context'; const logsPages = ['logs/stream', 'logs/anomalies', 'logs/log-categories', 'logs/settings']; @@ -19,14 +20,22 @@ const metricsPages = [ ]; export default ({ getPageObjects, getService }: FtrProviderContext) => { - const find = getService('find'); const pageObjects = getPageObjects(['common', 'infraHome']); + const find = getService('find'); + const kibanaServer = getService('kibanaServer'); const testSubjects = getService('testSubjects'); describe('Infra Not Found page', function () { this.tags('includeFirefox'); describe('Logs', () => { + before(async () => { + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: true }); + }); + after(async () => { + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: false }); + }); + it('should render the not found page when the route does not exist', async () => { await pageObjects.common.navigateToApp('logs/broken-link'); await testSubjects.existOrFail('infraNotFoundPage'); From 1b4ebaa85261074aec775d23165474863ddea45a Mon Sep 17 00:00:00 2001 From: Robert Jaszczurek <92210485+rbrtj@users.noreply.github.com> Date: Wed, 9 Oct 2024 10:14:50 +0200 Subject: [PATCH 2/8] [ML] Hide ES|QL based saved searches in ML & Transforms (#195084) ## Summary Fix for: [#187962](https://github.com/elastic/kibana/issues/187962) We were displaying ES|QL based searches in various areas of ML, not just in the places mentioned in the issue. Before: ![Screenshot 2024-10-04 at 16 16 26](https://github.com/user-attachments/assets/dff7e1d6-4c8e-4916-acec-c6b9931c2a39) Then, after selecting the ESQL based search: ![image](https://github.com/user-attachments/assets/9314cd0b-442a-4287-9d29-799e172f929a) After the fix: ![image](https://github.com/user-attachments/assets/e660ef24-c585-4d95-bcf1-2578ec9e663d) --- .../components/source_selection/source_selection.tsx | 9 +++++++-- .../datavisualizer/data_drift/index_patterns_picker.tsx | 7 +++++++ .../jobs/new_job/pages/index_or_search/page.tsx | 6 ++++++ .../components/search_selection/search_selection.tsx | 6 ++++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.tsx index ae03a0779edf1..5aa0ccc46a5cd 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.tsx @@ -11,7 +11,7 @@ import { EuiCallOut, EuiPageBody, EuiPanel, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { getNestedProperty } from '@kbn/ml-nested-property'; import { SavedObjectFinder } from '@kbn/saved-objects-finder-plugin/public'; -import type { SavedObjectCommon } from '@kbn/saved-objects-finder-plugin/common'; +import type { FinderAttributes, SavedObjectCommon } from '@kbn/saved-objects-finder-plugin/common'; import { CreateDataViewButton } from '../../../../../components/create_data_view_button'; import { useMlKibana, useNavigateToPath } from '../../../../../contexts/kibana'; import { useToastNotificationService } from '../../../../../services/toast_notification_service'; @@ -22,6 +22,8 @@ import { const fixedPageSize: number = 20; +type SavedObject = SavedObjectCommon; + export const SourceSelection: FC = () => { const { services: { @@ -41,7 +43,7 @@ export const SourceSelection: FC = () => { id: string, type: string, fullName?: string, - savedObject?: SavedObjectCommon + savedObject?: SavedObject ) => { // Kibana data views including `:` are cross-cluster search indices // and are not supported by Data Frame Analytics yet. For saved searches @@ -142,6 +144,9 @@ export const SourceSelection: FC = () => { defaultMessage: 'Saved search', } ), + showSavedObject: (savedObject: SavedObject) => + // ES|QL Based saved searches are not supported in DFA, filter them out + savedObject.attributes.isTextBasedQuery !== true, }, { type: 'index-pattern', diff --git a/x-pack/plugins/ml/public/application/datavisualizer/data_drift/index_patterns_picker.tsx b/x-pack/plugins/ml/public/application/datavisualizer/data_drift/index_patterns_picker.tsx index 04dd9ca764ab0..1f31ce934e442 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/data_drift/index_patterns_picker.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/data_drift/index_patterns_picker.tsx @@ -14,12 +14,16 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectFinder } from '@kbn/saved-objects-finder-plugin/public'; import { type DataViewEditorService as DataViewEditorServiceSpec } from '@kbn/data-view-editor-plugin/public'; import { INDEX_PATTERN_TYPE } from '@kbn/data-views-plugin/public'; +import type { FinderAttributes, SavedObjectCommon } from '@kbn/saved-objects-finder-plugin/common'; import { createPath } from '../../routing/router'; import { ML_PAGES } from '../../../../common/constants/locator'; import { DataDriftIndexPatternsEditor } from './data_drift_index_patterns_editor'; import { MlPageHeader } from '../../components/page_header'; import { useMlKibana, useNavigateToPath } from '../../contexts/kibana'; + +type SavedObject = SavedObjectCommon; + export const DataDriftIndexOrSearchRedirect: FC = () => { const navigateToPath = useNavigateToPath(); const { contentManagement, uiSettings } = useMlKibana().services; @@ -65,6 +69,9 @@ export const DataDriftIndexOrSearchRedirect: FC = () => { defaultMessage: 'Saved search', } ), + showSavedObject: (savedObject: SavedObject) => + // ES|QL Based saved searches are not supported in Data Drift, filter them out + savedObject.attributes.isTextBasedQuery !== true, }, { type: 'index-pattern', diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/index_or_search/page.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/index_or_search/page.tsx index 972b8dc09e3ef..6e630ca61886f 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/index_or_search/page.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/index_or_search/page.tsx @@ -11,6 +11,7 @@ import { EuiFlexGroup, EuiPageBody, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectFinder } from '@kbn/saved-objects-finder-plugin/public'; +import type { FinderAttributes, SavedObjectCommon } from '@kbn/saved-objects-finder-plugin/common'; import { CreateDataViewButton } from '../../../../components/create_data_view_button'; import { useMlKibana, useNavigateToPath } from '../../../../contexts/kibana'; import { MlPageHeader } from '../../../../components/page_header'; @@ -21,6 +22,8 @@ export interface PageProps { const RESULTS_PER_PAGE = 20; +type SavedObject = SavedObjectCommon; + export const Page: FC = ({ nextStepPath, extraButtons, @@ -69,6 +72,9 @@ export const Page: FC = ({ defaultMessage: 'Saved search', } ), + showSavedObject: (savedObject: SavedObject) => + // ES|QL Based saved searches are not supported across ML, filter them out + savedObject.attributes.isTextBasedQuery !== true, }, { type: 'index-pattern', diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/search_selection/search_selection.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/search_selection/search_selection.tsx index 3c6fcc67f0c7e..7c0b03f7b9856 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/search_selection/search_selection.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/search_selection/search_selection.tsx @@ -11,6 +11,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import React, { type FC, Fragment } from 'react'; import { SavedObjectFinder } from '@kbn/saved-objects-finder-plugin/public'; +import type { FinderAttributes, SavedObjectCommon } from '@kbn/saved-objects-finder-plugin/common'; import { useAppDependencies } from '../../../../app_dependencies'; interface SearchSelectionProps { @@ -19,6 +20,8 @@ interface SearchSelectionProps { canEditDataView: boolean; } +type SavedObject = SavedObjectCommon; + const fixedPageSize: number = 8; export const SearchSelection: FC = ({ @@ -64,6 +67,9 @@ export const SearchSelection: FC = ({ defaultMessage: 'Saved search', } ), + showSavedObject: (savedObject: SavedObject) => + // ES|QL Based saved searches are not supported in transforms, filter them out + savedObject.attributes.isTextBasedQuery !== true, }, { type: 'index-pattern', From 6df672421d6df47cb3e8f86ab9448f021dabc871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Georgiana-Andreea=20Onolea=C8=9B=C4=83?= Date: Wed, 9 Oct 2024 11:22:57 +0300 Subject: [PATCH 3/8] [ResponseOps][Rules] Rule type selection modal content cut off on small screens due to lack of vertical scrolling (#195234) ## Summary Closes https://github.com/elastic/kibana/issues/184048 - the rule type selection modal is now scrollable on smaller windows https://github.com/user-attachments/assets/47082b35-02a7-4b67-9a88-ee4200908bef Co-authored-by: Antonio --- .../src/rule_type_modal/components/rule_type_list.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/kbn-alerts-ui-shared/src/rule_type_modal/components/rule_type_list.tsx b/packages/kbn-alerts-ui-shared/src/rule_type_modal/components/rule_type_list.tsx index 9b84bb83cf1fa..4e21e428f3c5c 100644 --- a/packages/kbn-alerts-ui-shared/src/rule_type_modal/components/rule_type_list.tsx +++ b/packages/kbn-alerts-ui-shared/src/rule_type_modal/components/rule_type_list.tsx @@ -97,6 +97,8 @@ export const RuleTypeList: React.FC = ({ grow={1} style={{ paddingTop: euiTheme.size.base /* Match drop shadow padding in the right column */, + paddingRight: euiTheme.size.base, + overflowY: 'auto', }} > From 88cf632da91dbbfc7e119cdc6d930baed57de4e6 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Wed, 9 Oct 2024 10:34:05 +0200 Subject: [PATCH 4/8] [ML] Updates for Trained Models table layout and model states (#194614) ## Summary - Updates Trained Models table layout - Adds the E5 model disclaimer - Removes redundant success toasts about model download, deletion, and start of a deployment image ### Checklist - [x] 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 - [x] [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 - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] 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)) - [x] 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)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --- .../src/constants/trained_models.ts | 9 + .../analytics_list/use_table_settings.ts | 47 ++- .../data_drift/data_view_editor.tsx | 3 +- .../model_management/delete_models_modal.tsx | 12 +- .../model_management/get_model_state.tsx | 62 ++- .../model_management/model_actions.tsx | 103 ++--- .../model_management/models_list.tsx | 358 ++++++++---------- .../model_management/model_provider.test.ts | 8 + .../translations/translations/fr-FR.json | 9 - .../translations/translations/ja-JP.json | 8 - .../translations/translations/zh-CN.json | 9 - .../apis/ml/trained_models/model_downloads.ts | 4 + .../services/ml/trained_models_table.ts | 18 +- 13 files changed, 335 insertions(+), 315 deletions(-) diff --git a/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts b/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts index 95337518361e9..9fd3483771a9f 100644 --- a/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts +++ b/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts @@ -120,6 +120,10 @@ export const ELASTIC_MODEL_DEFINITIONS: Record< license: 'MIT', licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small', type: ['pytorch', 'text_embedding'], + disclaimer: i18n.translate('xpack.ml.trainedModels.modelsList.e5v1Disclaimer', { + defaultMessage: + 'This E5 model, as defined, hosted, integrated and used in conjunction with our other Elastic Software is covered by our standard warranty.', + }), }, [E5_LINUX_OPTIMIZED_MODEL_ID]: { modelName: 'e5', @@ -138,6 +142,10 @@ export const ELASTIC_MODEL_DEFINITIONS: Record< license: 'MIT', licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small_linux-x86_64', type: ['pytorch', 'text_embedding'], + disclaimer: i18n.translate('xpack.ml.trainedModels.modelsList.e5v1Disclaimer', { + defaultMessage: + 'This E5 model, as defined, hosted, integrated and used in conjunction with our other Elastic Software is covered by our standard warranty.', + }), }, } as const); @@ -167,6 +175,7 @@ export interface ModelDefinition { /** Link to the external license/documentation page */ licenseUrl?: string; type?: readonly string[]; + disclaimer?: string; } export type ModelDefinitionResponse = ModelDefinition & { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts index 670efb1627ef7..fa24a65c425bc 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts @@ -30,9 +30,11 @@ export interface CriteriaWithPagination extends Criteria { }; } -interface UseTableSettingsReturnValue { +interface UseTableSettingsReturnValue { onTableChange: EuiBasicTableProps['onChange']; - pagination: Required>; + pagination: HidePagination extends true + ? Required> | boolean + : Required>; sorting: { sort: { field: keyof T; @@ -44,8 +46,31 @@ interface UseTableSettingsReturnValue { export function useTableSettings( totalItemCount: number, pageState: ListingPageUrlState, - updatePageState: (update: Partial) => void -): UseTableSettingsReturnValue { + updatePageState: (update: Partial) => void, + hide: true +): UseTableSettingsReturnValue; + +export function useTableSettings( + totalItemCount: number, + pageState: ListingPageUrlState, + updatePageState: (update: Partial) => void, + hide?: false +): UseTableSettingsReturnValue; + +/** + * + * @param totalItemCount + * @param pageState + * @param updatePageState + * @param hide If true, hides pagination when total number of items is lower that the smallest per page option + * @returns + */ +export function useTableSettings( + totalItemCount: number, + pageState: ListingPageUrlState, + updatePageState: (update: Partial) => void, + hide: boolean = false +): UseTableSettingsReturnValue { const { pageIndex, pageSize, sortField, sortDirection } = pageState; const onTableChange: EuiBasicTableProps['onChange'] = useCallback( @@ -66,15 +91,19 @@ export function useTableSettings( [pageState, updatePageState] ); - const pagination = useMemo( - () => ({ + const pagination = useMemo(() => { + if (hide && totalItemCount <= Math.min(...PAGE_SIZE_OPTIONS)) { + // Hide pagination if total number of items is lower that the smallest per page option + return false; + } + + return { pageIndex, pageSize, totalItemCount, pageSizeOptions: PAGE_SIZE_OPTIONS, - }), - [totalItemCount, pageIndex, pageSize] - ); + }; + }, [totalItemCount, pageIndex, pageSize, hide]); const sorting = useMemo( () => ({ diff --git a/x-pack/plugins/ml/public/application/datavisualizer/data_drift/data_view_editor.tsx b/x-pack/plugins/ml/public/application/datavisualizer/data_drift/data_view_editor.tsx index 5f52ef1c928f8..eafe31cb0f355 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/data_drift/data_view_editor.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/data_drift/data_view_editor.tsx @@ -80,8 +80,7 @@ export function DataViewEditor({ const { onTableChange, pagination } = useTableSettings( matchedReferenceIndices.length, pageState, - // @ts-expect-error callback will have all the 4 necessary params - updatePageState + updatePageState as Parameters['2'] ); const pageOfItems = useMemo(() => { diff --git a/x-pack/plugins/ml/public/application/model_management/delete_models_modal.tsx b/x-pack/plugins/ml/public/application/model_management/delete_models_modal.tsx index 1e08ae9874567..0f5c515c22776 100644 --- a/x-pack/plugins/ml/public/application/model_management/delete_models_modal.tsx +++ b/x-pack/plugins/ml/public/application/model_management/delete_models_modal.tsx @@ -35,7 +35,7 @@ interface DeleteModelsModalProps { export const DeleteModelsModal: FC = ({ models, onClose }) => { const trainedModelsApiService = useTrainedModelsApiService(); - const { displayErrorToast, displaySuccessToast } = useToastNotificationService(); + const { displayErrorToast } = useToastNotificationService(); const [canDeleteModel, setCanDeleteModel] = useState(false); const [deletePipelines, setDeletePipelines] = useState(false); @@ -66,16 +66,6 @@ export const DeleteModelsModal: FC = ({ models, onClose }) ) ); - displaySuccessToast( - i18n.translate('xpack.ml.trainedModels.modelsList.successfullyDeletedMessage', { - defaultMessage: - '{modelsCount, plural, one {Model {modelIds}} other {# models}} {modelsCount, plural, one {has} other {have}} been successfully deleted', - values: { - modelsCount: modelIds.length, - modelIds: modelIds.join(', '), - }, - }) - ); } catch (error) { displayErrorToast( error, diff --git a/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx b/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx index 8591a3b9e8dc9..d8bf2b8084a6a 100644 --- a/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx +++ b/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx @@ -5,8 +5,17 @@ * 2.0. */ +import React from 'react'; import { DEPLOYMENT_STATE, MODEL_STATE, type ModelState } from '@kbn/ml-trained-models-utils'; -import type { EuiHealthProps } from '@elastic/eui'; +import { + EuiBadge, + EuiHealth, + EuiLoadingSpinner, + type EuiHealthProps, + EuiFlexGroup, + EuiFlexItem, + EuiText, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import type { ModelItem } from './models_list'; @@ -33,11 +42,11 @@ export const getModelDeploymentState = (model: ModelItem): ModelState | undefine export const getModelStateColor = ( state: ModelState | undefined -): { color: EuiHealthProps['color']; name: string } | null => { +): { color: EuiHealthProps['color']; name: string; component?: React.ReactNode } | null => { switch (state) { case MODEL_STATE.DOWNLOADED: return { - color: 'subdued', + color: 'success', name: i18n.translate('xpack.ml.trainedModels.modelsList.modelState.downloadedName', { defaultMessage: 'Ready to deploy', }), @@ -46,37 +55,64 @@ export const getModelStateColor = ( return { color: 'primary', name: i18n.translate('xpack.ml.trainedModels.modelsList.modelState.downloadingName', { - defaultMessage: 'Downloading...', + defaultMessage: 'Downloading', }), }; case MODEL_STATE.STARTED: return { - color: 'success', + color: '#E6F9F7', name: i18n.translate('xpack.ml.trainedModels.modelsList.modelState.startedName', { defaultMessage: 'Deployed', }), + get component() { + return ( + + + {this.name} + + + ); + }, }; case MODEL_STATE.STARTING: return { color: 'success', name: i18n.translate('xpack.ml.trainedModels.modelsList.modelState.startingName', { - defaultMessage: 'Starting deployment...', + defaultMessage: 'Deploying', }), + get component() { + return ( + + + + + + {this.name} + + + ); + }, }; case MODEL_STATE.STOPPING: return { color: 'accent', name: i18n.translate('xpack.ml.trainedModels.modelsList.modelState.stoppingName', { - defaultMessage: 'Stopping deployment...', + defaultMessage: 'Stopping', }), + get component() { + return ( + + + + + + {this.name} + + + ); + }, }; case MODEL_STATE.NOT_DOWNLOADED: - return { - color: '#d4dae5', - name: i18n.translate('xpack.ml.trainedModels.modelsList.modelState.notDownloadedName', { - defaultMessage: 'Not downloaded', - }), - }; default: return null; } diff --git a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx index b9e0c39578349..b4ddff093933a 100644 --- a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx +++ b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx @@ -8,7 +8,7 @@ import type { Action } from '@elastic/eui/src/components/basic_table/action_types'; import { i18n } from '@kbn/i18n'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; -import { EuiToolTip } from '@elastic/eui'; +import { EuiToolTip, useIsWithinMaxBreakpoint } from '@elastic/eui'; import React, { useCallback, useMemo, useEffect, useState } from 'react'; import { BUILT_IN_MODEL_TAG, @@ -53,6 +53,8 @@ export function useModelActions({ fetchModels: () => Promise; modelAndDeploymentIds: string[]; }): Array> { + const isMobileLayout = useIsWithinMaxBreakpoint('l'); + const { services: { application: { navigateToUrl }, @@ -132,7 +134,7 @@ export function useModelActions({ [] ); - return useMemo( + return useMemo>>( () => [ { name: i18n.translate('xpack.ml.trainedModels.modelsList.viewTrainingDataNameActionLabel', { @@ -203,12 +205,18 @@ export function useModelActions({ ), 'data-test-subj': 'mlModelsTableRowStartDeploymentAction', icon: 'play', - type: 'icon', + // @ts-ignore + type: isMobileLayout ? 'icon' : 'button', isPrimary: true, + color: 'success', enabled: (item) => { - return canStartStopTrainedModels && !isLoading && item.state !== MODEL_STATE.DOWNLOADING; + return canStartStopTrainedModels && !isLoading; + }, + available: (item) => { + return ( + item.model_type === TRAINED_MODEL_TYPE.PYTORCH && item.state === MODEL_STATE.DOWNLOADED + ); }, - available: (item) => item.model_type === TRAINED_MODEL_TYPE.PYTORCH, onClick: async (item) => { const modelDeploymentParams = await getUserInputModelDeploymentParams( item, @@ -234,14 +242,6 @@ export function useModelActions({ : {}), } ); - displaySuccessToast( - i18n.translate('xpack.ml.trainedModels.modelsList.startSuccess', { - defaultMessage: 'Deployment for "{modelId}" has been started successfully.', - values: { - modelId: item.model_id, - }, - }) - ); await fetchModels(); } catch (e) { displayErrorToast( @@ -342,6 +342,7 @@ export function useModelActions({ available: (item) => item.model_type === TRAINED_MODEL_TYPE.PYTORCH && canStartStopTrainedModels && + // Deployment can be either started, starting, or exist in a failed state (item.state === MODEL_STATE.STARTED || item.state === MODEL_STATE.STARTING) && // Only show the action if there is at least one deployment that is not used by the inference service (!Array.isArray(item.inference_apis) || @@ -373,16 +374,6 @@ export function useModelActions({ force: requireForceStop, } ); - displaySuccessToast( - i18n.translate('xpack.ml.trainedModels.modelsList.stopSuccess', { - defaultMessage: - '{numberOfDeployments, plural, one {Deployment} other {Deployments}} for "{modelId}" has been stopped successfully.', - values: { - modelId: item.model_id, - numberOfDeployments: deploymentIds.length, - }, - }) - ); if (Object.values(results).some((r) => r.error !== undefined)) { Object.entries(results).forEach(([id, r]) => { if (r.error !== undefined) { @@ -423,7 +414,9 @@ export function useModelActions({ }), 'data-test-subj': 'mlModelsTableRowDownloadModelAction', icon: 'download', - type: 'icon', + color: 'text', + // @ts-ignore + type: isMobileLayout ? 'icon' : 'button', isPrimary: true, available: (item) => canCreateTrainedModels && item.state === MODEL_STATE.NOT_DOWNLOADED, enabled: (item) => !isLoading, @@ -480,10 +473,16 @@ export function useModelActions({ }, { name: (model) => { - return ( + return model.state === MODEL_STATE.DOWNLOADING ? ( + <> + {i18n.translate('xpack.ml.trainedModels.modelsList.deleteModelActionLabel', { + defaultMessage: 'Cancel', + })} + + ) : ( <> {i18n.translate('xpack.ml.trainedModels.modelsList.deleteModelActionLabel', { - defaultMessage: 'Delete model', + defaultMessage: 'Delete', })} ); @@ -491,27 +490,35 @@ export function useModelActions({ description: (model: ModelItem) => { const hasDeployments = model.deployment_ids.length > 0; const { hasInferenceServices } = model; - return hasInferenceServices - ? i18n.translate( - 'xpack.ml.trainedModels.modelsList.deleteDisabledWithInferenceServicesTooltip', - { - defaultMessage: 'Model is used by the _inference API', - } - ) - : hasDeployments - ? i18n.translate( - 'xpack.ml.trainedModels.modelsList.deleteDisabledWithDeploymentsTooltip', - { - defaultMessage: 'Model has started deployments', - } - ) - : i18n.translate('xpack.ml.trainedModels.modelsList.deleteModelActionLabel', { - defaultMessage: 'Delete model', - }); + + if (model.state === MODEL_STATE.DOWNLOADING) { + return i18n.translate('xpack.ml.trainedModels.modelsList.cancelDownloadActionLabel', { + defaultMessage: 'Cancel download', + }); + } else if (hasInferenceServices) { + return i18n.translate( + 'xpack.ml.trainedModels.modelsList.deleteDisabledWithInferenceServicesTooltip', + { + defaultMessage: 'Model is used by the _inference API', + } + ); + } else if (hasDeployments) { + return i18n.translate( + 'xpack.ml.trainedModels.modelsList.deleteDisabledWithDeploymentsTooltip', + { + defaultMessage: 'Model has started deployments', + } + ); + } else { + return i18n.translate('xpack.ml.trainedModels.modelsList.deleteModelActionLabel', { + defaultMessage: 'Delete model', + }); + } }, 'data-test-subj': 'mlModelsTableRowDeleteAction', icon: 'trash', - type: 'icon', + // @ts-ignore + type: isMobileLayout ? 'icon' : 'button', color: 'danger', isPrimary: false, onClick: (model) => { @@ -539,9 +546,10 @@ export function useModelActions({ }), 'data-test-subj': 'mlModelsTableRowTestAction', icon: 'inputOutput', - type: 'icon', + // @ts-ignore + type: isMobileLayout ? 'icon' : 'button', isPrimary: true, - available: isTestable, + available: (item) => isTestable(item, true), onClick: (item) => { if (isDfaTrainedModel(item) && !isBuiltInModel(item)) { onDfaTestAction(item); @@ -550,7 +558,7 @@ export function useModelActions({ } }, enabled: (item) => { - return canTestTrainedModels && isTestable(item, true) && !isLoading; + return canTestTrainedModels && !isLoading; }, }, { @@ -612,6 +620,7 @@ export function useModelActions({ trainedModelsApiService, urlLocator, onModelDownloadRequest, + isMobileLayout, ] ); } 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 3aef951f1545e..f218030c65ad3 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 @@ -5,9 +5,6 @@ * 2.0. */ -import type { FC } from 'react'; -import { useRef } from 'react'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; import type { SearchFilterConfig } from '@elastic/eui'; import { EuiBadge, @@ -16,52 +13,43 @@ import { EuiCallOut, EuiFlexGroup, EuiFlexItem, - EuiHealth, EuiIcon, EuiInMemoryTable, EuiLink, EuiProgress, EuiSpacer, EuiSwitch, + EuiText, EuiTitle, EuiToolTip, type EuiSearchBarProps, } from '@elastic/eui'; -import { groupBy, isEmpty } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; import type { EuiBasicTableColumn } from '@elastic/eui/src/components/basic_table/basic_table'; import type { EuiTableSelectionType } from '@elastic/eui/src/components/basic_table/table_types'; -import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; -import { isPopulatedObject } from '@kbn/ml-is-populated-object'; -import { usePageUrlState } from '@kbn/ml-url-state'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; import { useTimefilter } from '@kbn/ml-date-picker'; -import type { DeploymentState } from '@kbn/ml-trained-models-utils'; +import { isDefined } from '@kbn/ml-is-defined'; +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { useStorage } from '@kbn/ml-local-storage'; import { BUILT_IN_MODEL_TAG, BUILT_IN_MODEL_TYPE, - DEPLOYMENT_STATE, - ELASTIC_MODEL_DEFINITIONS, ELASTIC_MODEL_TAG, ELASTIC_MODEL_TYPE, ELSER_ID_V1, MODEL_STATE, type ModelState, } from '@kbn/ml-trained-models-utils'; -import { isDefined } from '@kbn/ml-is-defined'; -import { useStorage } from '@kbn/ml-local-storage'; +import type { ListingPageUrlState } from '@kbn/ml-url-state'; +import { usePageUrlState } from '@kbn/ml-url-state'; import { dynamic } from '@kbn/shared-ux-utility'; +import { cloneDeep, groupBy, isEmpty, memoize } from 'lodash'; +import type { FC } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import useMountedState from 'react-use/lib/useMountedState'; -import type { ListingPageUrlState } from '@kbn/ml-url-state'; -import { getModelStateColor, getModelDeploymentState } from './get_model_state'; +import { ML_PAGES } from '../../../common/constants/locator'; import { ML_ELSER_CALLOUT_DISMISSED } from '../../../common/types/storage'; -import { TechnicalPreviewBadge } from '../components/technical_preview_badge'; -import { useModelActions } from './model_actions'; -import { ModelsTableToConfigMapping } from './config_mapping'; -import type { ModelsBarStats } from '../components/stats_bar'; -import { StatsBar } from '../components/stats_bar'; -import { useMlKibana } from '../contexts/kibana'; -import { useTrainedModelsApiService } from '../services/ml_api_service/trained_models'; import type { ModelDownloadState, ModelPipelines, @@ -69,17 +57,23 @@ import type { TrainedModelDeploymentStatsResponse, TrainedModelStat, } from '../../../common/types/trained_models'; -import { DeleteModelsModal } from './delete_models_modal'; -import { ML_PAGES } from '../../../common/constants/locator'; +import { AddInferencePipelineFlyout } from '../components/ml_inference'; +import { SavedObjectsWarning } from '../components/saved_objects_warning'; +import type { ModelsBarStats } from '../components/stats_bar'; +import { StatsBar } from '../components/stats_bar'; +import { TechnicalPreviewBadge } from '../components/technical_preview_badge'; +import { useMlKibana } from '../contexts/kibana'; +import { useEnabledFeatures } from '../contexts/ml'; import { useTableSettings } from '../data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings'; -import { useToastNotificationService } from '../services/toast_notification_service'; -import { useFieldFormatter } from '../contexts/kibana/use_field_formatter'; import { useRefresh } from '../routing/use_refresh'; -import { SavedObjectsWarning } from '../components/saved_objects_warning'; -import { TestModelAndPipelineCreationFlyout } from './test_models'; +import { useTrainedModelsApiService } from '../services/ml_api_service/trained_models'; +import { useToastNotificationService } from '../services/toast_notification_service'; +import { ModelsTableToConfigMapping } from './config_mapping'; +import { DeleteModelsModal } from './delete_models_modal'; +import { getModelDeploymentState, getModelStateColor } from './get_model_state'; +import { useModelActions } from './model_actions'; import { TestDfaModelsFlyout } from './test_dfa_models_flyout'; -import { AddInferencePipelineFlyout } from '../components/ml_inference'; -import { useEnabledFeatures } from '../contexts/ml'; +import { TestModelAndPipelineCreationFlyout } from './test_models'; type Stats = Omit; @@ -106,6 +100,7 @@ export type ModelItem = TrainedModelConfigResponse & { softwareLicense?: string; licenseUrl?: string; downloadState?: ModelDownloadState; + disclaimer?: string; }; export type ModelItemFull = Required; @@ -165,8 +160,6 @@ export const ModelsList: FC = ({ useTimefilter({ timeRangeSelector: false, autoRefreshSelector: true }); - const dateFormatter = useFieldFormatter(FIELD_FORMAT_IDS.DATE); - // allow for an internally controlled page state which stores the state in the URL // or an external page state, which is passed in as a prop. // external page state is used on the management page. @@ -188,7 +181,7 @@ export const ModelsList: FC = ({ const trainedModelsApiService = useTrainedModelsApiService(); - const { displayErrorToast, displaySuccessToast } = useToastNotificationService(); + const { displayErrorToast } = useToastNotificationService(); const [isInitialized, setIsInitialized] = useState(false); const [isLoading, setIsLoading] = useState(false); @@ -219,28 +212,9 @@ export const ModelsList: FC = ({ }, [items]); /** - * Checks if the model download complete. + * Fetch of model definitions available for download needs to happen only once */ - const isDownloadComplete = useCallback( - async (modelId: string): Promise => { - try { - const response = await trainedModelsApiService.getTrainedModels(modelId, { - include: 'definition_status', - }); - // @ts-ignore - return !!response[0]?.fully_defined; - } catch (error) { - displayErrorToast( - error, - i18n.translate('xpack.ml.trainedModels.modelsList.downloadStatusCheckErrorMessage', { - defaultMessage: 'Failed to check download status', - }) - ); - } - return false; - }, - [trainedModelsApiService, displayErrorToast] - ); + const getTrainedModelDownloads = memoize(trainedModelsApiService.getTrainedModelDownloads); /** * Fetches trained models. @@ -288,15 +262,20 @@ export const ModelsList: FC = ({ const idMap = new Map( resultItems.map((model) => [model.model_id, model]) ); - const forDownload = await trainedModelsApiService.getTrainedModelDownloads(); + /** + * Fetches model definitions available for download + */ + const forDownload = await getTrainedModelDownloads(); + const notDownloaded: ModelItem[] = forDownload - .filter(({ model_id: modelId, hidden, recommended, supported }) => { + .filter(({ model_id: modelId, hidden, recommended, supported, disclaimer }) => { if (idMap.has(modelId)) { const model = idMap.get(modelId)!; if (recommended) { model.recommended = true; } model.supported = supported; + model.disclaimer = disclaimer; } return !idMap.has(modelId) && !hidden; }) @@ -315,23 +294,28 @@ export const ModelsList: FC = ({ softwareLicense: modelDefinition.license, licenseUrl: modelDefinition.licenseUrl, supported: modelDefinition.supported, + disclaimer: modelDefinition.disclaimer, } as ModelItem; }); resultItems = [...resultItems, ...notDownloaded]; } - setItems(resultItems); - - if (expandedItemsToRefresh.length > 0) { - await fetchModelsStats(expandedItemsToRefresh); - - setItemIdToExpandedRowMap( - expandedItemsToRefresh.reduce((acc, item) => { - acc[item.model_id] = ; - return acc; - }, {} as Record) - ); - } + setItems((prevItems) => { + // Need to merge existing items with new items + // to preserve state and download status + return resultItems.map((item) => { + const prevItem = prevItems.find((i) => i.model_id === item.model_id); + return { + ...item, + ...(prevItem?.state === MODEL_STATE.DOWNLOADING + ? { + state: prevItem.state, + downloadState: prevItem.downloadState, + } + : {}), + }; + }); + }); } catch (error) { displayErrorToast( error, @@ -340,7 +324,9 @@ export const ModelsList: FC = ({ }) ); } + setIsInitialized(true); + setIsLoading(false); await fetchDownloadStatus(); @@ -399,20 +385,6 @@ export const ModelsList: FC = ({ return c.reason ?? ''; }, ''); }); - - const elasticModels = models.filter((model) => - Object.hasOwn(ELASTIC_MODEL_DEFINITIONS, model.model_id) - ); - if (elasticModels.length > 0) { - for (const model of elasticModels) { - if (Object.values(DEPLOYMENT_STATE).includes(model.state as DeploymentState)) { - // no need to check for the download status if the model has been deployed - continue; - } - const isDownloaded = await isDownloadComplete(model.model_id); - model.state = isDownloaded ? MODEL_STATE.DOWNLOADED : MODEL_STATE.DOWNLOADING; - } - } } return true; @@ -429,6 +401,8 @@ export const ModelsList: FC = ({ }, []); const downLoadStatusFetchInProgress = useRef(false); + const abortedDownload = useRef(new Set()); + /** * Updates model list with download status */ @@ -448,47 +422,43 @@ export const ModelsList: FC = ({ if (isMounted()) { setItems((prevItems) => { return prevItems.map((item) => { - const newItem = { ...item }; + if (!item.type?.includes('pytorch')) { + return item; + } + const newItem = cloneDeep(item); + if (downloadStatus[item.model_id]) { + newItem.state = MODEL_STATE.DOWNLOADING; newItem.downloadState = downloadStatus[item.model_id]; } else { - if (downloadInProgress.has(item.model_id)) { + /* Unfortunately, model download status does not report 100% download state, only from 1 to 99. Hence, there might be 3 cases + * 1. Model is not downloaded at all + * 2. Model download was in progress and finished + * 3. Model download was in progress and aborted + */ + delete newItem.downloadState; + + if (abortedDownload.current.has(item.model_id)) { + // Change downloading state to not downloaded + newItem.state = MODEL_STATE.NOT_DOWNLOADED; + abortedDownload.current.delete(item.model_id); + } else if (downloadInProgress.has(item.model_id) || !newItem.state) { // Change downloading state to downloaded - delete newItem.downloadState; newItem.state = MODEL_STATE.DOWNLOADED; } + + downloadInProgress.delete(item.model_id); } return newItem; }); }); } - const downloadedModelIds = Array.from(downloadInProgress).filter( - (v) => !downloadStatus[v] - ); - - if (downloadedModelIds.length > 0) { - // Show success toast - displaySuccessToast( - i18n.translate('xpack.ml.trainedModels.modelsList.downloadCompleteSuccess', { - defaultMessage: - '"{modelIds}" {modelIdsLength, plural, one {has} other {have}} been downloaded successfully.', - values: { - modelIds: downloadedModelIds.join(', '), - modelIdsLength: downloadedModelIds.length, - }, - }) - ); - } - Object.keys(downloadStatus).forEach((modelId) => { if (downloadStatus[modelId]) { downloadInProgress.add(modelId); } }); - downloadedModelIds.forEach((v) => { - downloadInProgress.delete(v); - }); if (isEmpty(downloadStatus)) { downLoadStatusFetchInProgress.current = false; @@ -501,7 +471,7 @@ export const ModelsList: FC = ({ downLoadStatusFetchInProgress.current = false; } }, - [trainedModelsApiService, displaySuccessToast, isMounted] + [trainedModelsApiService, isMounted] ); /** @@ -575,7 +545,6 @@ export const ModelsList: FC = ({ if (itemIdToExpandedRowMapValues[item.model_id]) { delete itemIdToExpandedRowMapValues[item.model_id]; } else { - await fetchModelsStats([item]); itemIdToExpandedRowMapValues[item.model_id] = ; } setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues); @@ -583,9 +552,8 @@ export const ModelsList: FC = ({ const columns: Array> = [ { - align: 'left', - width: '32px', isExpander: true, + align: 'center', render: (item: ModelItem) => { if (!item.stats) { return null; @@ -610,35 +578,25 @@ export const ModelsList: FC = ({ }, { name: modelIdColumnName, - width: '15%', sortable: ({ model_id: modelId }: ModelItem) => modelId, truncateText: false, textOnly: false, 'data-test-subj': 'mlModelsTableColumnId', - render: ({ description, model_id: modelId }: ModelItem) => { + render: ({ + description, + model_id: modelId, + recommended, + supported, + type, + disclaimer, + }: ModelItem) => { const isTechPreview = description?.includes('(Tech Preview)'); - return ( - - {modelId} - {isTechPreview ? ( - - - - ) : null} - - ); - }, - }, - { - name: i18n.translate('xpack.ml.trainedModels.modelsList.modelDescriptionHeader', { - defaultMessage: 'Description', - }), - truncateText: false, - 'data-test-subj': 'mlModelsTableColumnDescription', - render: ({ description, recommended, tags, supported }: ModelItem) => { - if (!description) return null; - const descriptionText = description.replace('(Tech Preview)', ''); + let descriptionText = description?.replace('(Tech Preview)', ''); + + if (disclaimer) { + descriptionText += '. ' + disclaimer; + } const tooltipContent = supported === false ? ( @@ -653,80 +611,98 @@ export const ModelsList: FC = ({ /> ) : null; - return tooltipContent ? ( - - <> - {descriptionText}  - - - - ) : ( - descriptionText + return ( + + + + {modelId} + + {isTechPreview ? ( + + + + ) : null} + + + {descriptionText ? ( + + {descriptionText} + {tooltipContent ? ( + <> +   + + + + + ) : null} + + ) : null} + + {Array.isArray(type) && type.length > 0 ? ( + + {type.map((t) => ( + + + + {t} + + + + ))} + + ) : null} + ); }, }, { - width: '15%', - field: ModelsTableToConfigMapping.type, - name: i18n.translate('xpack.ml.trainedModels.modelsList.typeHeader', { - defaultMessage: 'Type', - }), - sortable: true, - truncateText: true, - align: 'left', - render: (types: string[]) => ( - - {types.map((type) => ( - - - {type} - - - ))} - - ), - 'data-test-subj': 'mlModelsTableColumnType', - }, - { - width: '10%', name: i18n.translate('xpack.ml.trainedModels.modelsList.stateHeader', { defaultMessage: 'State', }), - align: 'left', truncateText: false, + width: '150px', render: ({ state, downloadState }: ModelItem) => { const config = getModelStateColor(state); if (!config) return null; - const isDownloadInProgress = state === MODEL_STATE.DOWNLOADING && downloadState; + const isProgressbarVisible = state === MODEL_STATE.DOWNLOADING && downloadState; const label = ( - + {config.name} - + ); return ( - {isDownloadInProgress ? ( + {isProgressbarVisible ? ( - {((downloadState.downloaded_parts / downloadState.total_parts) * 100).toFixed( - 0 - ) + '%'} + {downloadState + ? ( + (downloadState.downloaded_parts / downloadState.total_parts) * + 100 + ).toFixed(0) + '%' + : '100%'} } - value={downloadState?.downloaded_parts} - max={downloadState?.total_parts} + value={downloadState?.downloaded_parts ?? 1} + max={downloadState?.total_parts ?? 1} size="xs" color={config.color} /> ) : ( - {label} + + {config.component ?? label} + )} ); @@ -734,21 +710,10 @@ export const ModelsList: FC = ({ 'data-test-subj': 'mlModelsTableColumnDeploymentState', }, { - width: '20%', - field: ModelsTableToConfigMapping.createdAt, - name: i18n.translate('xpack.ml.trainedModels.modelsList.createdAtHeader', { - defaultMessage: 'Created at', - }), - dataType: 'date', - render: (v: number) => dateFormatter(v), - sortable: true, - 'data-test-subj': 'mlModelsTableColumnCreatedAt', - }, - { - width: '15%', name: i18n.translate('xpack.ml.trainedModels.modelsList.actionsHeader', { defaultMessage: 'Actions', }), + width: '200px', actions, 'data-test-subj': 'mlModelsTableColumnActions', }, @@ -836,7 +801,8 @@ export const ModelsList: FC = ({ const { onTableChange, pagination, sorting } = useTableSettings( items.length, pageState, - updatePageState + updatePageState, + true ); const search: EuiSearchBarProps = { @@ -921,6 +887,7 @@ export const ModelsList: FC = ({
+ tableLayout={'auto'} responsiveBreakpoint={'xl'} allowNeutralSort={false} columns={columns} @@ -974,7 +941,14 @@ export const ModelsList: FC = ({ {modelsToDelete.length > 0 && ( { + modelsToDelete.forEach((model) => { + if (model.state === MODEL_STATE.DOWNLOADING) { + abortedDownload.current.add(model.model_id); + } + }); + setModelsToDelete([]); + if (refreshList) { fetchModelsData(); } diff --git a/x-pack/plugins/ml/server/models/model_management/model_provider.test.ts b/x-pack/plugins/ml/server/models/model_management/model_provider.test.ts index 33530bade5fcf..0b9b93720234d 100644 --- a/x-pack/plugins/ml/server/models/model_management/model_provider.test.ts +++ b/x-pack/plugins/ml/server/models/model_management/model_provider.test.ts @@ -89,6 +89,8 @@ describe('modelsProvider', () => { { config: { input: { field_names: ['text_field'] } }, description: 'E5 (EmbEddings from bidirEctional Encoder rEpresentations)', + disclaimer: + 'This E5 model, as defined, hosted, integrated and used in conjunction with our other Elastic Software is covered by our standard warranty.', model_id: '.multilingual-e5-small', default: true, supported: true, @@ -103,6 +105,8 @@ describe('modelsProvider', () => { config: { input: { field_names: ['text_field'] } }, description: 'E5 (EmbEddings from bidirEctional Encoder rEpresentations), optimized for linux-x86_64', + disclaimer: + 'This E5 model, as defined, hosted, integrated and used in conjunction with our other Elastic Software is covered by our standard warranty.', model_id: '.multilingual-e5-small_linux-x86_64', os: 'Linux', recommended: true, @@ -175,6 +179,8 @@ describe('modelsProvider', () => { { config: { input: { field_names: ['text_field'] } }, description: 'E5 (EmbEddings from bidirEctional Encoder rEpresentations)', + disclaimer: + 'This E5 model, as defined, hosted, integrated and used in conjunction with our other Elastic Software is covered by our standard warranty.', model_id: '.multilingual-e5-small', recommended: true, supported: true, @@ -189,6 +195,8 @@ describe('modelsProvider', () => { config: { input: { field_names: ['text_field'] } }, description: 'E5 (EmbEddings from bidirEctional Encoder rEpresentations), optimized for linux-x86_64', + disclaimer: + 'This E5 model, as defined, hosted, integrated and used in conjunction with our other Elastic Software is covered by our standard warranty.', model_id: '.multilingual-e5-small_linux-x86_64', os: 'Linux', supported: false, diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index e47a52ec5df58..0ee9a547fc7f1 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -30070,7 +30070,6 @@ "xpack.ml.trainedModels.modelsList.builtInModelLabel": "intégré", "xpack.ml.trainedModels.modelsList.builtInModelMessage": "Modèle intégré", "xpack.ml.trainedModels.modelsList.collapseRow": "Réduire", - "xpack.ml.trainedModels.modelsList.createdAtHeader": "Créé à", "xpack.ml.trainedModels.modelsList.deleteDisabledWithDeploymentsTooltip": "Le modèle a commencé à être déployé", "xpack.ml.trainedModels.modelsList.deleteDisabledWithInferenceServicesTooltip": "Le modèle est utilisé par l'API _inference", "xpack.ml.trainedModels.modelsList.deleteModal.approvePipelinesDeletionLabel": "Supprimer {pipelinesCount, plural, one {le pipeline} other {les pipelines}}", @@ -30085,9 +30084,7 @@ "xpack.ml.trainedModels.modelsList.deleteModelsButtonLabel": "Supprimer", "xpack.ml.trainedModels.modelsList.deployModelActionLabel": "Déployer le modèle", "xpack.ml.trainedModels.modelsList.disableSelectableMessage": "Le modèle a des pipelines associés", - "xpack.ml.trainedModels.modelsList.downloadCompleteSuccess": "\"{modelIds}\" {modelIdsLength, plural, one {a été téléchargé} other {ont été téléchargés}} avec succès.", "xpack.ml.trainedModels.modelsList.downloadFailed": "Échec du téléchargement de \"{modelId}\"", - "xpack.ml.trainedModels.modelsList.downloadStatusCheckErrorMessage": "Échec de la vérification du statut du téléchargement", "xpack.ml.trainedModels.modelsList.e5Title": "E5 (EmbEddings from bidirEctional Encoder rEpresentations)", "xpack.ml.trainedModels.modelsList.e5v1Description": "E5 (EmbEddings from bidirEctional Encoder rEpresentations)", "xpack.ml.trainedModels.modelsList.e5v1x86Description": "E5 (EmbEddings from bidirEctional Encoder rEpresentations), optimisé for linux-x86_64", @@ -30125,11 +30122,9 @@ "xpack.ml.trainedModels.modelsList.forceStopDialog.selectDeploymentsLegend": "Sélectionner les déploiements à arrêter", "xpack.ml.trainedModels.modelsList.forceStopDialog.title": "Arrêter {deploymentCount, plural, one {le déploiement} other {les déploiements}} du modèle {modelId} ?", "xpack.ml.trainedModels.modelsList.mitLicenseLabel": "Licence : MIT", - "xpack.ml.trainedModels.modelsList.modelDescriptionHeader": "Description", "xpack.ml.trainedModels.modelsList.modelIdHeader": "ID", "xpack.ml.trainedModels.modelsList.modelState.downloadedName": "Paré au déploiement", "xpack.ml.trainedModels.modelsList.modelState.downloadingName": "Téléchargement...", - "xpack.ml.trainedModels.modelsList.modelState.notDownloadedName": "Non téléchargé", "xpack.ml.trainedModels.modelsList.modelState.startedName": "Déployé", "xpack.ml.trainedModels.modelsList.modelState.startingName": "Déploiement lancé...", "xpack.ml.trainedModels.modelsList.modelState.stoppingName": "Déploiement en phase d'arrêt...", @@ -30156,14 +30151,10 @@ "xpack.ml.trainedModels.modelsList.startDeployment.updateButton": "Mettre à jour", "xpack.ml.trainedModels.modelsList.startDeployment.viewElserDocLink": "Afficher la documentation", "xpack.ml.trainedModels.modelsList.startFailed": "Impossible de démarrer \"{modelId}\"", - "xpack.ml.trainedModels.modelsList.startSuccess": "Le déploiement pour \"{modelId}\" a bien été démarré.", "xpack.ml.trainedModels.modelsList.stateHeader": "État", "xpack.ml.trainedModels.modelsList.stopDeploymentWarning": "Impossible d'arrêter \"{deploymentId}\"", "xpack.ml.trainedModels.modelsList.stopFailed": "Impossible d'arrêter \"{modelId}\"", - "xpack.ml.trainedModels.modelsList.stopSuccess": "{numberOfDeployments, plural, one {Le déploiement} other {Les déploiements}} pour \"{modelId}\" {numberOfDeployments, plural, one {a bien été arrêté} other {ont bien été arrêtés}}.", - "xpack.ml.trainedModels.modelsList.successfullyDeletedMessage": "{modelsCount, plural, one {Le modèle {modelIds}} other {# modèles}} {modelsCount, plural, one {a bien été supprimé} other {ont bien été supprimés}}.", "xpack.ml.trainedModels.modelsList.totalAmountLabel": "Total de modèles entraînés", - "xpack.ml.trainedModels.modelsList.typeHeader": "Type", "xpack.ml.trainedModels.modelsList.updateDeployment.modalTitle": "Mettre à jour le déploiement {modelId}", "xpack.ml.trainedModels.modelsList.updateFailed": "Impossible de mettre à jour \"{modelId}\"", "xpack.ml.trainedModels.modelsList.updateSuccess": "Le déploiement pour \"{modelId}\" a bien été mis à jour.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 8e2125deaf18c..d7a24239bfec4 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -29817,7 +29817,6 @@ "xpack.ml.trainedModels.modelsList.builtInModelLabel": "ビルトイン", "xpack.ml.trainedModels.modelsList.builtInModelMessage": "ビルトインモデル", "xpack.ml.trainedModels.modelsList.collapseRow": "縮小", - "xpack.ml.trainedModels.modelsList.createdAtHeader": "作成日時:", "xpack.ml.trainedModels.modelsList.deleteDisabledWithDeploymentsTooltip": "モデルはデプロイを開始しました", "xpack.ml.trainedModels.modelsList.deleteDisabledWithInferenceServicesTooltip": "モデルは_inference APIによって使用されます。", "xpack.ml.trainedModels.modelsList.deleteModal.approvePipelinesDeletionLabel": "{pipelinesCount, plural, other {パイプライン}}を削除", @@ -29832,9 +29831,7 @@ "xpack.ml.trainedModels.modelsList.deleteModelsButtonLabel": "削除", "xpack.ml.trainedModels.modelsList.deployModelActionLabel": "モデルをデプロイ", "xpack.ml.trainedModels.modelsList.disableSelectableMessage": "モデルにはパイプラインが関連付けられています", - "xpack.ml.trainedModels.modelsList.downloadCompleteSuccess": "\"{modelIds}\" {modelIdsLength, plural, other {が}}正常にダウンロードされました。", "xpack.ml.trainedModels.modelsList.downloadFailed": "\"{modelId}\"をダウンロードできませんでした", - "xpack.ml.trainedModels.modelsList.downloadStatusCheckErrorMessage": "ダウンロードステータスを確認できませんでした", "xpack.ml.trainedModels.modelsList.e5Title": "E5(bidirEctional Encoder rEpresentationsからのEmbEddings)", "xpack.ml.trainedModels.modelsList.e5v1Description": "E5(bidirEctional Encoder rEpresentationsからのEmbEddings)", "xpack.ml.trainedModels.modelsList.e5v1x86Description": "E5(bidirEctional Encoder rEpresentationsからのEmbEddings)、inux-x86_64向けに最適化", @@ -29872,11 +29869,9 @@ "xpack.ml.trainedModels.modelsList.forceStopDialog.selectDeploymentsLegend": "停止するデプロイを選択", "xpack.ml.trainedModels.modelsList.forceStopDialog.title": "モデル{modelId}の{deploymentCount, plural, other {デプロイ}}を停止しますか?", "xpack.ml.trainedModels.modelsList.mitLicenseLabel": "ライセンス:MIT", - "xpack.ml.trainedModels.modelsList.modelDescriptionHeader": "説明", "xpack.ml.trainedModels.modelsList.modelIdHeader": "ID", "xpack.ml.trainedModels.modelsList.modelState.downloadedName": "デプロイできます", "xpack.ml.trainedModels.modelsList.modelState.downloadingName": "ダウンロード中...", - "xpack.ml.trainedModels.modelsList.modelState.notDownloadedName": "未ダウンロード", "xpack.ml.trainedModels.modelsList.modelState.startedName": "デプロイ済み", "xpack.ml.trainedModels.modelsList.modelState.startingName": "デプロイを開始中...", "xpack.ml.trainedModels.modelsList.modelState.stoppingName": "デプロイを停止中...", @@ -29903,13 +29898,10 @@ "xpack.ml.trainedModels.modelsList.startDeployment.updateButton": "更新", "xpack.ml.trainedModels.modelsList.startDeployment.viewElserDocLink": "ドキュメンテーションを表示", "xpack.ml.trainedModels.modelsList.startFailed": "\"{modelId}\"の開始に失敗しました", - "xpack.ml.trainedModels.modelsList.startSuccess": "\"{modelId}\"のデプロイが正常に開始しました。", "xpack.ml.trainedModels.modelsList.stateHeader": "ステータス", "xpack.ml.trainedModels.modelsList.stopDeploymentWarning": "\"{deploymentId}\"を停止できませんでした", "xpack.ml.trainedModels.modelsList.stopFailed": "\"{modelId}\"の停止に失敗しました", - "xpack.ml.trainedModels.modelsList.stopSuccess": "\"{modelId}\"の{numberOfDeployments, plural, other {デプロイ}}が正常に停止しました。", "xpack.ml.trainedModels.modelsList.totalAmountLabel": "学習済みモデルの合計数", - "xpack.ml.trainedModels.modelsList.typeHeader": "型", "xpack.ml.trainedModels.modelsList.updateDeployment.modalTitle": "{modelId}デプロイを更新", "xpack.ml.trainedModels.modelsList.updateFailed": "\"{modelId}\"を更新できませんでした", "xpack.ml.trainedModels.modelsList.updateSuccess": "\"{modelId}\"のデプロイが正常に更新されました。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 3b7a338fab717..8ad1766adf0f6 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -29857,7 +29857,6 @@ "xpack.ml.trainedModels.modelsList.builtInModelLabel": "内置", "xpack.ml.trainedModels.modelsList.builtInModelMessage": "内置模型", "xpack.ml.trainedModels.modelsList.collapseRow": "折叠", - "xpack.ml.trainedModels.modelsList.createdAtHeader": "创建于", "xpack.ml.trainedModels.modelsList.deleteDisabledWithDeploymentsTooltip": "模型已开始部署", "xpack.ml.trainedModels.modelsList.deleteDisabledWithInferenceServicesTooltip": "模型由 _inference API 使用", "xpack.ml.trainedModels.modelsList.deleteModal.approvePipelinesDeletionLabel": "删除{pipelinesCount, plural, other {管道}}", @@ -29872,9 +29871,7 @@ "xpack.ml.trainedModels.modelsList.deleteModelsButtonLabel": "删除", "xpack.ml.trainedModels.modelsList.deployModelActionLabel": "部署模型", "xpack.ml.trainedModels.modelsList.disableSelectableMessage": "模型有关联的管道", - "xpack.ml.trainedModels.modelsList.downloadCompleteSuccess": "“{modelIds}”{modelIdsLength, plural, other {已}}成功下载。", "xpack.ml.trainedModels.modelsList.downloadFailed": "无法下载“{modelId}”", - "xpack.ml.trainedModels.modelsList.downloadStatusCheckErrorMessage": "无法检查下载状态", "xpack.ml.trainedModels.modelsList.e5Title": "E5 (EmbEddings from bidirEctional Encoder rEpresentations)", "xpack.ml.trainedModels.modelsList.e5v1Description": "E5 (EmbEddings from bidirEctional Encoder rEpresentations)", "xpack.ml.trainedModels.modelsList.e5v1x86Description": "针对 linux-x86_64 进行了优化的 E5 (EmbEddings from bidirEctional Encoder rEpresentations)", @@ -29912,11 +29909,9 @@ "xpack.ml.trainedModels.modelsList.forceStopDialog.selectDeploymentsLegend": "选择要停止的部署", "xpack.ml.trainedModels.modelsList.forceStopDialog.title": "停止{deploymentCount, plural, other {部署}}模型 {modelId}?", "xpack.ml.trainedModels.modelsList.mitLicenseLabel": "许可证:MIT", - "xpack.ml.trainedModels.modelsList.modelDescriptionHeader": "描述", "xpack.ml.trainedModels.modelsList.modelIdHeader": "ID", "xpack.ml.trainedModels.modelsList.modelState.downloadedName": "准备部署", "xpack.ml.trainedModels.modelsList.modelState.downloadingName": "正在下载......", - "xpack.ml.trainedModels.modelsList.modelState.notDownloadedName": "未下载", "xpack.ml.trainedModels.modelsList.modelState.startedName": "已部署", "xpack.ml.trainedModels.modelsList.modelState.startingName": "开始部署......", "xpack.ml.trainedModels.modelsList.modelState.stoppingName": "停止部署......", @@ -29943,14 +29938,10 @@ "xpack.ml.trainedModels.modelsList.startDeployment.updateButton": "更新", "xpack.ml.trainedModels.modelsList.startDeployment.viewElserDocLink": "查看文档", "xpack.ml.trainedModels.modelsList.startFailed": "无法启动“{modelId}”", - "xpack.ml.trainedModels.modelsList.startSuccess": "已成功启动“{modelId}”的部署。", "xpack.ml.trainedModels.modelsList.stateHeader": "状态", "xpack.ml.trainedModels.modelsList.stopDeploymentWarning": "无法停止“{deploymentId}”", "xpack.ml.trainedModels.modelsList.stopFailed": "无法停止“{modelId}”", - "xpack.ml.trainedModels.modelsList.stopSuccess": "已成功停止“{modelId}”的{numberOfDeployments, plural, other {部署}}。", - "xpack.ml.trainedModels.modelsList.successfullyDeletedMessage": "{modelsCount, plural, one {模型 {modelIds}} other {# 个模型}}{modelsCount, plural, other {已}}成功删除", "xpack.ml.trainedModels.modelsList.totalAmountLabel": "已训练的模型总数", - "xpack.ml.trainedModels.modelsList.typeHeader": "类型", "xpack.ml.trainedModels.modelsList.updateDeployment.modalTitle": "更新 {modelId} 部署", "xpack.ml.trainedModels.modelsList.updateFailed": "无法更新“{modelId}”", "xpack.ml.trainedModels.modelsList.updateSuccess": "已成功更新“{modelId}”的部署。", diff --git a/x-pack/test/api_integration/apis/ml/trained_models/model_downloads.ts b/x-pack/test/api_integration/apis/ml/trained_models/model_downloads.ts index 4e229c133b4fd..4e5fd70314495 100644 --- a/x-pack/test/api_integration/apis/ml/trained_models/model_downloads.ts +++ b/x-pack/test/api_integration/apis/ml/trained_models/model_downloads.ts @@ -100,6 +100,8 @@ export default ({ getService }: FtrProviderContext) => { }, }, description: 'E5 (EmbEddings from bidirEctional Encoder rEpresentations)', + disclaimer: + 'This E5 model, as defined, hosted, integrated and used in conjunction with our other Elastic Software is covered by our standard warranty.', license: 'MIT', licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small', type: ['pytorch', 'text_embedding'], @@ -119,6 +121,8 @@ export default ({ getService }: FtrProviderContext) => { }, description: 'E5 (EmbEddings from bidirEctional Encoder rEpresentations), optimized for linux-x86_64', + disclaimer: + 'This E5 model, as defined, hosted, integrated and used in conjunction with our other Elastic Software is covered by our standard warranty.', license: 'MIT', licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small_linux-x86_64', type: ['pytorch', 'text_embedding'], diff --git a/x-pack/test/functional/services/ml/trained_models_table.ts b/x-pack/test/functional/services/ml/trained_models_table.ts index 990ca6c1ed37a..941308b61c328 100644 --- a/x-pack/test/functional/services/ml/trained_models_table.ts +++ b/x-pack/test/functional/services/ml/trained_models_table.ts @@ -52,17 +52,16 @@ export function TrainedModelsTableProvider( id: string; description: string; modelTypes: string[]; - createdAt: string; state: string; } = { id: $tr .findTestSubject('mlModelsTableColumnId') - .find('.euiTableCellContent') + .findTestSubject('mlModelsTableColumnIdValueId') .text() .trim(), description: $tr - .findTestSubject('mlModelsTableColumnDescription') - .find('.euiTableCellContent') + .findTestSubject('mlModelsTableColumnId') + .findTestSubject('mlModelsTableColumnIdValueDescription') .text() .trim(), modelTypes, @@ -71,11 +70,6 @@ export function TrainedModelsTableProvider( .find('.euiTableCellContent') .text() .trim(), - createdAt: $tr - .findTestSubject('mlModelsTableColumnCreatedAt') - .find('.euiTableCellContent') - .text() - .trim(), }; rows.push(rowObject); @@ -161,12 +155,6 @@ export function TrainedModelsTableProvider( expectedRow.modelTypes )}' (got '${JSON.stringify(modelRow.modelTypes)}')` ); - // 'Created at' will be different on each run, - // so we will just assert that the value is in the expected timestamp format. - expect(modelRow.createdAt).to.match( - /^\w{3}\s\d+,\s\d{4}\s@\s\d{2}:\d{2}:\d{2}\.\d{3}$/, - `Expected trained model row created at time to have same format as 'Dec 5, 2019 @ 12:28:34.594' (got '${modelRow.createdAt}')` - ); } public async assertTableIsPopulated() { From f4a4a681f58c1c64eb8a05070b44f0605c625458 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Wed, 9 Oct 2024 02:40:45 -0600 Subject: [PATCH 5/8] [ML] Anomaly Detection: adds ability to delete forecasts from job (#194896) ## Summary Related issues: - https://github.com/elastic/kibana/issues/18511 - https://github.com/elastic/kibana/issues/192301 In this PR, in Job management > expanded row > Forecasts tab - a delete action has been added to each row in the forecasts table. A confirmation modal allows the user to confirm the delete action. In the SMV view, the forecast being currently viewed is now highlighted in the Forecast modal to make it easier to identify. ![image](https://github.com/user-attachments/assets/87814889-d41d-4780-98ab-695c6f12a023) image image Dark mode: image ### 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 - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] 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) --------- Co-authored-by: Elastic Machine Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../plugins/ml/common/types/capabilities.ts | 2 + .../capabilities/check_capabilities.ts | 4 + .../forecasts_table/forecasts_table.js | 168 ++++++++++++++---- .../timeseriesexplorer/state_manager.tsx | 7 +- .../services/ml_api_service/index.ts | 12 ++ .../forecasting_modal/forecasting_modal.js | 17 +- .../forecasting_modal/forecasts_list.js | 44 +++-- .../components/forecasting_modal/modal.js | 7 +- .../timeseriesexplorer/timeseriesexplorer.js | 1 + .../timeseriesexplorer_embeddable_chart.js | 1 + .../capabilities/check_capabilities.test.ts | 8 +- .../ml/server/routes/anomaly_detectors.ts | 36 ++++ .../schemas/anomaly_detectors_schema.ts | 5 + .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../anomaly_detectors/forecast_with_spaces.ts | 32 +++- .../apis/ml/system/capabilities.ts | 4 +- .../apis/ml/system/space_capabilities.ts | 6 +- 19 files changed, 283 insertions(+), 74 deletions(-) diff --git a/x-pack/plugins/ml/common/types/capabilities.ts b/x-pack/plugins/ml/common/types/capabilities.ts index 7dc841a5cb4d7..e7a097fae7dc4 100644 --- a/x-pack/plugins/ml/common/types/capabilities.ts +++ b/x-pack/plugins/ml/common/types/capabilities.ts @@ -58,6 +58,7 @@ export const adminMlCapabilities = { canResetJob: false, canUpdateJob: false, canForecastJob: false, + canDeleteForecast: false, canCreateDatafeed: false, canDeleteDatafeed: false, canStartStopDatafeed: false, @@ -222,6 +223,7 @@ export const featureCapabilities: FeatureCapabilities = { 'canResetJob', 'canUpdateJob', 'canForecastJob', + 'canDeleteForecast', 'canCreateDatafeed', 'canDeleteDatafeed', 'canStartStopDatafeed', diff --git a/x-pack/plugins/ml/public/application/capabilities/check_capabilities.ts b/x-pack/plugins/ml/public/application/capabilities/check_capabilities.ts index ed930b857473d..734993a4e4a6e 100644 --- a/x-pack/plugins/ml/public/application/capabilities/check_capabilities.ts +++ b/x-pack/plugins/ml/public/application/capabilities/check_capabilities.ts @@ -269,6 +269,10 @@ export function createPermissionFailureMessage(privilegeType: keyof MlCapabiliti message = i18n.translate('xpack.ml.privilege.noPermission.runForecastsTooltip', { defaultMessage: 'You do not have permission to run forecasts.', }); + } else if (privilegeType === 'canDeleteForecast') { + message = i18n.translate('xpack.ml.privilege.noPermission.deleteForecastsTooltip', { + defaultMessage: 'You do not have permission to delete forecasts.', + }); } return i18n.translate('xpack.ml.privilege.pleaseContactAdministratorTooltip', { defaultMessage: '{message} Please contact your administrator.', diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/forecasts_table/forecasts_table.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/forecasts_table/forecasts_table.js index 5f1cbb1c76ca0..bfed613b9ad5d 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/forecasts_table/forecasts_table.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/forecasts_table/forecasts_table.js @@ -9,8 +9,8 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { - EuiButtonIcon, EuiCallOut, + EuiConfirmModal, EuiFlexGroup, EuiFlexItem, EuiInMemoryTable, @@ -32,9 +32,43 @@ import { isTimeSeriesViewJob, } from '../../../../../../../common/util/job_utils'; import { ML_APP_LOCATOR, ML_PAGES } from '../../../../../../../common/constants/locator'; +import { checkPermission } from '../../../../../capabilities/check_capabilities'; const MAX_FORECASTS = 500; +const DeleteForecastConfirm = ({ onCancel, onConfirm }) => ( + +

+ +

+
+); + /** * Table component for rendering the lists of forecasts run on an ML job. */ @@ -44,6 +78,7 @@ export class ForecastsTable extends Component { this.state = { isLoading: props.job.data_counts.processed_record_count !== 0, forecasts: [], + forecastIdToDelete: undefined, }; this.mlForecastService = forecastServiceFactory(constructorContext.services.mlServices.mlApi); } @@ -54,6 +89,11 @@ export class ForecastsTable extends Component { static contextType = context; componentDidMount() { + this.loadForecasts(); + this.canDeleteJobForecast = checkPermission('canDeleteForecast'); + } + + async loadForecasts() { const dataCounts = this.props.job.data_counts; if (dataCounts.processed_record_count > 0) { // Get the list of all the forecasts with results at or later than the specified 'from' time. @@ -163,6 +203,36 @@ export class ForecastsTable extends Component { await navigateToUrl(singleMetricViewerForecastLink); } + async deleteForecast(forecastId) { + const { + services: { + mlServices: { mlApi }, + }, + } = this.context; + + this.setState({ + isLoading: true, + forecastIdToDelete: undefined, + }); + + try { + await mlApi.deleteForecast({ jobId: this.props.job.job_id, forecastId }); + } catch (error) { + this.setState({ + forecastIdToDelete: undefined, + isLoading: false, + errorMessage: i18n.translate( + 'xpack.ml.jobsList.jobDetails.forecastsTable.deleteForecastErrorMessage', + { + defaultMessage: 'An error occurred when deleting the forecast.', + } + ), + }); + } + + this.loadForecasts(); + } + render() { if (this.state.isLoading === true) { return ( @@ -302,48 +372,74 @@ export class ForecastsTable extends Component { textOnly: true, }, { - name: i18n.translate('xpack.ml.jobsList.jobDetails.forecastsTable.viewLabel', { - defaultMessage: 'View', + width: '75px', + name: i18n.translate('xpack.ml.jobsList.jobDetails.forecastsTable.actionsLabel', { + defaultMessage: 'Actions', }), - width: '60px', - render: (forecast) => { - const viewForecastAriaLabel = i18n.translate( - 'xpack.ml.jobsList.jobDetails.forecastsTable.viewAriaLabel', - { - defaultMessage: 'View forecast created at {createdDate}', - values: { - createdDate: timeFormatter(forecast.forecast_create_timestamp), - }, - } - ); - - return ( - this.openSingleMetricView(forecast)} - isDisabled={ + actions: [ + { + description: i18n.translate('xpack.ml.jobsList.jobDetails.forecastsTable.viewLabel', { + defaultMessage: 'View', + }), + type: 'icon', + icon: 'eye', + enabled: (forecast) => + !( this.props.job.blocked !== undefined || forecast.forecast_status !== FORECAST_REQUEST_STATE.FINISHED - } - iconType="singleMetricViewer" - aria-label={viewForecastAriaLabel} - data-test-subj="mlJobListForecastTabOpenSingleMetricViewButton" - /> - ); - }, + ), + onClick: (forecast) => this.openSingleMetricView(forecast), + 'data-test-subj': 'mlJobListForecastTabOpenSingleMetricViewButton', + }, + ...(this.canDeleteJobForecast + ? [ + { + description: i18n.translate( + 'xpack.ml.jobsList.jobDetails.forecastsTable.deleteForecastDescription', + { + defaultMessage: 'Delete forecast', + } + ), + type: 'icon', + icon: 'trash', + color: 'danger', + enabled: () => this.state.isLoading === false, + onClick: (item) => { + this.setState({ + forecastIdToDelete: item.forecast_id, + }); + }, + 'data-test-subj': 'mlJobListForecastTabDeleteForecastButton', + }, + ] + : []), + ], }, ]; return ( - + <> + + {this.state.forecastIdToDelete !== undefined ? ( + + this.setState({ + forecastIdToDelete: undefined, + }) + } + onConfirm={() => this.deleteForecast(this.state.forecastIdToDelete)} + /> + ) : null} + ); } } diff --git a/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer/state_manager.tsx b/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer/state_manager.tsx index fa1753a4342fc..309f24dd1c62b 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer/state_manager.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer/state_manager.tsx @@ -216,6 +216,10 @@ export const TimeSeriesExplorerUrlStateManager: FC { + if (selectedForecastIdProp !== selectedForecastId) { + setSelectedForecastIdProp(undefined); + } + if ( autoZoomDuration !== undefined && boundsMinMs !== undefined && @@ -223,9 +227,6 @@ export const TimeSeriesExplorerUrlStateManager: FC { diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts index fa6d179059eec..868ca0d5baa0f 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts @@ -103,6 +103,10 @@ export interface GetModelSnapshotsResponse { model_snapshots: ModelSnapshot[]; } +export interface DeleteForecastResponse { + acknowledged: boolean; +} + export function mlApiProvider(httpService: HttpService) { return { getJobs(obj?: { jobId?: string }) { @@ -368,6 +372,14 @@ export function mlApiProvider(httpService: HttpService) { }); }, + deleteForecast({ jobId, forecastId }: { jobId: string; forecastId: string }) { + return httpService.http({ + path: `${ML_INTERNAL_BASE_PATH}/anomaly_detectors/${jobId}/_forecast/${forecastId}`, + method: 'DELETE', + version: '1', + }); + }, + overallBuckets({ jobId, topN, diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js index 5ac0dd68700d6..1bd47ff69ebc6 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js @@ -30,9 +30,13 @@ import { forecastServiceFactory } from '../../../services/forecast_service'; import { ForecastButton } from './forecast_button'; export const FORECAST_DURATION_MAX_DAYS = 3650; // Max forecast duration allowed by analytics. - -const FORECAST_JOB_MIN_VERSION = '6.1.0'; // Forecasting only allowed for jobs created >= 6.1.0. +const STATUS_FINISHED_QUERY = { + term: { + forecast_status: FORECAST_REQUEST_STATE.FINISHED, + }, +}; const FORECASTS_VIEW_MAX = 5; // Display links to a maximum of 5 forecasts. +const FORECAST_JOB_MIN_VERSION = '6.1.0'; // Forecasting only allowed for jobs created >= 6.1.0. const FORECAST_DURATION_MAX_MS = FORECAST_DURATION_MAX_DAYS * 86400000; const WARN_NUM_PARTITIONS = 100; // Warn about running a forecast with this number of field values. const FORECAST_STATS_POLL_FREQUENCY = 250; // Frequency in ms at which to poll for forecast request stats. @@ -64,6 +68,7 @@ export class ForecastingModal extends Component { latestRecordTimestamp: PropTypes.number, entities: PropTypes.array, setForecastId: PropTypes.func, + selectedForecastId: PropTypes.string, }; constructor(props) { @@ -405,13 +410,8 @@ export class ForecastingModal extends Component { // Get the list of all the finished forecasts for this job with results at or later than the dashboard 'from' time. const { timefilter } = this.context.services.data.query.timefilter; const bounds = timefilter.getActiveBounds(); - const statusFinishedQuery = { - term: { - forecast_status: FORECAST_REQUEST_STATE.FINISHED, - }, - }; this.mlForecastService - .getForecastsSummary(job, statusFinishedQuery, bounds.min.valueOf(), FORECASTS_VIEW_MAX) + .getForecastsSummary(job, STATUS_FINISHED_QUERY, bounds.min.valueOf(), FORECASTS_VIEW_MAX) .then((resp) => { this.setState({ previousForecasts: resp.forecasts, @@ -558,6 +558,7 @@ export class ForecastingModal extends Component { jobOpeningState={this.state.jobOpeningState} jobClosingState={this.state.jobClosingState} messages={this.state.messages} + selectedForecastId={this.props.selectedForecastId} /> )}
diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasts_list.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasts_list.js index 9e01f06094451..52ce2b201dd8d 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasts_list.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasts_list.js @@ -12,11 +12,12 @@ import PropTypes from 'prop-types'; import React from 'react'; -import { EuiButtonIcon, EuiIcon, EuiInMemoryTable, EuiText, EuiToolTip } from '@elastic/eui'; +import { EuiButtonIcon, EuiIconTip, EuiInMemoryTable, EuiText } from '@elastic/eui'; import { formatHumanReadableDateTimeSeconds } from '@kbn/ml-date-utils'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { useCurrentThemeVars } from '../../../contexts/kibana'; function getColumns(viewForecast) { return [ @@ -75,37 +76,41 @@ function getColumns(viewForecast) { ]; } -// TODO - add in ml-info-icon to the h3 element, -// then remove tooltip and inline style. -export function ForecastsList({ forecasts, viewForecast }) { +export function ForecastsList({ forecasts, viewForecast, selectedForecastId }) { + const { euiTheme } = useCurrentThemeVars(); + const getRowProps = (item) => { return { 'data-test-subj': `mlForecastsListRow row-${item.rowId}`, + ...(item.forecast_id === selectedForecastId + ? { + style: { + backgroundColor: `${euiTheme.euiPanelBackgroundColorModifiers.primary}`, + }, + } + : {}), }; }; return ( -

+

+   + + } + />

- - } - > - - 0 && ( - + )} @@ -104,4 +108,5 @@ Modal.propType = { jobOpeningState: PropTypes.number, jobClosingState: PropTypes.number, messages: PropTypes.array, + selectedForecastId: PropTypes.string, }; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js index 601bbf058868f..57ded98fc8374 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js @@ -1081,6 +1081,7 @@ export class TimeSeriesExplorer extends React.Component { latestRecordTimestamp={selectedJob.data_counts.latest_record_timestamp} setForecastId={this.setForecastId} className="forecast-controls" + selectedForecastId={this.props.selectedForecastId} /> diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_embeddable_chart/timeseriesexplorer_embeddable_chart.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_embeddable_chart/timeseriesexplorer_embeddable_chart.js index 90b0e76167517..48ef63c2eae37 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_embeddable_chart/timeseriesexplorer_embeddable_chart.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_embeddable_chart/timeseriesexplorer_embeddable_chart.js @@ -1051,6 +1051,7 @@ export class TimeSeriesExplorerEmbeddableChart extends React.Component { setForecastId={this.setForecastId} className="forecast-controls" onForecastComplete={onForecastComplete} + selectedForecastId={this.props.selectedForecastId} /> )} diff --git a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts index d19ff7f723d0a..e82371c358152 100644 --- a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts +++ b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts @@ -47,7 +47,7 @@ describe('check_capabilities', () => { ); const { capabilities } = await getCapabilities(); const count = Object.keys(capabilities).length; - expect(count).toBe(43); + expect(count).toBe(44); }); }); @@ -86,6 +86,7 @@ describe('check_capabilities', () => { expect(capabilities.canCloseJob).toBe(false); expect(capabilities.canResetJob).toBe(false); expect(capabilities.canForecastJob).toBe(false); + expect(capabilities.canDeleteForecast).toBe(false); expect(capabilities.canStartStopDatafeed).toBe(false); expect(capabilities.canUpdateJob).toBe(false); expect(capabilities.canCreateDatafeed).toBe(false); @@ -146,6 +147,7 @@ describe('check_capabilities', () => { expect(capabilities.canCloseJob).toBe(true); expect(capabilities.canResetJob).toBe(true); expect(capabilities.canForecastJob).toBe(true); + expect(capabilities.canDeleteForecast).toBe(true); expect(capabilities.canStartStopDatafeed).toBe(true); expect(capabilities.canUpdateJob).toBe(true); expect(capabilities.canCreateDatafeed).toBe(true); @@ -206,6 +208,7 @@ describe('check_capabilities', () => { expect(capabilities.canCloseJob).toBe(false); expect(capabilities.canResetJob).toBe(false); expect(capabilities.canForecastJob).toBe(false); + expect(capabilities.canDeleteForecast).toBe(false); expect(capabilities.canStartStopDatafeed).toBe(false); expect(capabilities.canUpdateJob).toBe(false); expect(capabilities.canCreateDatafeed).toBe(false); @@ -266,6 +269,7 @@ describe('check_capabilities', () => { expect(capabilities.canCloseJob).toBe(false); expect(capabilities.canResetJob).toBe(false); expect(capabilities.canForecastJob).toBe(false); + expect(capabilities.canDeleteForecast).toBe(false); expect(capabilities.canStartStopDatafeed).toBe(false); expect(capabilities.canUpdateJob).toBe(false); expect(capabilities.canCreateDatafeed).toBe(false); @@ -326,6 +330,7 @@ describe('check_capabilities', () => { expect(capabilities.canCloseJob).toBe(false); expect(capabilities.canResetJob).toBe(false); expect(capabilities.canForecastJob).toBe(false); + expect(capabilities.canDeleteForecast).toBe(false); expect(capabilities.canStartStopDatafeed).toBe(false); expect(capabilities.canUpdateJob).toBe(false); expect(capabilities.canCreateDatafeed).toBe(false); @@ -387,6 +392,7 @@ describe('check_capabilities', () => { expect(capabilities.canCloseJob).toBe(false); expect(capabilities.canResetJob).toBe(false); expect(capabilities.canForecastJob).toBe(false); + expect(capabilities.canDeleteForecast).toBe(false); expect(capabilities.canStartStopDatafeed).toBe(false); expect(capabilities.canUpdateJob).toBe(false); expect(capabilities.canCreateDatafeed).toBe(false); diff --git a/x-pack/plugins/ml/server/routes/anomaly_detectors.ts b/x-pack/plugins/ml/server/routes/anomaly_detectors.ts index 1fafd467595e9..4f843620003ba 100644 --- a/x-pack/plugins/ml/server/routes/anomaly_detectors.ts +++ b/x-pack/plugins/ml/server/routes/anomaly_detectors.ts @@ -13,6 +13,7 @@ import type { RouteInitialization } from '../types'; import { anomalyDetectionJobSchema, anomalyDetectionUpdateJobSchema, + deleteForecastSchema, jobIdSchema, getBucketsSchema, getOverallBucketsSchema, @@ -379,6 +380,41 @@ export function jobRoutes({ router, routeGuard }: RouteInitialization) { }) ); + router.versioned + .delete({ + path: `${ML_INTERNAL_BASE_PATH}/anomaly_detectors/{jobId}/_forecast/{forecastId}`, + access: 'internal', + options: { + tags: ['access:ml:canDeleteForecast'], + }, + summary: 'Deletes specified forecast for specified job', + description: 'Deletes a specified forecast for the specified anomaly detection job.', + }) + .addVersion( + { + version: '1', + validate: { + request: { + params: deleteForecastSchema, + }, + }, + }, + routeGuard.fullLicenseAPIGuard(async ({ mlClient, request, response }) => { + try { + const { jobId, forecastId } = request.params; + const body = await mlClient.deleteForecast({ + job_id: jobId, + forecast_id: forecastId, + }); + return response.ok({ + body, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + router.versioned .post({ path: `${ML_INTERNAL_BASE_PATH}/anomaly_detectors/{jobId}/_forecast`, diff --git a/x-pack/plugins/ml/server/routes/schemas/anomaly_detectors_schema.ts b/x-pack/plugins/ml/server/routes/schemas/anomaly_detectors_schema.ts index 370b5d657e7c1..3b1eb0b481e46 100644 --- a/x-pack/plugins/ml/server/routes/schemas/anomaly_detectors_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/anomaly_detectors_schema.ts @@ -154,6 +154,11 @@ export const jobIdSchema = schema.object({ ...jobIdSchemaBasic, }); +export const deleteForecastSchema = schema.object({ + ...jobIdSchemaBasic, + forecastId: schema.string(), +}); + export const getBucketsSchema = schema.object({ anomaly_score: schema.maybe(schema.number()), desc: schema.maybe(schema.boolean()), diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 0ee9a547fc7f1..ea995a275449d 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -28641,7 +28641,6 @@ "xpack.ml.jobsList.jobDetails.forecastsTable.processingTimeLabel": "Temps de traitement", "xpack.ml.jobsList.jobDetails.forecastsTable.statusLabel": "Statut", "xpack.ml.jobsList.jobDetails.forecastsTable.toLabel": "À", - "xpack.ml.jobsList.jobDetails.forecastsTable.viewAriaLabel": "Afficher la prévision créée le {createdDate}", "xpack.ml.jobsList.jobDetails.forecastsTable.viewLabel": "Afficher", "xpack.ml.jobsList.jobDetails.generalTitle": "Général", "xpack.ml.jobsList.jobDetails.influencersTitle": "Influenceurs", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index d7a24239bfec4..2ec8bc11bc0c8 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -28390,7 +28390,6 @@ "xpack.ml.jobsList.jobDetails.forecastsTable.processingTimeLabel": "処理時間", "xpack.ml.jobsList.jobDetails.forecastsTable.statusLabel": "ステータス", "xpack.ml.jobsList.jobDetails.forecastsTable.toLabel": "終了:", - "xpack.ml.jobsList.jobDetails.forecastsTable.viewAriaLabel": "{createdDate} に作成された予測を表示", "xpack.ml.jobsList.jobDetails.forecastsTable.viewLabel": "表示", "xpack.ml.jobsList.jobDetails.generalTitle": "一般", "xpack.ml.jobsList.jobDetails.influencersTitle": "影響", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 8ad1766adf0f6..12ee59bb6fc9c 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -28427,7 +28427,6 @@ "xpack.ml.jobsList.jobDetails.forecastsTable.processingTimeLabel": "处理时间", "xpack.ml.jobsList.jobDetails.forecastsTable.statusLabel": "状态", "xpack.ml.jobsList.jobDetails.forecastsTable.toLabel": "至", - "xpack.ml.jobsList.jobDetails.forecastsTable.viewAriaLabel": "查看在 {createdDate} 创建的预测", "xpack.ml.jobsList.jobDetails.forecastsTable.viewLabel": "查看", "xpack.ml.jobsList.jobDetails.generalTitle": "常规", "xpack.ml.jobsList.jobDetails.influencersTitle": "影响因素", diff --git a/x-pack/test/api_integration/apis/ml/anomaly_detectors/forecast_with_spaces.ts b/x-pack/test/api_integration/apis/ml/anomaly_detectors/forecast_with_spaces.ts index 1176452408762..a330edd9a41d7 100644 --- a/x-pack/test/api_integration/apis/ml/anomaly_detectors/forecast_with_spaces.ts +++ b/x-pack/test/api_integration/apis/ml/anomaly_detectors/forecast_with_spaces.ts @@ -38,7 +38,28 @@ export default ({ getService }: FtrProviderContext) => { return body; } + async function deleteForecast( + jobId: string, + forecastId: string, + space: string, + user: USER, + expectedStatusCode: number + ) { + const { body, status } = await supertest + .delete( + `${ + space ? `/s/${space}` : '' + }/internal/ml/anomaly_detectors/${jobId}/_forecast/${forecastId}` + ) + .auth(user, ml.securityCommon.getPasswordForUser(user)) + .set(getCommonRequestHeader('1')); + ml.api.assertResponseStatusCode(expectedStatusCode, status, body); + + return body; + } + describe('POST anomaly_detectors _forecast with spaces', function () { + let forecastId: string; before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await ml.testResources.setKibanaTimeZoneToUTC(); @@ -79,13 +100,22 @@ export default ({ getService }: FtrProviderContext) => { await ml.api.waitForDatafeedState(forecastJobDatafeedId, DATAFEED_STATE.STOPPED); await ml.api.waitForJobState(forecastJobId, JOB_STATE.CLOSED); await ml.api.openAnomalyDetectionJob(forecastJobId); - await runForecast(forecastJobId, idSpace1, '1d', USER.ML_POWERUSER, 200); + const resp = await runForecast(forecastJobId, idSpace1, '1d', USER.ML_POWERUSER, 200); + forecastId = resp.forecast_id; await ml.testExecution.logTestStep( `forecast results should exist for job '${forecastJobId}'` ); await ml.api.assertForecastResultsExist(forecastJobId); }); + it('should not delete forecast for user without permissions', async () => { + await await deleteForecast(forecastJobId, forecastId, idSpace1, USER.ML_VIEWER, 403); + }); + + it('should delete forecast for user with permissions', async () => { + await await deleteForecast(forecastJobId, forecastId, idSpace1, USER.ML_POWERUSER, 200); + }); + it('should not run forecast for open job with invalid duration', async () => { await runForecast(forecastJobId, idSpace1, 3600000, USER.ML_POWERUSER, 400); }); diff --git a/x-pack/test/api_integration/apis/ml/system/capabilities.ts b/x-pack/test/api_integration/apis/ml/system/capabilities.ts index b653632432310..c4775cacdfa66 100644 --- a/x-pack/test/api_integration/apis/ml/system/capabilities.ts +++ b/x-pack/test/api_integration/apis/ml/system/capabilities.ts @@ -12,7 +12,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; import { getCommonRequestHeader } from '../../../../functional/services/ml/common_api'; import { USER } from '../../../../functional/services/ml/security_common'; -const NUMBER_OF_CAPABILITIES = 43; +const NUMBER_OF_CAPABILITIES = 44; export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertestWithoutAuth'); @@ -61,6 +61,7 @@ export default ({ getService }: FtrProviderContext) => { canResetJob: false, canUpdateJob: false, canForecastJob: false, + canDeleteForecast: false, canCreateDatafeed: false, canDeleteDatafeed: false, canStartStopDatafeed: false, @@ -111,6 +112,7 @@ export default ({ getService }: FtrProviderContext) => { canResetJob: true, canUpdateJob: true, canForecastJob: true, + canDeleteForecast: true, canCreateDatafeed: true, canDeleteDatafeed: true, canStartStopDatafeed: true, diff --git a/x-pack/test/api_integration/apis/ml/system/space_capabilities.ts b/x-pack/test/api_integration/apis/ml/system/space_capabilities.ts index f45d54a741da3..1832e5d096e34 100644 --- a/x-pack/test/api_integration/apis/ml/system/space_capabilities.ts +++ b/x-pack/test/api_integration/apis/ml/system/space_capabilities.ts @@ -15,7 +15,7 @@ import { USER } from '../../../../functional/services/ml/security_common'; const idSpaceWithMl = 'space_with_ml'; const idSpaceNoMl = 'space_no_ml'; -const NUMBER_OF_CAPABILITIES = 43; +const NUMBER_OF_CAPABILITIES = 44; export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertestWithoutAuth'); @@ -90,6 +90,7 @@ export default ({ getService }: FtrProviderContext) => { canResetJob: false, canUpdateJob: false, canForecastJob: false, + canDeleteForecast: false, canCreateDatafeed: false, canDeleteDatafeed: false, canStartStopDatafeed: false, @@ -139,6 +140,7 @@ export default ({ getService }: FtrProviderContext) => { canResetJob: false, canUpdateJob: false, canForecastJob: false, + canDeleteForecast: false, canCreateDatafeed: false, canDeleteDatafeed: false, canStartStopDatafeed: false, @@ -188,6 +190,7 @@ export default ({ getService }: FtrProviderContext) => { canResetJob: true, canUpdateJob: true, canForecastJob: true, + canDeleteForecast: true, canCreateDatafeed: true, canDeleteDatafeed: true, canStartStopDatafeed: true, @@ -237,6 +240,7 @@ export default ({ getService }: FtrProviderContext) => { canResetJob: false, canUpdateJob: false, canForecastJob: false, + canDeleteForecast: false, canCreateDatafeed: false, canDeleteDatafeed: false, canStartStopDatafeed: false, From 7fdd90d148660256d822c45f5a4594d82d21af38 Mon Sep 17 00:00:00 2001 From: Vitalii Dmyterko <92328789+vitaliidm@users.noreply.github.com> Date: Wed, 9 Oct 2024 10:14:16 +0100 Subject: [PATCH 6/8] [Security Solution][Detection Engine] fix cypress MKI flaky test (#194866) ## Summary This an attempt to fix flaky Cypress test: https://buildkite.com/organizations/elastic/analytics/suites/serverless-mki-cypress-detection-engine/tests/9cd134bd-fa8b-8ff3-858e-ba1733d30e2c?branch=main I was not able to reproduce it locally. Also, old version of test was very stable on flaky test runner too: https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/7078 I changed test a bit by re-arranging order of form filling. So, suppression fields will be last. Maybe it can reduce possibility of race condition when form is just rendered and fields being interacted with by Cypress. Also, added assertion if threshold checkbox changed it status to enabled before interacting with other suppression inputs. If this won't help, next step can be using default suppression configuration instead. New version of test: 200 runs w/o failures https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/7084 --------- Co-authored-by: Ryland Herrick --- .../detection_engine/rule_creation/threshold_rule.cy.ts | 8 ++++++-- .../cypress/tasks/create_new_rule.ts | 9 +++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/threshold_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/threshold_rule.cy.ts index 11740c1f795f8..8af755c6ed328 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/threshold_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/threshold_rule.cy.ts @@ -58,6 +58,8 @@ import { fillScheduleRuleAndContinue, selectThresholdRuleType, waitForAlertsToPopulate, + fillDefineThresholdRule, + continueFromDefineStep, } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; @@ -68,7 +70,7 @@ import { CREATE_RULE_URL } from '../../../../urls/navigation'; describe( 'Threshold rules', { - tags: ['@ess', '@serverless', '@skipInServerlessMKI'], + tags: ['@ess', '@serverless'], }, () => { const rule = getNewThresholdRule(); @@ -152,8 +154,10 @@ describe( it('Creates a new threshold rule with suppression enabled', () => { selectThresholdRuleType(); + fillDefineThresholdRule(rule); enablesAndPopulatesThresholdSuppression(5, 'h'); - fillDefineThresholdRuleAndContinue(rule); + continueFromDefineStep(); + // ensures duration displayed on define step in preview mode cy.get(DEFINITION_DETAILS).within(() => { getDetails(SUPPRESS_FOR_DETAILS).should('have.text', '5h'); diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts b/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts index 68dc2cfffd908..501dd0461dd44 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts @@ -557,7 +557,7 @@ export const fillRuleActionFilters = (alertsFilter: AlertsFilter) => { .type(`{selectall}${alertsFilter.timeframe.timezone}{enter}`); }; -export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRuleCreateProps) => { +export const fillDefineThresholdRule = (rule: ThresholdRuleCreateProps) => { const thresholdField = 0; const threshold = 1; @@ -578,7 +578,11 @@ export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRuleCreateProp cy.wrap(inputs[threshold]).clear(); cy.wrap(inputs[threshold]).type(`${rule.threshold.value}`); }); - cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true }); +}; + +export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRuleCreateProps) => { + fillDefineThresholdRule(rule); + continueFromDefineStep(); }; export const fillDefineEqlRule = (rule: EqlRuleCreateProps) => { @@ -908,6 +912,7 @@ export const enablesAndPopulatesThresholdSuppression = ( // enables suppression for threshold rule cy.get(THRESHOLD_ENABLE_SUPPRESSION_CHECKBOX).should('not.be.checked'); cy.get(THRESHOLD_ENABLE_SUPPRESSION_CHECKBOX).click(); + cy.get(THRESHOLD_ENABLE_SUPPRESSION_CHECKBOX).should('be.checked'); setAlertSuppressionDuration(interval, timeUnit); From 94934505eb3673db64dd429bf25386bdc9fe9bc5 Mon Sep 17 00:00:00 2001 From: Alex Szabo Date: Wed, 9 Oct 2024 11:26:46 +0200 Subject: [PATCH 7/8] [CI] Add tags for kibana pipelines (#195388) ## Summary Kibana-related pipelines are hard to find on Buildkite, due to other, ingest-related pipelines having 'kibana' in their names. This pipeline adds tags to pipelines serving `kibana` CI duties, so they can be easily found using Buildkite's tags/labels. The tags added are mostly `kibana` but some pipelines also got the `security-solution` label, as these pipelines can be easily associated with the served solution. --- .../_templates/_new_pipeline.yml | 2 ++ .../pipeline-resource-definitions/kibana-api-docs.yml | 2 ++ .../kibana-apis-capacity-testing-daily.yml | 2 ++ .../kibana-artifacts-container-image.yml | 2 ++ .../kibana-artifacts-snapshot.yml | 2 ++ .../kibana-artifacts-staging.yml | 2 ++ .../kibana-artifacts-trigger.yml | 2 ++ .../kibana-chrome-forward-testing.yml | 2 ++ .buildkite/pipeline-resource-definitions/kibana-codeql.yml | 2 ++ .../pipeline-resource-definitions/kibana-coverage-daily.yml | 2 ++ .../kibana-es-forward-testing.yml | 2 ++ .../kibana-es-serverless-snapshots.yml | 2 ++ .../pipeline-resource-definitions/kibana-es-snapshots.yml | 6 ++++++ .../kibana-esql-grammar-sync.yml | 2 ++ .../pipeline-resource-definitions/kibana-fips-daily.yml | 2 ++ .buildkite/pipeline-resource-definitions/kibana-flaky.yml | 2 ++ .../kibana-fleet-packages-daily.yml | 2 ++ .../kibana-migration-staging.yml | 2 ++ .../kibana-on-merge-unsupported-ftrs.yml | 2 ++ .../pipeline-resource-definitions/kibana-on-merge.yml | 2 ++ .../kibana-performance-daily.yml | 2 ++ .../kibana-performance-data-set-extraction-daily.yml | 2 ++ .../kibana-pointer-compression.yml | 2 ++ .buildkite/pipeline-resource-definitions/kibana-pr.yml | 2 ++ .../kibana-purge-cloud-deployments.yml | 2 ++ .../kibana-serverless-emergency-release.yml | 2 ++ .../kibana-serverless-quality-gates-emergency.yml | 2 ++ .../kibana-serverless-quality-gates.yml | 2 ++ .../kibana-serverless-release-testing.yml | 2 ++ .../kibana-serverless-release.yml | 2 ++ .../scalability_testing-daily.yml | 2 ++ .../security-solution-ess/security-solution-ess.yml | 3 +++ ...less-security-solution-quality-gate-defend-workflows.yml | 3 +++ ...less-security-solution-quality-gate-detection-engine.yml | 3 +++ ...less-security-solution-quality-gate-entity-analytics.yml | 3 +++ ...na-serverless-security-solution-quality-gate-explore.yml | 3 +++ ...ana-serverless-security-solution-quality-gate-gen-ai.yml | 3 +++ ...erless-security-solution-quality-gate-investigations.yml | 3 +++ ...rless-security-solution-quality-gate-rule-management.yml | 3 +++ .../trigger-version-dependent-jobs.yml | 2 ++ 40 files changed, 92 insertions(+) diff --git a/.buildkite/pipeline-resource-definitions/_templates/_new_pipeline.yml b/.buildkite/pipeline-resource-definitions/_templates/_new_pipeline.yml index f33e738882693..6ef0d7652b964 100644 --- a/.buildkite/pipeline-resource-definitions/_templates/_new_pipeline.yml +++ b/.buildkite/pipeline-resource-definitions/_templates/_new_pipeline.yml @@ -70,3 +70,5 @@ spec: # Optionally, set schedule-specific env-vars here env: SCHEDULED: 'true' + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-api-docs.yml b/.buildkite/pipeline-resource-definitions/kibana-api-docs.yml index 5368b60314e47..26ff7242dac65 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-api-docs.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-api-docs.yml @@ -49,3 +49,5 @@ spec: cronline: 0 0 * * * America/New_York message: Daily build branch: main + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-apis-capacity-testing-daily.yml b/.buildkite/pipeline-resource-definitions/kibana-apis-capacity-testing-daily.yml index c52e6203485f4..244a0351de0be 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-apis-capacity-testing-daily.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-apis-capacity-testing-daily.yml @@ -47,3 +47,5 @@ spec: cronline: 0 1/3 * * * Europe/Berlin message: Capacity every 3h testing branch: main + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-artifacts-container-image.yml b/.buildkite/pipeline-resource-definitions/kibana-artifacts-container-image.yml index eff970c69af6b..37bc5ee59ff0b 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-artifacts-container-image.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-artifacts-container-image.yml @@ -42,3 +42,5 @@ spec: access_level: MANAGE_BUILD_AND_READ kibana-tech-leads: access_level: MANAGE_BUILD_AND_READ + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-artifacts-snapshot.yml b/.buildkite/pipeline-resource-definitions/kibana-artifacts-snapshot.yml index e1c40f690f4ec..f994f0cba33c3 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-artifacts-snapshot.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-artifacts-snapshot.yml @@ -43,3 +43,5 @@ spec: access_level: MANAGE_BUILD_AND_READ kibana-tech-leads: access_level: MANAGE_BUILD_AND_READ + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-artifacts-staging.yml b/.buildkite/pipeline-resource-definitions/kibana-artifacts-staging.yml index 71bcc4079c50d..1d7b0488c7b3f 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-artifacts-staging.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-artifacts-staging.yml @@ -43,3 +43,5 @@ spec: access_level: MANAGE_BUILD_AND_READ kibana-tech-leads: access_level: MANAGE_BUILD_AND_READ + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-artifacts-trigger.yml b/.buildkite/pipeline-resource-definitions/kibana-artifacts-trigger.yml index 8a9585762de83..f08e505b9aabb 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-artifacts-trigger.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-artifacts-trigger.yml @@ -44,3 +44,5 @@ spec: access_level: MANAGE_BUILD_AND_READ kibana-tech-leads: access_level: MANAGE_BUILD_AND_READ + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-chrome-forward-testing.yml b/.buildkite/pipeline-resource-definitions/kibana-chrome-forward-testing.yml index beeb6152509b6..15265da35f390 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-chrome-forward-testing.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-chrome-forward-testing.yml @@ -47,3 +47,5 @@ spec: cronline: 0 12 * * * message: Daily Chrome Forward Testing branch: main + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-codeql.yml b/.buildkite/pipeline-resource-definitions/kibana-codeql.yml index 3da2c9137c4e0..68a41a547a64a 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-codeql.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-codeql.yml @@ -32,3 +32,5 @@ spec: access_level: MANAGE_BUILD_AND_READ everyone: access_level: READ_ONLY + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-coverage-daily.yml b/.buildkite/pipeline-resource-definitions/kibana-coverage-daily.yml index c73a276a6d786..4192bb9186589 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-coverage-daily.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-coverage-daily.yml @@ -48,3 +48,5 @@ spec: cronline: 0 5 * * * message: Daily 6 am UTC branch: main + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-es-forward-testing.yml b/.buildkite/pipeline-resource-definitions/kibana-es-forward-testing.yml index 2d87415841df6..fa4ee2d263873 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-es-forward-testing.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-es-forward-testing.yml @@ -40,3 +40,5 @@ spec: access_level: MANAGE_BUILD_AND_READ everyone: access_level: BUILD_AND_READ + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-es-serverless-snapshots.yml b/.buildkite/pipeline-resource-definitions/kibana-es-serverless-snapshots.yml index 60bedaafba586..684e2e07fb187 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-es-serverless-snapshots.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-es-serverless-snapshots.yml @@ -53,3 +53,5 @@ spec: env: PUBLISH_DOCKER_TAG: 'true' branch: main + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-es-snapshots.yml b/.buildkite/pipeline-resource-definitions/kibana-es-snapshots.yml index 1c041d7016f44..851862a613111 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-es-snapshots.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-es-snapshots.yml @@ -61,6 +61,8 @@ spec: cronline: 0 9 * * * America/New_York message: Daily build branch: '7.17' + tags: + - kibana --- # yaml-language-server: $schema=https://gist.githubusercontent.com/elasticmachine/988b80dae436cafea07d9a4a460a011d/raw/rre.schema.json apiVersion: backstage.io/v1alpha1 @@ -108,6 +110,8 @@ spec: access_level: MANAGE_BUILD_AND_READ kibana-tech-leads: access_level: MANAGE_BUILD_AND_READ + tags: + - kibana --- # yaml-language-server: $schema=https://gist.githubusercontent.com/elasticmachine/988b80dae436cafea07d9a4a460a011d/raw/rre.schema.json apiVersion: backstage.io/v1alpha1 @@ -156,3 +160,5 @@ spec: access_level: MANAGE_BUILD_AND_READ kibana-tech-leads: access_level: MANAGE_BUILD_AND_READ + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-esql-grammar-sync.yml b/.buildkite/pipeline-resource-definitions/kibana-esql-grammar-sync.yml index 952babf7d580d..8cc4b54a5ce0c 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-esql-grammar-sync.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-esql-grammar-sync.yml @@ -51,3 +51,5 @@ spec: cronline: 0 0 * * 1 America/New_York message: Weekly build branch: main + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-fips-daily.yml b/.buildkite/pipeline-resource-definitions/kibana-fips-daily.yml index b64521858c1f6..bedb81cccc5a4 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-fips-daily.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-fips-daily.yml @@ -38,3 +38,5 @@ spec: access_level: MANAGE_BUILD_AND_READ everyone: access_level: READ_ONLY + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-flaky.yml b/.buildkite/pipeline-resource-definitions/kibana-flaky.yml index 82797c03f2378..f1c348e059209 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-flaky.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-flaky.yml @@ -39,3 +39,5 @@ spec: access_level: MANAGE_BUILD_AND_READ kibana-tech-leads: access_level: MANAGE_BUILD_AND_READ + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-fleet-packages-daily.yml b/.buildkite/pipeline-resource-definitions/kibana-fleet-packages-daily.yml index 8805fef47f914..d948460513c8e 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-fleet-packages-daily.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-fleet-packages-daily.yml @@ -47,3 +47,5 @@ spec: message: Single user daily test env: {} branch: main + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-migration-staging.yml b/.buildkite/pipeline-resource-definitions/kibana-migration-staging.yml index b999babc24fc8..980fee4db5671 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-migration-staging.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-migration-staging.yml @@ -30,3 +30,5 @@ spec: access_level: MANAGE_BUILD_AND_READ everyone: access_level: BUILD_AND_READ + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-on-merge-unsupported-ftrs.yml b/.buildkite/pipeline-resource-definitions/kibana-on-merge-unsupported-ftrs.yml index b9c6cb8970271..a6ddb28309987 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-on-merge-unsupported-ftrs.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-on-merge-unsupported-ftrs.yml @@ -44,3 +44,5 @@ spec: access_level: MANAGE_BUILD_AND_READ kibana-tech-leads: access_level: MANAGE_BUILD_AND_READ + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-on-merge.yml b/.buildkite/pipeline-resource-definitions/kibana-on-merge.yml index 6fe305979652e..5e6622e6da513 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-on-merge.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-on-merge.yml @@ -47,3 +47,5 @@ spec: access_level: MANAGE_BUILD_AND_READ kibana-tech-leads: access_level: MANAGE_BUILD_AND_READ + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-performance-daily.yml b/.buildkite/pipeline-resource-definitions/kibana-performance-daily.yml index 9ed561c9cfdbe..915cce93a2481 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-performance-daily.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-performance-daily.yml @@ -48,3 +48,5 @@ spec: cronline: 0 */3 * * * Europe/Berlin message: Single user daily test branch: main + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-performance-data-set-extraction-daily.yml b/.buildkite/pipeline-resource-definitions/kibana-performance-data-set-extraction-daily.yml index aa38564fd963b..3fe79832d35fd 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-performance-data-set-extraction-daily.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-performance-data-set-extraction-daily.yml @@ -47,3 +47,5 @@ spec: cronline: 0 3/8 * * * Europe/Berlin message: Extract APM traces branch: main + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-pointer-compression.yml b/.buildkite/pipeline-resource-definitions/kibana-pointer-compression.yml index 5a23fc95d9971..bcc94453b14e2 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-pointer-compression.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-pointer-compression.yml @@ -36,3 +36,5 @@ spec: access_level: MANAGE_BUILD_AND_READ everyone: access_level: READ_ONLY + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-pr.yml b/.buildkite/pipeline-resource-definitions/kibana-pr.yml index 4d6275843327e..2ce36f6799b5a 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-pr.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-pr.yml @@ -47,3 +47,5 @@ spec: access_level: MANAGE_BUILD_AND_READ kibana-tech-leads: access_level: MANAGE_BUILD_AND_READ + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-purge-cloud-deployments.yml b/.buildkite/pipeline-resource-definitions/kibana-purge-cloud-deployments.yml index 3b5d3fd84fad5..9124d001d6f70 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-purge-cloud-deployments.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-purge-cloud-deployments.yml @@ -49,3 +49,5 @@ spec: access_level: MANAGE_BUILD_AND_READ everyone: access_level: BUILD_AND_READ + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-serverless-emergency-release.yml b/.buildkite/pipeline-resource-definitions/kibana-serverless-emergency-release.yml index 5911095262ac1..c51e44432596d 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-serverless-emergency-release.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-serverless-emergency-release.yml @@ -28,3 +28,5 @@ spec: access_level: BUILD_AND_READ everyone: access_level: READ_ONLY + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-serverless-quality-gates-emergency.yml b/.buildkite/pipeline-resource-definitions/kibana-serverless-quality-gates-emergency.yml index ba053d7c44da6..267db48ba6d90 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-serverless-quality-gates-emergency.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-serverless-quality-gates-emergency.yml @@ -31,3 +31,5 @@ spec: access_level: BUILD_AND_READ everyone: access_level: READ_ONLY + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-serverless-quality-gates.yml b/.buildkite/pipeline-resource-definitions/kibana-serverless-quality-gates.yml index 1f57f2ca83250..8d4e7f35cd6fe 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-serverless-quality-gates.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-serverless-quality-gates.yml @@ -31,3 +31,5 @@ spec: access_level: BUILD_AND_READ everyone: access_level: READ_ONLY + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-serverless-release-testing.yml b/.buildkite/pipeline-resource-definitions/kibana-serverless-release-testing.yml index fe3fdaf49c748..0a033e72d53b8 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-serverless-release-testing.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-serverless-release-testing.yml @@ -44,3 +44,5 @@ spec: access_level: MANAGE_BUILD_AND_READ kibana-tech-leads: access_level: MANAGE_BUILD_AND_READ + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/kibana-serverless-release.yml b/.buildkite/pipeline-resource-definitions/kibana-serverless-release.yml index 057a31c47190a..7a35ea3ad1ec8 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-serverless-release.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-serverless-release.yml @@ -46,3 +46,5 @@ spec: env: AUTO_SELECT_COMMIT: 'true' branch: main + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/scalability_testing-daily.yml b/.buildkite/pipeline-resource-definitions/scalability_testing-daily.yml index 06f2f2dd6634b..162bb6220ea1c 100644 --- a/.buildkite/pipeline-resource-definitions/scalability_testing-daily.yml +++ b/.buildkite/pipeline-resource-definitions/scalability_testing-daily.yml @@ -47,3 +47,5 @@ spec: cronline: 0 6 * * * Europe/Berlin message: Scalability daily benchmarking branch: main + tags: + - kibana diff --git a/.buildkite/pipeline-resource-definitions/security-solution-ess/security-solution-ess.yml b/.buildkite/pipeline-resource-definitions/security-solution-ess/security-solution-ess.yml index b22891cbe9d2d..239bd74c66922 100644 --- a/.buildkite/pipeline-resource-definitions/security-solution-ess/security-solution-ess.yml +++ b/.buildkite/pipeline-resource-definitions/security-solution-ess/security-solution-ess.yml @@ -35,3 +35,6 @@ spec: access_level: MANAGE_BUILD_AND_READ everyone: access_level: BUILD_AND_READ + tags: + - kibana + - security-solution diff --git a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-defend-workflows.yml b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-defend-workflows.yml index d5e32ca55172c..d4d2541f1c4ad 100644 --- a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-defend-workflows.yml +++ b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-defend-workflows.yml @@ -36,3 +36,6 @@ spec: access_level: MANAGE_BUILD_AND_READ everyone: access_level: BUILD_AND_READ + tags: + - kibana + - security-solution diff --git a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-detection-engine.yml b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-detection-engine.yml index 8dc4265b3e6f4..77361eed441e6 100644 --- a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-detection-engine.yml +++ b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-detection-engine.yml @@ -36,3 +36,6 @@ spec: access_level: MANAGE_BUILD_AND_READ everyone: access_level: BUILD_AND_READ + tags: + - kibana + - security-solution diff --git a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-entity-analytics.yml b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-entity-analytics.yml index 9d5bba5f40d1d..49338bf7b6d32 100644 --- a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-entity-analytics.yml +++ b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-entity-analytics.yml @@ -36,3 +36,6 @@ spec: access_level: MANAGE_BUILD_AND_READ everyone: access_level: BUILD_AND_READ + tags: + - kibana + - security-solution diff --git a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-explore.yml b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-explore.yml index cd2739fe4a6fb..ee8cf00a755f9 100644 --- a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-explore.yml +++ b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-explore.yml @@ -36,3 +36,6 @@ spec: access_level: MANAGE_BUILD_AND_READ everyone: access_level: BUILD_AND_READ + tags: + - kibana + - security-solution diff --git a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-gen-ai.yml b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-gen-ai.yml index 1aeeefe2a0ad8..f22e321176eb0 100644 --- a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-gen-ai.yml +++ b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-gen-ai.yml @@ -36,3 +36,6 @@ spec: access_level: MANAGE_BUILD_AND_READ everyone: access_level: BUILD_AND_READ + tags: + - kibana + - security-solution diff --git a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-investigations.yml b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-investigations.yml index 955bcf24b1e63..7de3b5f8cc282 100644 --- a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-investigations.yml +++ b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-investigations.yml @@ -36,3 +36,6 @@ spec: access_level: MANAGE_BUILD_AND_READ everyone: access_level: BUILD_AND_READ + tags: + - kibana + - security-solution diff --git a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-rule-management.yml b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-rule-management.yml index af0386076dd4d..4f095294f4422 100644 --- a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-rule-management.yml +++ b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-rule-management.yml @@ -36,3 +36,6 @@ spec: access_level: MANAGE_BUILD_AND_READ everyone: access_level: BUILD_AND_READ + tags: + - kibana + - security-solution diff --git a/.buildkite/pipeline-resource-definitions/trigger-version-dependent-jobs.yml b/.buildkite/pipeline-resource-definitions/trigger-version-dependent-jobs.yml index ea474356b137d..8dd486c3176ce 100644 --- a/.buildkite/pipeline-resource-definitions/trigger-version-dependent-jobs.yml +++ b/.buildkite/pipeline-resource-definitions/trigger-version-dependent-jobs.yml @@ -69,3 +69,5 @@ spec: env: TRIGGER_PIPELINE_SET: artifacts-trigger MESSAGE: Daily build + tags: + - kibana From ee4815a570e4ac1151deec1eb28437ec585411b6 Mon Sep 17 00:00:00 2001 From: Sandra G Date: Wed, 9 Oct 2024 05:47:16 -0400 Subject: [PATCH 8/8] [Data Usage] add locator to link to data stream management (#195433) ## Summary add locator to link to data stream management recently made available https://github.com/elastic/kibana/pull/195299 --- .../public/app/components/legend_action.tsx | 12 ++++++------ x-pack/plugins/data_usage/tsconfig.json | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/data_usage/public/app/components/legend_action.tsx b/x-pack/plugins/data_usage/public/app/components/legend_action.tsx index a816d1f8eadda..c9059037c4445 100644 --- a/x-pack/plugins/data_usage/public/app/components/legend_action.tsx +++ b/x-pack/plugins/data_usage/public/app/components/legend_action.tsx @@ -14,6 +14,7 @@ import { EuiListGroupItem, EuiSpacer, } from '@elastic/eui'; +import { IndexManagementLocatorParams } from '@kbn/index-management-shared-types'; import { DatasetQualityLink } from './dataset_quality_link'; import { useKibanaContextForPlugin } from '../../utils/use_kibana'; @@ -39,12 +40,11 @@ export const LegendAction: React.FC = React.memo( const hasIndexManagementFeature = !!capabilities?.index_management; const onClickIndexManagement = useCallback(async () => { - // TODO: use proper index management locator https://github.com/elastic/kibana/issues/195083 - const dataQualityLocator = locators.get('MANAGEMENT_APP_LOCATOR'); - if (dataQualityLocator) { - await dataQualityLocator.navigate({ - sectionId: 'data', - appId: `index_management/data_streams/${label}`, + const locator = locators.get('INDEX_MANAGEMENT_LOCATOR_ID'); + if (locator) { + await locator.navigate({ + page: 'data_streams_details', + dataStreamName: label, }); } togglePopover(null); // Close the popover after action diff --git a/x-pack/plugins/data_usage/tsconfig.json b/x-pack/plugins/data_usage/tsconfig.json index cecbeb654db30..d3754906475e9 100644 --- a/x-pack/plugins/data_usage/tsconfig.json +++ b/x-pack/plugins/data_usage/tsconfig.json @@ -28,6 +28,7 @@ "@kbn/core-http-browser", "@kbn/core-chrome-browser", "@kbn/features-plugin", + "@kbn/index-management-shared-types", ], "exclude": ["target/**/*"] }