From d619e41e0e676802bee2b95a314a46194ca1d5b7 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Mon, 4 Nov 2024 16:50:48 +0100 Subject: [PATCH 01/16] wip(discover): implement app menu actions --- src/plugins/discover/kibana.jsonc | 5 +- src/plugins/discover/public/build_services.ts | 2 + .../accessors/get_app_menu.ts | 161 ++++++++++++++++++ .../accessors/index.ts | 1 + .../logs_data_source_profile/profile.ts | 2 + src/plugins/discover/public/types.ts | 1 + .../components/dataset_quality_link.tsx | 7 +- 7 files changed, 174 insertions(+), 5 deletions(-) create mode 100644 src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_app_menu.ts diff --git a/src/plugins/discover/kibana.jsonc b/src/plugins/discover/kibana.jsonc index 87837a38ed834..90cb2347be787 100644 --- a/src/plugins/discover/kibana.jsonc +++ b/src/plugins/discover/kibana.jsonc @@ -30,7 +30,7 @@ "unifiedDocViewer", "unifiedSearch", "unifiedHistogram", - "contentManagement" + "contentManagement", ], "optionalPlugins": [ "dataVisualizer", @@ -46,7 +46,8 @@ "observabilityAIAssistant", "aiops", "fieldsMetadata", - "logsDataAccess" + "logsDataAccess", + "slo" ], "requiredBundles": [ "kibanaUtils", diff --git a/src/plugins/discover/public/build_services.ts b/src/plugins/discover/public/build_services.ts index bae2907af7699..fe972c0f9eb61 100644 --- a/src/plugins/discover/public/build_services.ts +++ b/src/plugins/discover/public/build_services.ts @@ -59,6 +59,7 @@ import type { AiopsPluginStart } from '@kbn/aiops-plugin/public'; import type { DataVisualizerPluginStart } from '@kbn/data-visualizer-plugin/public'; import type { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/public'; import { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/public'; +import { SloPublicStart } from '@kbn/slo-plugin/public'; import type { DiscoverStartPlugins } from './types'; import type { DiscoverContextAppLocator } from './application/context/services/locator'; import type { DiscoverSingleDocLocator } from './application/doc/locator'; @@ -135,6 +136,7 @@ export interface DiscoverServices { ebtManager: DiscoverEBTManager; fieldsMetadata?: FieldsMetadataPublicStart; logsDataAccess?: LogsDataAccessPluginStart; + slo: SloPublicStart; } export const buildServices = memoize( diff --git a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_app_menu.ts b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_app_menu.ts new file mode 100644 index 0000000000000..91d40b50bf188 --- /dev/null +++ b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_app_menu.ts @@ -0,0 +1,161 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { AppMenuActionId, AppMenuActionType } from '@kbn/discover-utils'; +import { + DATA_QUALITY_LOCATOR_ID, + DataQualityLocatorParams, + OBSERVABILITY_ONBOARDING_LOCATOR, + ObservabilityOnboardingLocatorParams, +} from '@kbn/deeplinks-observability'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; +import type { DataSourceProfileProvider } from '../../../../profiles'; +import { ProfileProviderServices } from '../../../profile_provider_services'; + +export const createGetAppMenu = + (services: ProfileProviderServices): DataSourceProfileProvider['profile']['getAppMenu'] => + (prev) => + (params) => { + const prevValue = prev(params); + + // This is what is available via params: + const { dataView } = params; + const { share, slo, timefilter, triggersActionsUi } = services; + + return { + appMenuRegistry: (registry) => { + const onboardingLocator = share?.url.locators.get( + OBSERVABILITY_ONBOARDING_LOCATOR + ); + + const dataQualityLocator = + share?.url.locators.get(DATA_QUALITY_LOCATOR_ID); + + if (onboardingLocator) { + registry.registerCustomAction({ + id: 'add-data-action', + type: AppMenuActionType.custom, + controlProps: { + label: 'Add data', + testId: 'add-data-action', + onClick: ({ onFinishAction }) => { + onboardingLocator.navigate({ category: 'logs' }); + + onFinishAction(); // This allows to return focus back to the app menu DOM node + }, + }, + }); + } + + if (dataQualityLocator) { + registry.registerCustomAction({ + id: 'dataset-quality-link', + type: AppMenuActionType.custom, + controlProps: { + label: 'Data sets', + testId: 'dataset-quality-link', + onClick: ({ onFinishAction }) => { + const refresh = timefilter.getRefreshInterval(); + const { from, to } = timefilter.getTime(); + + dataQualityLocator.navigate({ + filters: { + timeRange: { + from: from ?? 'now-24h', + to: to ?? 'now', + refresh, + }, + }, + }); + + onFinishAction(); // This allows to return focus back to the app menu DOM node + }, + }, + }); + } + + // This example shows how to add a custom action under the Alerts submenu + registry.registerCustomActionUnderSubmenu(AppMenuActionId.alerts, { + // It's also possible to override the submenu actions by using the same id + // as `AppMenuActionId.createRule` or `AppMenuActionId.manageRulesAndConnectors` + id: AppMenuActionId.createRule, + type: AppMenuActionType.custom, + order: 101, + controlProps: { + label: 'Create custom threshold rule', + iconType: 'visGauge', + testId: 'custom-threshold-rule', + onClick: ({ onFinishAction }) => { + const index = dataView?.getIndexPattern(); + + return triggersActionsUi.getAddRuleFlyout({ + consumer: 'logs', + ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, + canChangeTrigger: false, + initialValues: { + params: { + searchConfiguration: { + index, + // query: getQuery(logsExplorerState.query), + // filter: getDiscoverFiltersFromState( + // index.id, + // logsExplorerState.filters, + // logsExplorerState.controls + // ), + }, + }, + }, + onClose: onFinishAction, + }); + }, + }, + }); + + registry.registerCustomActionUnderSubmenu(AppMenuActionId.alerts, { + // It's also possible to override the submenu actions by using the same id + // as `AppMenuActionId.createRule` or `AppMenuActionId.manageRulesAndConnectors` + id: 'create-slo', + type: AppMenuActionType.custom, + order: 102, + controlProps: { + label: 'Create SLO', + iconType: 'bell', + testId: 'create-slo', + onClick: ({ onFinishAction }) => { + const index = dataView?.getIndexPattern(); + + return slo.getCreateSLOFlyout({ + initialValues: { + indicator: { + type: 'sli.kql.custom', + params: { + index, + timestampField: dataView?.timeFieldName, + // filter: { + // kqlQuery: query, + // filters: getDiscoverFiltersFromState( + // dataView.id, + // logsExplorerState.filters, + // logsExplorerState.controls + // ), + // }, + }, + }, + // groupBy: logsExplorerState.chart.breakdownField ?? undefined, + }, + onClose: onFinishAction, + }); + }, + }, + }); + + return prevValue.appMenuRegistry(registry); + }, + }; + }; diff --git a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/index.ts b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/index.ts index 720a1add3c926..0aecf1501b0d0 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/index.ts +++ b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/index.ts @@ -9,5 +9,6 @@ export { getRowIndicatorProvider } from './get_row_indicator_provider'; export { createGetDefaultAppState } from './get_default_app_state'; +export { createGetAppMenu } from './get_app_menu'; export { getCellRenderers } from './get_cell_renderers'; export { getRowAdditionalLeadingControls } from './get_row_additional_leading_controls'; diff --git a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/profile.ts b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/profile.ts index f2818c336bf40..74d1ddf8b5256 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/profile.ts +++ b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/profile.ts @@ -13,6 +13,7 @@ import { getCellRenderers, getRowIndicatorProvider, getRowAdditionalLeadingControls, + createGetAppMenu, } from './accessors'; import { extractIndexPatternFrom } from '../../extract_index_pattern_from'; @@ -21,6 +22,7 @@ export const createLogsDataSourceProfileProvider = ( ): DataSourceProfileProvider => ({ profileId: 'logs-data-source-profile', profile: { + getAppMenu: createGetAppMenu(services), getCellRenderers, getRowIndicatorProvider, getRowAdditionalLeadingControls, diff --git a/src/plugins/discover/public/types.ts b/src/plugins/discover/public/types.ts index 3b24341e1a654..8926e19ecf744 100644 --- a/src/plugins/discover/public/types.ts +++ b/src/plugins/discover/public/types.ts @@ -172,4 +172,5 @@ export interface DiscoverStartPlugins { unifiedSearch: UnifiedSearchPublicPluginStart; urlForwarding: UrlForwardingStart; usageCollection?: UsageCollectionSetup; + slo: SloPublicStart; } diff --git a/x-pack/plugins/observability_solution/observability_logs_explorer/public/components/dataset_quality_link.tsx b/x-pack/plugins/observability_solution/observability_logs_explorer/public/components/dataset_quality_link.tsx index 05c74a9a1c82a..bd754545cca06 100644 --- a/x-pack/plugins/observability_solution/observability_logs_explorer/public/components/dataset_quality_link.tsx +++ b/x-pack/plugins/observability_solution/observability_logs_explorer/public/components/dataset_quality_link.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiHeaderLink } from '@elastic/eui'; +import { EuiHeaderLink, EuiLink } from '@elastic/eui'; import { LogsExplorerPublicState } from '@kbn/logs-explorer-plugin/public'; import { getRouterLinkProps } from '@kbn/router-utils'; import { LocatorPublic } from '@kbn/share-plugin/public'; @@ -92,13 +92,14 @@ export const DatasetQualityLink = React.memo( }); return ( - {datasetQualityLinkTitle} - + ); } ); From c02bf21cd993b2139a647864073817fd2987cd9b Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Tue, 5 Nov 2024 14:27:47 +0100 Subject: [PATCH 02/16] feat(discover): implement app menu actions mvp --- src/plugins/discover/kibana.jsonc | 2 +- src/plugins/discover/public/build_services.ts | 5 +- .../accessors/get_app_menu.ts | 271 ++++++++++-------- src/plugins/discover/public/types.ts | 2 + .../services/discover_features/types.ts | 7 +- .../observability_solution/slo/kibana.jsonc | 1 + .../slo/public/plugin.ts | 28 +- .../slo/public/types.ts | 2 + 8 files changed, 181 insertions(+), 137 deletions(-) diff --git a/src/plugins/discover/kibana.jsonc b/src/plugins/discover/kibana.jsonc index 90cb2347be787..f605d0ae1df95 100644 --- a/src/plugins/discover/kibana.jsonc +++ b/src/plugins/discover/kibana.jsonc @@ -15,6 +15,7 @@ "charts", "data", "dataViews", + "discoverShared", "embeddable", "inspector", "fieldFormats", @@ -47,7 +48,6 @@ "aiops", "fieldsMetadata", "logsDataAccess", - "slo" ], "requiredBundles": [ "kibanaUtils", diff --git a/src/plugins/discover/public/build_services.ts b/src/plugins/discover/public/build_services.ts index fe972c0f9eb61..df194bc03fa0f 100644 --- a/src/plugins/discover/public/build_services.ts +++ b/src/plugins/discover/public/build_services.ts @@ -59,7 +59,7 @@ import type { AiopsPluginStart } from '@kbn/aiops-plugin/public'; import type { DataVisualizerPluginStart } from '@kbn/data-visualizer-plugin/public'; import type { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/public'; import { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/public'; -import { SloPublicStart } from '@kbn/slo-plugin/public'; +import { DiscoverSharedPublicStart } from '@kbn/discover-shared-plugin/public'; import type { DiscoverStartPlugins } from './types'; import type { DiscoverContextAppLocator } from './application/context/services/locator'; import type { DiscoverSingleDocLocator } from './application/doc/locator'; @@ -90,6 +90,7 @@ export interface DiscoverServices { chrome: ChromeStart; core: CoreStart; data: DataPublicPluginStart; + discoverShared: DiscoverSharedPublicStart; docLinks: DocLinksStart; embeddable: EmbeddableStart; history: History; @@ -136,7 +137,6 @@ export interface DiscoverServices { ebtManager: DiscoverEBTManager; fieldsMetadata?: FieldsMetadataPublicStart; logsDataAccess?: LogsDataAccessPluginStart; - slo: SloPublicStart; } export const buildServices = memoize( @@ -180,6 +180,7 @@ export const buildServices = memoize( core, data: plugins.data, dataVisualizer: plugins.dataVisualizer, + discoverShared: plugins.discoverShared, docLinks: core.docLinks, embeddable: plugins.embeddable, i18n: core.i18n, diff --git a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_app_menu.ts b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_app_menu.ts index 91d40b50bf188..26c8f0e2ab296 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_app_menu.ts +++ b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_app_menu.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { AppMenuActionId, AppMenuActionType } from '@kbn/discover-utils'; +import { AppMenuActionId, AppMenuActionType, AppMenuRegistry } from '@kbn/discover-utils'; import { DATA_QUALITY_LOCATOR_ID, DataQualityLocatorParams, @@ -15,6 +15,8 @@ import { ObservabilityOnboardingLocatorParams, } from '@kbn/deeplinks-observability'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; +import { isOfQueryType } from '@kbn/es-query'; +import { AppMenuExtensionParams } from '../../../..'; import type { DataSourceProfileProvider } from '../../../../profiles'; import { ProfileProviderServices } from '../../../profile_provider_services'; @@ -24,138 +26,161 @@ export const createGetAppMenu = (params) => { const prevValue = prev(params); - // This is what is available via params: - const { dataView } = params; - const { share, slo, timefilter, triggersActionsUi } = services; - return { appMenuRegistry: (registry) => { - const onboardingLocator = share?.url.locators.get( - OBSERVABILITY_ONBOARDING_LOCATOR - ); - - const dataQualityLocator = - share?.url.locators.get(DATA_QUALITY_LOCATOR_ID); - - if (onboardingLocator) { - registry.registerCustomAction({ - id: 'add-data-action', - type: AppMenuActionType.custom, - controlProps: { - label: 'Add data', - testId: 'add-data-action', - onClick: ({ onFinishAction }) => { - onboardingLocator.navigate({ category: 'logs' }); - - onFinishAction(); // This allows to return focus back to the app menu DOM node + // Register custom link actions + registerOnboardingLink(registry, services); + registerDatasetQualityLink(registry, services); + // Register alerts sub menu actions + registerCreateSLOAction(registry, services, params); + registerCustomThresholdRuleAction(registry, services, params); + + return prevValue.appMenuRegistry(registry); + }, + }; + }; + +const registerOnboardingLink = (registry: AppMenuRegistry, { share }: ProfileProviderServices) => { + const onboardingLocator = share?.url.locators.get( + OBSERVABILITY_ONBOARDING_LOCATOR + ); + + if (onboardingLocator) { + registry.registerCustomAction({ + id: 'add-data-action', + type: AppMenuActionType.custom, + controlProps: { + label: 'Add data', + testId: 'add-data-action', + onClick: ({ onFinishAction }) => { + onboardingLocator.navigate({ category: 'logs' }); + + onFinishAction(); + }, + }, + }); + } +}; + +const registerDatasetQualityLink = ( + registry: AppMenuRegistry, + { share, timefilter }: ProfileProviderServices +) => { + const dataQualityLocator = + share?.url.locators.get(DATA_QUALITY_LOCATOR_ID); + + if (dataQualityLocator) { + registry.registerCustomAction({ + id: 'dataset-quality-link', + type: AppMenuActionType.custom, + controlProps: { + label: 'Data sets', + testId: 'dataset-quality-link', + onClick: ({ onFinishAction }) => { + const refresh = timefilter.getRefreshInterval(); + const { from, to } = timefilter.getTime(); + + dataQualityLocator.navigate({ + filters: { + timeRange: { + from: from ?? 'now-24h', + to: to ?? 'now', + refresh, }, }, }); - } - - if (dataQualityLocator) { - registry.registerCustomAction({ - id: 'dataset-quality-link', - type: AppMenuActionType.custom, - controlProps: { - label: 'Data sets', - testId: 'dataset-quality-link', - onClick: ({ onFinishAction }) => { - const refresh = timefilter.getRefreshInterval(); - const { from, to } = timefilter.getTime(); - - dataQualityLocator.navigate({ - filters: { - timeRange: { - from: from ?? 'now-24h', - to: to ?? 'now', - refresh, - }, - }, - }); - - onFinishAction(); // This allows to return focus back to the app menu DOM node + + onFinishAction(); + }, + }, + }); + } +}; + +const registerCustomThresholdRuleAction = ( + registry: AppMenuRegistry, + { data, triggersActionsUi }: ProfileProviderServices, + { dataView }: AppMenuExtensionParams +) => { + registry.registerCustomActionUnderSubmenu(AppMenuActionId.alerts, { + // It's also possible to override the submenu actions by using the same id + // as `AppMenuActionId.createRule` or `AppMenuActionId.manageRulesAndConnectors` + id: AppMenuActionId.createRule, + type: AppMenuActionType.custom, + order: 101, + controlProps: { + label: 'Create custom threshold rule', + iconType: 'visGauge', + testId: 'custom-threshold-rule', + onClick: ({ onFinishAction }) => { + const index = dataView?.getIndexPattern(); + const { filters, query } = data.query.getState(); + + return triggersActionsUi.getAddRuleFlyout({ + consumer: 'logs', + ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, + canChangeTrigger: false, + initialValues: { + params: { + searchConfiguration: { + index, + query, + filter: filters, }, }, - }); - } - - // This example shows how to add a custom action under the Alerts submenu - registry.registerCustomActionUnderSubmenu(AppMenuActionId.alerts, { - // It's also possible to override the submenu actions by using the same id - // as `AppMenuActionId.createRule` or `AppMenuActionId.manageRulesAndConnectors` - id: AppMenuActionId.createRule, - type: AppMenuActionType.custom, - order: 101, - controlProps: { - label: 'Create custom threshold rule', - iconType: 'visGauge', - testId: 'custom-threshold-rule', - onClick: ({ onFinishAction }) => { - const index = dataView?.getIndexPattern(); - - return triggersActionsUi.getAddRuleFlyout({ - consumer: 'logs', - ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, - canChangeTrigger: false, - initialValues: { - params: { - searchConfiguration: { - index, - // query: getQuery(logsExplorerState.query), - // filter: getDiscoverFiltersFromState( - // index.id, - // logsExplorerState.filters, - // logsExplorerState.controls - // ), - }, - }, - }, - onClose: onFinishAction, - }); - }, }, + onClose: onFinishAction, }); + }, + }, + }); +}; + +const registerCreateSLOAction = ( + registry: AppMenuRegistry, + { data, discoverShared }: ProfileProviderServices, + { dataView, isEsqlMode }: AppMenuExtensionParams +) => { + const sloFeature = discoverShared.features.registry.getById('observability-create-slo'); + + if (sloFeature) { + registry.registerCustomActionUnderSubmenu(AppMenuActionId.alerts, { + // It's also possible to override the submenu actions by using the same id + // as `AppMenuActionId.createRule` or `AppMenuActionId.manageRulesAndConnectors` + id: 'create-slo', + type: AppMenuActionType.custom, + order: 102, + controlProps: { + label: 'Create SLO', + iconType: 'bell', + testId: 'create-slo', + onClick: ({ onFinishAction }) => { + const index = dataView?.getIndexPattern(); + const timestampField = dataView?.timeFieldName; + const { filters, query } = data.query.getState(); + + const filter = isEsqlMode + ? {} + : { + kqlQuery: isOfQueryType(query) ? query.query : '', + filters, + }; - registry.registerCustomActionUnderSubmenu(AppMenuActionId.alerts, { - // It's also possible to override the submenu actions by using the same id - // as `AppMenuActionId.createRule` or `AppMenuActionId.manageRulesAndConnectors` - id: 'create-slo', - type: AppMenuActionType.custom, - order: 102, - controlProps: { - label: 'Create SLO', - iconType: 'bell', - testId: 'create-slo', - onClick: ({ onFinishAction }) => { - const index = dataView?.getIndexPattern(); - - return slo.getCreateSLOFlyout({ - initialValues: { - indicator: { - type: 'sli.kql.custom', - params: { - index, - timestampField: dataView?.timeFieldName, - // filter: { - // kqlQuery: query, - // filters: getDiscoverFiltersFromState( - // dataView.id, - // logsExplorerState.filters, - // logsExplorerState.controls - // ), - // }, - }, - }, - // groupBy: logsExplorerState.chart.breakdownField ?? undefined, + return sloFeature.createSLOFlyout({ + initialValues: { + indicator: { + type: 'sli.kql.custom', + params: { + index, + timestampField, + filter, }, - onClose: onFinishAction, - }); + }, }, - }, - }); - - return prevValue.appMenuRegistry(registry); + onClose: onFinishAction, + }); + }, }, - }; - }; + }); + } +}; diff --git a/src/plugins/discover/public/types.ts b/src/plugins/discover/public/types.ts index 8926e19ecf744..af1b2be2ac25d 100644 --- a/src/plugins/discover/public/types.ts +++ b/src/plugins/discover/public/types.ts @@ -42,6 +42,7 @@ import type { AiopsPluginStart } from '@kbn/aiops-plugin/public'; import type { DataVisualizerPluginStart } from '@kbn/data-visualizer-plugin/public'; import type { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/public'; import type { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/public'; +import { DiscoverSharedPublicStart } from '@kbn/discover-shared-plugin/public'; import { DiscoverAppLocator } from '../common'; import { DiscoverCustomizationContext } from './customizations'; import { type DiscoverContainerProps } from './components/discover_container'; @@ -151,6 +152,7 @@ export interface DiscoverStartPlugins { dataViewFieldEditor: IndexPatternFieldEditorStart; dataViews: DataViewsServicePublic; dataVisualizer?: DataVisualizerPluginStart; + discoverShared: DiscoverSharedPublicStart; embeddable: EmbeddableStart; expressions: ExpressionsStart; fieldFormats: FieldFormatsStart; diff --git a/src/plugins/discover_shared/public/services/discover_features/types.ts b/src/plugins/discover_shared/public/services/discover_features/types.ts index ebfa3970e96e4..8383124a760a9 100644 --- a/src/plugins/discover_shared/public/services/discover_features/types.ts +++ b/src/plugins/discover_shared/public/services/discover_features/types.ts @@ -30,8 +30,13 @@ export interface ObservabilityLogsAIAssistantFeature { render: (deps: ObservabilityLogsAIAssistantFeatureRenderDeps) => JSX.Element; } +export interface ObservabilityCreateSLOFeature { + id: 'observability-create-slo'; + createSLOFlyout: (props: any) => JSX.Element; +} + // This should be a union of all the available client features. -export type DiscoverFeature = ObservabilityLogsAIAssistantFeature; +export type DiscoverFeature = ObservabilityLogsAIAssistantFeature | ObservabilityCreateSLOFeature; /** * Service types diff --git a/x-pack/plugins/observability_solution/slo/kibana.jsonc b/x-pack/plugins/observability_solution/slo/kibana.jsonc index c1054089c508a..dfa28b44f769e 100644 --- a/x-pack/plugins/observability_solution/slo/kibana.jsonc +++ b/x-pack/plugins/observability_solution/slo/kibana.jsonc @@ -22,6 +22,7 @@ "dashboard", "data", "dataViews", + "discoverShared", "lens", "dataViewEditor", "dataViewFieldEditor", diff --git a/x-pack/plugins/observability_solution/slo/public/plugin.ts b/x-pack/plugins/observability_solution/slo/public/plugin.ts index 3e320238794bc..a21a7716fc984 100644 --- a/x-pack/plugins/observability_solution/slo/public/plugin.ts +++ b/x-pack/plugins/observability_solution/slo/public/plugin.ts @@ -176,17 +176,25 @@ export class SloPlugin public start(coreStart: CoreStart, pluginsStart: SloPublicPluginsStart) { const kibanaVersion = this.initContext.env.packageInfo.version; + + const getCreateSLOFlyout = getCreateSLOFlyoutLazy({ + core: coreStart, + isDev: this.initContext.env.mode.dev, + kibanaVersion, + observabilityRuleTypeRegistry: pluginsStart.observability.observabilityRuleTypeRegistry, + ObservabilityPageTemplate: pluginsStart.observabilityShared.navigation.PageTemplate, + plugins: pluginsStart, + isServerless: !!pluginsStart.serverless, + experimentalFeatures: this.experimentalFeatures, + }); + + pluginsStart.discoverShared.features.registry.register({ + id: 'observability-create-slo', + createSLOFlyout: getCreateSLOFlyout, + }); + return { - getCreateSLOFlyout: getCreateSLOFlyoutLazy({ - core: coreStart, - isDev: this.initContext.env.mode.dev, - kibanaVersion, - observabilityRuleTypeRegistry: pluginsStart.observability.observabilityRuleTypeRegistry, - ObservabilityPageTemplate: pluginsStart.observabilityShared.navigation.PageTemplate, - plugins: pluginsStart, - isServerless: !!pluginsStart.serverless, - experimentalFeatures: this.experimentalFeatures, - }), + getCreateSLOFlyout, }; } diff --git a/x-pack/plugins/observability_solution/slo/public/types.ts b/x-pack/plugins/observability_solution/slo/public/types.ts index 9e730bd429541..1d9384dcb7fec 100644 --- a/x-pack/plugins/observability_solution/slo/public/types.ts +++ b/x-pack/plugins/observability_solution/slo/public/types.ts @@ -47,6 +47,7 @@ import type { DiscoverStart } from '@kbn/discover-plugin/public'; import { DataViewFieldEditorStart } from '@kbn/data-view-field-editor-plugin/public'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import { DiscoverSharedPublicStart } from '@kbn/discover-shared-plugin/public'; import { SloPlugin } from './plugin'; export interface SloPublicPluginsSetup { @@ -70,6 +71,7 @@ export interface SloPublicPluginsStart { cloud?: CloudStart; dashboard: DashboardStart; dataViewEditor: DataViewEditorStart; + discoverShared: DiscoverSharedPublicStart; fieldFormats: FieldFormatsStart; observability: ObservabilityPublicStart; observabilityShared: ObservabilitySharedPluginStart; From d5e2705d96185ee20f0294bf6fd7314abcb367f0 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Tue, 5 Nov 2024 15:45:09 +0100 Subject: [PATCH 03/16] feat(discover): implement app menu for obs root profile --- .../context_awareness/__mocks__/index.tsx | 2 + .../accessors/index.ts | 1 - .../logs_data_source_profile/profile.ts | 2 - .../accessors/get_app_menu.ts | 4 +- .../accessors/index.ts | 10 ++++ .../observability_root_profile/index.ts | 10 ++++ .../profile.test.ts | 51 +++++++++++++++++++ .../observability_root_profile/profile.tsx | 28 ++++++++++ .../profile_providers/observability/types.ts | 12 +++++ .../register_profile_providers.ts | 2 + 10 files changed, 117 insertions(+), 5 deletions(-) rename src/plugins/discover/public/context_awareness/profile_providers/{common/logs_data_source_profile => observability/observability_root_profile}/accessors/get_app_menu.ts (97%) create mode 100644 src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/index.ts create mode 100644 src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/index.ts create mode 100644 src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/profile.test.ts create mode 100644 src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/profile.tsx create mode 100644 src/plugins/discover/public/context_awareness/profile_providers/observability/types.ts diff --git a/src/plugins/discover/public/context_awareness/__mocks__/index.tsx b/src/plugins/discover/public/context_awareness/__mocks__/index.tsx index 153d401cc980a..6cb880b8ec988 100644 --- a/src/plugins/discover/public/context_awareness/__mocks__/index.tsx +++ b/src/plugins/discover/public/context_awareness/__mocks__/index.tsx @@ -25,6 +25,7 @@ import { ProfileProviderServices } from '../profile_providers/profile_provider_s import { ProfilesManager } from '../profiles_manager'; import { DiscoverEBTManager } from '../../services/discover_ebt_manager'; import { createLogsContextServiceMock } from '@kbn/discover-utils/src/__mocks__'; +import { discoverSharedPluginMock } from '@kbn/discover-shared-plugin/public/mocks'; export const createContextAwarenessMocks = ({ shouldRegisterProviders = true, @@ -179,6 +180,7 @@ export const createContextAwarenessMocks = ({ const createProfileProviderServicesMock = () => { return { + discoverShared: discoverSharedPluginMock.createStartContract(), logsContextService: createLogsContextServiceMock(), } as ProfileProviderServices; }; diff --git a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/index.ts b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/index.ts index 0aecf1501b0d0..720a1add3c926 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/index.ts +++ b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/index.ts @@ -9,6 +9,5 @@ export { getRowIndicatorProvider } from './get_row_indicator_provider'; export { createGetDefaultAppState } from './get_default_app_state'; -export { createGetAppMenu } from './get_app_menu'; export { getCellRenderers } from './get_cell_renderers'; export { getRowAdditionalLeadingControls } from './get_row_additional_leading_controls'; diff --git a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/profile.ts b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/profile.ts index 74d1ddf8b5256..f2818c336bf40 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/profile.ts +++ b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/profile.ts @@ -13,7 +13,6 @@ import { getCellRenderers, getRowIndicatorProvider, getRowAdditionalLeadingControls, - createGetAppMenu, } from './accessors'; import { extractIndexPatternFrom } from '../../extract_index_pattern_from'; @@ -22,7 +21,6 @@ export const createLogsDataSourceProfileProvider = ( ): DataSourceProfileProvider => ({ profileId: 'logs-data-source-profile', profile: { - getAppMenu: createGetAppMenu(services), getCellRenderers, getRowIndicatorProvider, getRowAdditionalLeadingControls, diff --git a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_app_menu.ts b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts similarity index 97% rename from src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_app_menu.ts rename to src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts index 26c8f0e2ab296..312f62e506468 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_app_menu.ts +++ b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts @@ -17,11 +17,11 @@ import { import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { isOfQueryType } from '@kbn/es-query'; import { AppMenuExtensionParams } from '../../../..'; -import type { DataSourceProfileProvider } from '../../../../profiles'; +import type { RootProfileProvider } from '../../../../profiles'; import { ProfileProviderServices } from '../../../profile_provider_services'; export const createGetAppMenu = - (services: ProfileProviderServices): DataSourceProfileProvider['profile']['getAppMenu'] => + (services: ProfileProviderServices): RootProfileProvider['profile']['getAppMenu'] => (prev) => (params) => { const prevValue = prev(params); diff --git a/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/index.ts b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/index.ts new file mode 100644 index 0000000000000..4a719e634621e --- /dev/null +++ b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export { createGetAppMenu } from './get_app_menu'; diff --git a/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/index.ts b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/index.ts new file mode 100644 index 0000000000000..ad888b42b5610 --- /dev/null +++ b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export { createObservabilityRootProfileProvider } from './profile'; diff --git a/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/profile.test.ts b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/profile.test.ts new file mode 100644 index 0000000000000..b83afb266f2bd --- /dev/null +++ b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/profile.test.ts @@ -0,0 +1,51 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { SolutionType } from '../../../profiles'; +import { createContextAwarenessMocks } from '../../../__mocks__'; +import { createObservabilityRootProfileProvider } from './profile'; + +const mockServices = createContextAwarenessMocks().profileProviderServices; + +describe('observabilityRootProfileProvider', () => { + const observabilityRootProfileProvider = createObservabilityRootProfileProvider(mockServices); + const RESOLUTION_MATCH = { + isMatch: true, + context: { solutionType: SolutionType.Observability }, + }; + const RESOLUTION_MISMATCH = { + isMatch: false, + }; + + it('should match when the solution project is observability', () => { + expect( + observabilityRootProfileProvider.resolve({ + solutionNavId: SolutionType.Observability, + }) + ).toEqual(RESOLUTION_MATCH); + }); + + it('should NOT match when the solution project anything but observability', () => { + expect( + observabilityRootProfileProvider.resolve({ + solutionNavId: SolutionType.Default, + }) + ).toEqual(RESOLUTION_MISMATCH); + expect( + observabilityRootProfileProvider.resolve({ + solutionNavId: SolutionType.Search, + }) + ).toEqual(RESOLUTION_MISMATCH); + expect( + observabilityRootProfileProvider.resolve({ + solutionNavId: SolutionType.Security, + }) + ).toEqual(RESOLUTION_MISMATCH); + }); +}); diff --git a/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/profile.tsx b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/profile.tsx new file mode 100644 index 0000000000000..d4b10c8d0a095 --- /dev/null +++ b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/profile.tsx @@ -0,0 +1,28 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { RootProfileProvider, SolutionType } from '../../../profiles'; +import { ProfileProviderServices } from '../../profile_provider_services'; +import { createGetAppMenu } from './accessors'; + +export const createObservabilityRootProfileProvider = ( + services: ProfileProviderServices +): RootProfileProvider => ({ + profileId: 'observability-root-profile', + profile: { + getAppMenu: createGetAppMenu(services), + }, + resolve: (params) => { + if (params.solutionNavId === SolutionType.Observability) { + return { isMatch: true, context: { solutionType: SolutionType.Observability } }; + } + + return { isMatch: false }; + }, +}); diff --git a/src/plugins/discover/public/context_awareness/profile_providers/observability/types.ts b/src/plugins/discover/public/context_awareness/profile_providers/observability/types.ts new file mode 100644 index 0000000000000..dee339bcbbaee --- /dev/null +++ b/src/plugins/discover/public/context_awareness/profile_providers/observability/types.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { ProfileProviderServices } from '../profile_provider_services'; + +export type SecurityProfileProviderFactory = (services: ProfileProviderServices) => T; diff --git a/src/plugins/discover/public/context_awareness/profile_providers/register_profile_providers.ts b/src/plugins/discover/public/context_awareness/profile_providers/register_profile_providers.ts index 58ff63ca35c19..06538bdfe09d0 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/register_profile_providers.ts +++ b/src/plugins/discover/public/context_awareness/profile_providers/register_profile_providers.ts @@ -24,6 +24,7 @@ import { ProfileProviderServices, } from './profile_provider_services'; import type { DiscoverServices } from '../../build_services'; +import { createObservabilityRootProfileProvider } from './observability/observability_root_profile'; /** * Register profile providers for root, data source, and document contexts to the profile profile services @@ -118,6 +119,7 @@ export const registerEnabledProfileProviders = < const createRootProfileProviders = (providerServices: ProfileProviderServices) => [ createExampleRootProfileProvider(), createSecurityRootProfileProvider(providerServices), + createObservabilityRootProfileProvider(providerServices), ]; /** From 8c28477cf2f807630c0663869318d3b301e2e18c Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Tue, 5 Nov 2024 15:58:18 +0100 Subject: [PATCH 04/16] refactor(discover): remove add data from app menu --- .../accessors/get_app_menu.ts | 30 +------------------ 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts index 312f62e506468..f380846092af3 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts +++ b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts @@ -8,12 +8,7 @@ */ import { AppMenuActionId, AppMenuActionType, AppMenuRegistry } from '@kbn/discover-utils'; -import { - DATA_QUALITY_LOCATOR_ID, - DataQualityLocatorParams, - OBSERVABILITY_ONBOARDING_LOCATOR, - ObservabilityOnboardingLocatorParams, -} from '@kbn/deeplinks-observability'; +import { DATA_QUALITY_LOCATOR_ID, DataQualityLocatorParams } from '@kbn/deeplinks-observability'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { isOfQueryType } from '@kbn/es-query'; import { AppMenuExtensionParams } from '../../../..'; @@ -29,7 +24,6 @@ export const createGetAppMenu = return { appMenuRegistry: (registry) => { // Register custom link actions - registerOnboardingLink(registry, services); registerDatasetQualityLink(registry, services); // Register alerts sub menu actions registerCreateSLOAction(registry, services, params); @@ -40,28 +34,6 @@ export const createGetAppMenu = }; }; -const registerOnboardingLink = (registry: AppMenuRegistry, { share }: ProfileProviderServices) => { - const onboardingLocator = share?.url.locators.get( - OBSERVABILITY_ONBOARDING_LOCATOR - ); - - if (onboardingLocator) { - registry.registerCustomAction({ - id: 'add-data-action', - type: AppMenuActionType.custom, - controlProps: { - label: 'Add data', - testId: 'add-data-action', - onClick: ({ onFinishAction }) => { - onboardingLocator.navigate({ category: 'logs' }); - - onFinishAction(); - }, - }, - }); - } -}; - const registerDatasetQualityLink = ( registry: AppMenuRegistry, { share, timefilter }: ProfileProviderServices From e9a679a33a56154ecb661dba108c713b420f04f9 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Tue, 5 Nov 2024 16:02:46 +0100 Subject: [PATCH 05/16] refactor(discover): update types --- src/plugins/discover/public/types.ts | 1 - .../public/components/dataset_quality_link.tsx | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/plugins/discover/public/types.ts b/src/plugins/discover/public/types.ts index af1b2be2ac25d..2ef380db98703 100644 --- a/src/plugins/discover/public/types.ts +++ b/src/plugins/discover/public/types.ts @@ -174,5 +174,4 @@ export interface DiscoverStartPlugins { unifiedSearch: UnifiedSearchPublicPluginStart; urlForwarding: UrlForwardingStart; usageCollection?: UsageCollectionSetup; - slo: SloPublicStart; } diff --git a/x-pack/plugins/observability_solution/observability_logs_explorer/public/components/dataset_quality_link.tsx b/x-pack/plugins/observability_solution/observability_logs_explorer/public/components/dataset_quality_link.tsx index bd754545cca06..05c74a9a1c82a 100644 --- a/x-pack/plugins/observability_solution/observability_logs_explorer/public/components/dataset_quality_link.tsx +++ b/x-pack/plugins/observability_solution/observability_logs_explorer/public/components/dataset_quality_link.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiHeaderLink, EuiLink } from '@elastic/eui'; +import { EuiHeaderLink } from '@elastic/eui'; import { LogsExplorerPublicState } from '@kbn/logs-explorer-plugin/public'; import { getRouterLinkProps } from '@kbn/router-utils'; import { LocatorPublic } from '@kbn/share-plugin/public'; @@ -92,14 +92,13 @@ export const DatasetQualityLink = React.memo( }); return ( - {datasetQualityLinkTitle} - + ); } ); From 6cde32f7cc28d789e1fa7644ac79401d7d2846bd Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Tue, 5 Nov 2024 16:04:00 +0100 Subject: [PATCH 06/16] refactor(discover): remove mock change --- .../discover/public/context_awareness/__mocks__/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/plugins/discover/public/context_awareness/__mocks__/index.tsx b/src/plugins/discover/public/context_awareness/__mocks__/index.tsx index 6cb880b8ec988..153d401cc980a 100644 --- a/src/plugins/discover/public/context_awareness/__mocks__/index.tsx +++ b/src/plugins/discover/public/context_awareness/__mocks__/index.tsx @@ -25,7 +25,6 @@ import { ProfileProviderServices } from '../profile_providers/profile_provider_s import { ProfilesManager } from '../profiles_manager'; import { DiscoverEBTManager } from '../../services/discover_ebt_manager'; import { createLogsContextServiceMock } from '@kbn/discover-utils/src/__mocks__'; -import { discoverSharedPluginMock } from '@kbn/discover-shared-plugin/public/mocks'; export const createContextAwarenessMocks = ({ shouldRegisterProviders = true, @@ -180,7 +179,6 @@ export const createContextAwarenessMocks = ({ const createProfileProviderServicesMock = () => { return { - discoverShared: discoverSharedPluginMock.createStartContract(), logsContextService: createLogsContextServiceMock(), } as ProfileProviderServices; }; From b32046d1462b0007209a8aafe5683b7dcaf864ac Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Tue, 5 Nov 2024 16:07:30 +0100 Subject: [PATCH 07/16] refactor(discover): minor type update --- .../observability_root_profile/accessors/get_app_menu.ts | 4 ---- .../public/services/discover_features/types.ts | 5 ++++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts index f380846092af3..391fa6be1424e 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts +++ b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts @@ -75,8 +75,6 @@ const registerCustomThresholdRuleAction = ( { dataView }: AppMenuExtensionParams ) => { registry.registerCustomActionUnderSubmenu(AppMenuActionId.alerts, { - // It's also possible to override the submenu actions by using the same id - // as `AppMenuActionId.createRule` or `AppMenuActionId.manageRulesAndConnectors` id: AppMenuActionId.createRule, type: AppMenuActionType.custom, order: 101, @@ -117,8 +115,6 @@ const registerCreateSLOAction = ( if (sloFeature) { registry.registerCustomActionUnderSubmenu(AppMenuActionId.alerts, { - // It's also possible to override the submenu actions by using the same id - // as `AppMenuActionId.createRule` or `AppMenuActionId.manageRulesAndConnectors` id: 'create-slo', type: AppMenuActionType.custom, order: 102, diff --git a/src/plugins/discover_shared/public/services/discover_features/types.ts b/src/plugins/discover_shared/public/services/discover_features/types.ts index 8383124a760a9..72eb2aabaddc7 100644 --- a/src/plugins/discover_shared/public/services/discover_features/types.ts +++ b/src/plugins/discover_shared/public/services/discover_features/types.ts @@ -32,7 +32,10 @@ export interface ObservabilityLogsAIAssistantFeature { export interface ObservabilityCreateSLOFeature { id: 'observability-create-slo'; - createSLOFlyout: (props: any) => JSX.Element; + createSLOFlyout: (props: { + onClose: () => void; + initialValues: Record; + }) => JSX.Element; } // This should be a union of all the available client features. From 6b2480d4c6fb992b1978e3eed12854e786561371 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Tue, 5 Nov 2024 16:25:17 +0100 Subject: [PATCH 08/16] refactor(discover): update tsconfig --- src/plugins/discover/tsconfig.json | 3 ++- x-pack/plugins/observability_solution/slo/tsconfig.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/discover/tsconfig.json b/src/plugins/discover/tsconfig.json index 72d5594ba40f0..1bb3aa10acce0 100644 --- a/src/plugins/discover/tsconfig.json +++ b/src/plugins/discover/tsconfig.json @@ -98,7 +98,8 @@ "@kbn/logs-data-access-plugin", "@kbn/core-lifecycle-browser", "@kbn/discover-contextual-components", - "@kbn/esql-ast" + "@kbn/esql-ast", + "@kbn/discover-shared-plugin" ], "exclude": [ "target/**/*" diff --git a/x-pack/plugins/observability_solution/slo/tsconfig.json b/x-pack/plugins/observability_solution/slo/tsconfig.json index 3d968c00e1e1f..9391cc9962b58 100644 --- a/x-pack/plugins/observability_solution/slo/tsconfig.json +++ b/x-pack/plugins/observability_solution/slo/tsconfig.json @@ -96,6 +96,7 @@ "@kbn/core-application-browser", "@kbn/core-theme-browser", "@kbn/ebt-tools", - "@kbn/observability-alerting-rule-utils" + "@kbn/observability-alerting-rule-utils", + "@kbn/discover-shared-plugin" ] } From 250638f865839c535f8ba5af40a528ee2ffbd288 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Wed, 6 Nov 2024 12:27:04 +0100 Subject: [PATCH 09/16] tests(discover): skip search alert test for oblt projects --- .../common/discover_ml_uptime/discover/search_source_alert.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test_serverless/functional/test_suites/common/discover_ml_uptime/discover/search_source_alert.ts b/x-pack/test_serverless/functional/test_suites/common/discover_ml_uptime/discover/search_source_alert.ts index 69db169209d59..d4653ca02f6f1 100644 --- a/x-pack/test_serverless/functional/test_suites/common/discover_ml_uptime/discover/search_source_alert.ts +++ b/x-pack/test_serverless/functional/test_suites/common/discover_ml_uptime/discover/search_source_alert.ts @@ -367,7 +367,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('Search source Alert', function () { // see details: https://github.com/elastic/kibana/issues/193842 - this.tags(['failsOnMKI']); + this.tags(['failsOnMKI', 'skipSvlOblt']); before(async () => { await security.testUser.setRoles(['discover_alert']); await PageObjects.svlCommonPage.loginAsAdmin(); From 0553ddc0d2dc47da07a93e17509d0e0e45daa92a Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Wed, 6 Nov 2024 14:33:22 +0100 Subject: [PATCH 10/16] refactor(discover): remove unused file --- .../profile_providers/observability/types.ts | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 src/plugins/discover/public/context_awareness/profile_providers/observability/types.ts diff --git a/src/plugins/discover/public/context_awareness/profile_providers/observability/types.ts b/src/plugins/discover/public/context_awareness/profile_providers/observability/types.ts deleted file mode 100644 index dee339bcbbaee..0000000000000 --- a/src/plugins/discover/public/context_awareness/profile_providers/observability/types.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { ProfileProviderServices } from '../profile_provider_services'; - -export type SecurityProfileProviderFactory = (services: ProfileProviderServices) => T; From 8c073e23cff9d1566718a93645c8a522cf2e9980 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Thu, 7 Nov 2024 09:43:18 +0100 Subject: [PATCH 11/16] refactor(discover): use i18n --- .../accessors/get_app_menu.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts index 391fa6be1424e..46c5fd6c8ae8b 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts +++ b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts @@ -11,6 +11,7 @@ import { AppMenuActionId, AppMenuActionType, AppMenuRegistry } from '@kbn/discov import { DATA_QUALITY_LOCATOR_ID, DataQualityLocatorParams } from '@kbn/deeplinks-observability'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { isOfQueryType } from '@kbn/es-query'; +import { i18n } from '@kbn/i18n'; import { AppMenuExtensionParams } from '../../../..'; import type { RootProfileProvider } from '../../../../profiles'; import { ProfileProviderServices } from '../../../profile_provider_services'; @@ -46,7 +47,9 @@ const registerDatasetQualityLink = ( id: 'dataset-quality-link', type: AppMenuActionType.custom, controlProps: { - label: 'Data sets', + label: i18n.translate('discover.observabilitySolution.appMenu.datasets', { + defaultMessage: 'Data sets', + }), testId: 'dataset-quality-link', onClick: ({ onFinishAction }) => { const refresh = timefilter.getRefreshInterval(); @@ -79,7 +82,9 @@ const registerCustomThresholdRuleAction = ( type: AppMenuActionType.custom, order: 101, controlProps: { - label: 'Create custom threshold rule', + label: i18n.translate('discover.observabilitySolution.appMenu.customThresholdRule', { + defaultMessage: 'Create custom threshold rule', + }), iconType: 'visGauge', testId: 'custom-threshold-rule', onClick: ({ onFinishAction }) => { @@ -119,7 +124,9 @@ const registerCreateSLOAction = ( type: AppMenuActionType.custom, order: 102, controlProps: { - label: 'Create SLO', + label: i18n.translate('discover.observabilitySolution.appMenu.slo', { + defaultMessage: 'Create SLO', + }), iconType: 'bell', testId: 'create-slo', onClick: ({ onFinishAction }) => { From cb44a07cdbbbd11f855b1eb58da3352487c1889e Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Thu, 7 Nov 2024 11:02:18 +0100 Subject: [PATCH 12/16] test(discover): add o11y profile FT for app menu --- .../accessors/get_app_menu.ts | 6 +- .../context_awareness/_get_app_menu.ts | 81 +++++++++++++++++++ .../discover/context_awareness/index.ts | 40 +++++++++ .../test_suites/observability/index.ts | 1 + 4 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 x-pack/test_serverless/functional/test_suites/observability/discover/context_awareness/_get_app_menu.ts create mode 100644 x-pack/test_serverless/functional/test_suites/observability/discover/context_awareness/index.ts diff --git a/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts index 46c5fd6c8ae8b..3062e66489b9c 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts +++ b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts @@ -50,7 +50,7 @@ const registerDatasetQualityLink = ( label: i18n.translate('discover.observabilitySolution.appMenu.datasets', { defaultMessage: 'Data sets', }), - testId: 'dataset-quality-link', + testId: 'discoverAppMenuDatasetQualityLink', onClick: ({ onFinishAction }) => { const refresh = timefilter.getRefreshInterval(); const { from, to } = timefilter.getTime(); @@ -86,7 +86,7 @@ const registerCustomThresholdRuleAction = ( defaultMessage: 'Create custom threshold rule', }), iconType: 'visGauge', - testId: 'custom-threshold-rule', + testId: 'discoverAppMenuCustomThresholdRule', onClick: ({ onFinishAction }) => { const index = dataView?.getIndexPattern(); const { filters, query } = data.query.getState(); @@ -128,7 +128,7 @@ const registerCreateSLOAction = ( defaultMessage: 'Create SLO', }), iconType: 'bell', - testId: 'create-slo', + testId: 'discoverAppMenuCreateSlo', onClick: ({ onFinishAction }) => { const index = dataView?.getIndexPattern(); const timestampField = dataView?.timeFieldName; diff --git a/x-pack/test_serverless/functional/test_suites/observability/discover/context_awareness/_get_app_menu.ts b/x-pack/test_serverless/functional/test_suites/observability/discover/context_awareness/_get_app_menu.ts new file mode 100644 index 0000000000000..2be17df28d12f --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/observability/discover/context_awareness/_get_app_menu.ts @@ -0,0 +1,81 @@ +/* + * 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 expect from '@kbn/expect'; +import kbnRison from '@kbn/rison'; +import type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const { common, svlCommonPage } = getPageObjects([ + 'common', + 'timePicker', + 'discover', + 'header', + 'timePicker', + 'svlCommonPage', + ]); + const browser = getService('browser'); + const esArchiver = getService('esArchiver'); + const retry = getService('retry'); + const testSubjects = getService('testSubjects'); + + describe('extension getAppMenu', () => { + before(async () => { + await svlCommonPage.loginAsAdmin(); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + }); + + after(async () => { + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + }); + + beforeEach(async () => { + const state = kbnRison.encode({ + dataSource: { type: 'esql' }, + query: { esql: 'from logstash* | sort @timestamp desc' }, + }); + await common.navigateToActualUrl('discover', `?_a=${state}`, { + ensureCurrentUrl: false, + }); + }); + + it('should display a "Add data" link to navigate to the onboading page', async () => { + const link = await testSubjects.find('discoverAppMenuDatasetQualityLink'); + await link.click(); + + await retry.try(async () => { + const url = await browser.getCurrentUrl(); + expect(url).to.contain(`/app/management/data/data_quality`); + }); + }); + + it('should display a "Create custom threshold rule" action under the Alerts menu to create an o11y alert', async () => { + const alertsButton = await testSubjects.find('discoverAlertsButton'); + await alertsButton.click(); + + const createRuleButton = await testSubjects.find('discoverAppMenuCustomThresholdRule'); + await createRuleButton.click(); + + const ruleTitleElement = await testSubjects.find('selectedRuleTypeTitle'); + + await retry.try(async () => { + expect(await ruleTitleElement.getVisibleText()).to.equal('Custom threshold'); + }); + }); + + it('should display a "Create SLO" action under the Alerts menu to create an o11y alert', async () => { + const alertsButton = await testSubjects.find('discoverAlertsButton'); + await alertsButton.click(); + + const createSLOButton = await testSubjects.find('discoverAppMenuCreateSlo'); + await createSLOButton.click(); + + const sloTitleElement = await testSubjects.find('addSLOFlyoutTitle'); + expect(await sloTitleElement.getVisibleText()).to.equal('Create SLO'); + }); + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/observability/discover/context_awareness/index.ts b/x-pack/test_serverless/functional/test_suites/observability/discover/context_awareness/index.ts new file mode 100644 index 0000000000000..c8277b273f428 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/observability/discover/context_awareness/index.ts @@ -0,0 +1,40 @@ +/* + * 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 { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ getService, getPageObjects, loadTestFile }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const PageObjects = getPageObjects(['timePicker', 'svlCommonPage']); + const from = '2024-06-10T14:00:00.000Z'; + const to = '2024-06-10T16:30:00.000Z'; + + describe('discover/observabilitySolution/context_awareness', function () { + this.tags(['esGate']); + + before(async () => { + await esArchiver.load('test/functional/fixtures/es_archiver/discover/context_awareness'); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/discover/context_awareness' + ); + await kibanaServer.uiSettings.update({ + 'timepicker:timeDefaults': `{ "from": "${from}", "to": "${to}"}`, + }); + }); + + after(async () => { + await esArchiver.unload('test/functional/fixtures/es_archiver/discover/context_awareness'); + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/discover/context_awareness' + ); + await PageObjects.timePicker.resetDefaultAbsoluteRangeViaUiSettings(); + }); + + loadTestFile(require.resolve('./_get_app_menu')); + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/observability/index.ts b/x-pack/test_serverless/functional/test_suites/observability/index.ts index 566f2b8e6854e..0885a319636b1 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/index.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/index.ts @@ -15,6 +15,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./navigation')); loadTestFile(require.resolve('./observability_logs_explorer')); loadTestFile(require.resolve('./dataset_quality')); + loadTestFile(require.resolve('./discover/context_awareness')); loadTestFile(require.resolve('./onboarding')); loadTestFile(require.resolve('./rules/rules_list')); loadTestFile(require.resolve('./cases')); From 58eba92eeaa431bff16c6ba4391cd4511f5c291b Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Thu, 7 Nov 2024 14:46:37 +0100 Subject: [PATCH 13/16] fix(discover): use data view specs for alert rule --- .../observability_root_profile/accessors/get_app_menu.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts index 3062e66489b9c..84278b4a44eb0 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts +++ b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts @@ -88,7 +88,7 @@ const registerCustomThresholdRuleAction = ( iconType: 'visGauge', testId: 'discoverAppMenuCustomThresholdRule', onClick: ({ onFinishAction }) => { - const index = dataView?.getIndexPattern(); + const index = dataView?.toMinimalSpec(); const { filters, query } = data.query.getState(); return triggersActionsUi.getAddRuleFlyout({ From b7a61fb7f18ac228514bd4f025fdf4351ecf70bf Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 13 Nov 2024 09:38:39 +0000 Subject: [PATCH 14/16] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- x-pack/plugins/observability_solution/slo/public/plugin.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/observability_solution/slo/public/plugin.ts b/x-pack/plugins/observability_solution/slo/public/plugin.ts index 3109cb9b3ea44..225024cca6e67 100644 --- a/x-pack/plugins/observability_solution/slo/public/plugin.ts +++ b/x-pack/plugins/observability_solution/slo/public/plugin.ts @@ -212,12 +212,12 @@ export class SLOPlugin experimentalFeatures: this.experimentalFeatures, sloClient, }); - + const getCreateSLOFlyout = lazyWithContextProviders( lazy(() => import('./pages/slo_edit/shared_flyout/slo_add_form_flyout')), { spinnerSize: 'm' } - ) - + ); + pluginsStart.discoverShared.features.registry.register({ id: 'observability-create-slo', createSLOFlyout: getCreateSLOFlyout, From c22e5b31ce061f51ebfbe35d3c6ee0825559f276 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Wed, 13 Nov 2024 10:39:18 +0100 Subject: [PATCH 15/16] fix(slo): conflicts and improve typing --- .../public/services/discover_features/types.ts | 2 +- .../plugins/observability_solution/slo/public/plugin.ts | 8 ++++---- .../slo/public/utils/get_lazy_with_context_providers.tsx | 5 ++++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/plugins/discover_shared/public/services/discover_features/types.ts b/src/plugins/discover_shared/public/services/discover_features/types.ts index 72eb2aabaddc7..cdf78b3335507 100644 --- a/src/plugins/discover_shared/public/services/discover_features/types.ts +++ b/src/plugins/discover_shared/public/services/discover_features/types.ts @@ -35,7 +35,7 @@ export interface ObservabilityCreateSLOFeature { createSLOFlyout: (props: { onClose: () => void; initialValues: Record; - }) => JSX.Element; + }) => React.ReactNode; } // This should be a union of all the available client features. diff --git a/x-pack/plugins/observability_solution/slo/public/plugin.ts b/x-pack/plugins/observability_solution/slo/public/plugin.ts index 3109cb9b3ea44..9a1b5f3267b86 100644 --- a/x-pack/plugins/observability_solution/slo/public/plugin.ts +++ b/x-pack/plugins/observability_solution/slo/public/plugin.ts @@ -212,13 +212,13 @@ export class SLOPlugin experimentalFeatures: this.experimentalFeatures, sloClient, }); - + const getCreateSLOFlyout = lazyWithContextProviders( lazy(() => import('./pages/slo_edit/shared_flyout/slo_add_form_flyout')), { spinnerSize: 'm' } - ) - - pluginsStart.discoverShared.features.registry.register({ + ); + + plugins.discoverShared.features.registry.register({ id: 'observability-create-slo', createSLOFlyout: getCreateSLOFlyout, }); diff --git a/x-pack/plugins/observability_solution/slo/public/utils/get_lazy_with_context_providers.tsx b/x-pack/plugins/observability_solution/slo/public/utils/get_lazy_with_context_providers.tsx index a43aa9e7bff59..49bb461b97af0 100644 --- a/x-pack/plugins/observability_solution/slo/public/utils/get_lazy_with_context_providers.tsx +++ b/x-pack/plugins/observability_solution/slo/public/utils/get_lazy_with_context_providers.tsx @@ -47,7 +47,10 @@ export const getLazyWithContextProviders = experimentalFeatures, sloClient, }: Props) => - (LazyComponent: React.LazyExoticComponent, options?: Options): React.FunctionComponent => { + >( + LazyComponent: React.LazyExoticComponent, + options?: Options + ): React.FunctionComponent> => { const { spinnerSize = 'xl' } = options ?? {}; const queryClient = new QueryClient(); return (props) => ( From e333f5b3a9dd7ff7a0a88bde9f24390f3819941d Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Wed, 13 Nov 2024 11:24:20 +0100 Subject: [PATCH 16/16] fix(discover): broken slo creation due to filters --- .../observability_root_profile/accessors/get_app_menu.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts index 84278b4a44eb0..759765e93767f 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts +++ b/src/plugins/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.ts @@ -132,13 +132,13 @@ const registerCreateSLOAction = ( onClick: ({ onFinishAction }) => { const index = dataView?.getIndexPattern(); const timestampField = dataView?.timeFieldName; - const { filters, query } = data.query.getState(); + const { filters, query: kqlQuery } = data.query.getState(); const filter = isEsqlMode ? {} : { - kqlQuery: isOfQueryType(query) ? query.query : '', - filters, + kqlQuery: isOfQueryType(kqlQuery) ? kqlQuery.query : '', + filters: filters?.map(({ meta, query }) => ({ meta, query })), }; return sloFeature.createSLOFlyout({