diff --git a/x-pack/plugins/observability_solution/infra/public/apps/logs_app.tsx b/x-pack/plugins/observability_solution/infra/public/apps/logs_app.tsx index 329e059288e3e..9d5583b0ecf4c 100644 --- a/x-pack/plugins/observability_solution/infra/public/apps/logs_app.tsx +++ b/x-pack/plugins/observability_solution/infra/public/apps/logs_app.tsx @@ -6,13 +6,19 @@ */ import { History } from 'history'; -import { CoreStart } from '@kbn/core/public'; -import React from 'react'; +import { AppStatus, CoreStart } from '@kbn/core/public'; +import React, { useMemo } from 'react'; import ReactDOM from 'react-dom'; import { Router, Routes, Route } from '@kbn/shared-ux-router'; import { AppMountParameters } from '@kbn/core/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; -import { AllDatasetsLocatorParams, ALL_DATASETS_LOCATOR_ID } from '@kbn/deeplinks-observability'; +import { + AllDatasetsLocatorParams, + ALL_DATASETS_LOCATOR_ID, + OBSERVABILITY_LOGS_EXPLORER_APP_ID, +} from '@kbn/deeplinks-observability'; +import useObservable from 'react-use/lib/useObservable'; +import { map } from 'rxjs'; import { LinkToLogsPage } from '../pages/link_to/link_to_logs'; import { LogsPage } from '../pages/logs'; import { InfraClientStartDeps, InfraClientStartExports } from '../types'; @@ -57,7 +63,22 @@ const LogsApp: React.FC<{ storage: Storage; theme$: AppMountParameters['theme$']; }> = ({ core, history, pluginStart, plugins, setHeaderActionMenu, storage, theme$ }) => { - const { logs, discover, fleet } = core.application.capabilities; + const { logs } = core.application.capabilities; + + const isLogsExplorerAppAccessible = useObservable( + useMemo( + () => + core.application.applications$.pipe( + map( + (apps) => + (apps.get(OBSERVABILITY_LOGS_EXPLORER_APP_ID)?.status ?? AppStatus.inaccessible) === + AppStatus.accessible + ) + ), + [core.application.applications$] + ), + false + ); return ( @@ -74,7 +95,7 @@ const LogsApp: React.FC<{ toastsService={core.notifications.toasts} > - {Boolean(discover?.show && fleet?.read) && ( + {isLogsExplorerAppAccessible && ( { const { - services: { share }, + services: { share, application }, } = useKibanaContextForPlugin(); + const isLogsExplorerAppAccessible = useObservable( + useMemo( + () => + application.applications$.pipe( + map( + (apps) => + (apps.get(OBSERVABILITY_LOGS_EXPLORER_APP_ID)?.status ?? AppStatus.inaccessible) === + AppStatus.accessible + ) + ), + [application.applications$] + ), + false + ); + const { dismissalStorageKey, message } = pageConfigurations[page]; const [isDismissed, setDismissed] = useLocalStorage(dismissalStorageKey, false); - if (isDismissed) { + if (isDismissed || !isLogsExplorerAppAccessible) { return null; } + const allDatasetLocator = + share.url.locators.get(ALL_DATASETS_LOCATOR_ID); + return ( fill data-test-subj="infraLogsDeprecationCalloutTryLogsExplorerButton" color="warning" - {...getLogsExplorerLinkProps(share)} + {...getLogsExplorerLinkProps(allDatasetLocator!)} > {i18n.translate('xpack.infra.logsDeprecationCallout.tryLogsExplorerButtonLabel', { defaultMessage: 'Try Logs Explorer', @@ -81,9 +107,7 @@ export const LogsDeprecationCallout = ({ page }: LogsDeprecationCalloutProps) => ); }; -const getLogsExplorerLinkProps = (share: SharePublicStart) => { - const locator = share.url.locators.get(ALL_DATASETS_LOCATOR_ID)!; - +const getLogsExplorerLinkProps = (locator: LocatorPublic) => { return getRouterLinkProps({ href: locator.getRedirectUrl({}), onClick: () => locator.navigate({}), diff --git a/x-pack/plugins/observability_solution/infra/public/plugin.ts b/x-pack/plugins/observability_solution/infra/public/plugin.ts index daaa3510e1660..7b32df012a192 100644 --- a/x-pack/plugins/observability_solution/infra/public/plugin.ts +++ b/x-pack/plugins/observability_solution/infra/public/plugin.ts @@ -13,6 +13,8 @@ import { DEFAULT_APP_CATEGORIES, PluginInitializerContext, AppDeepLinkLocations, + AppStatus, + ApplicationStart, } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { enableInfrastructureHostsView } from '@kbn/observability-plugin/public'; @@ -21,7 +23,7 @@ import { MetricsExplorerLocatorParams, ObservabilityTriggerId, } from '@kbn/observability-shared-plugin/common'; -import { BehaviorSubject, combineLatest, from } from 'rxjs'; +import { BehaviorSubject, combineLatest, distinctUntilChanged, from, of, switchMap } from 'rxjs'; import { map } from 'rxjs'; import type { EmbeddableApiContext } from '@kbn/presentation-publishing'; import { apiCanAddNewPanel } from '@kbn/presentation-containers'; @@ -35,6 +37,7 @@ import { } 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 { OBSERVABILITY_LOGS_EXPLORER_APP_ID } from '@kbn/deeplinks-observability/constants'; import type { InfraPublicConfig } from '../common/plugin_config_types'; import { createInventoryMetricRuleType } from './alerting/inventory'; import { createLogThresholdRuleType } from './alerting/log_threshold'; @@ -131,76 +134,75 @@ export class Plugin implements InfraClientPluginClass { messageFields: this.config.sources?.default?.fields?.message, }); - const startDep$AndHostViewFlag$ = combineLatest([ + const startDep$AndAccessibleFlag$ = combineLatest([ from(core.getStartServices()), core.settings.client.get$(enableInfrastructureHostsView), - ]); + ]).pipe( + switchMap(([[{ application }], isInfrastructureHostsViewEnabled]) => + combineLatest([ + of(application), + of(isInfrastructureHostsViewEnabled), + getLogsExplorerAccessible$(application), + ]) + ) + ); 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( - map( - ([ - [ - { - application: { capabilities }, - }, - ], - isInfrastructureHostsViewEnabled, - ]) => { - const { infrastructure, logs } = capabilities; - return [ - ...(logs.show - ? [ - { - label: logsTitle, - sortKey: 200, - entries: getLogsNavigationEntries({ - capabilities, - config: this.config, - routes: logRoutes, - }), - }, - ] - : []), - ...(infrastructure.show - ? [ - { - label: metricsTitle, - sortKey: 300, - entries: [ - { - label: inventoryTitle, - app: 'metrics', - path: '/inventory', - }, - ...(this.config.featureFlags.metricsExplorerEnabled - ? [ - { - label: metricsExplorerTitle, - app: 'metrics', - path: '/explorer', - }, - ] - : []), - ...(isInfrastructureHostsViewEnabled - ? [ - { - label: hostsTitle, - app: 'metrics', - path: '/hosts', - }, - ] - : []), - ], - }, - ] - : []), - ]; - } - ) + startDep$AndAccessibleFlag$.pipe( + map(([application, isInfrastructureHostsViewEnabled, isLogsExplorerAccessible]) => { + const { infrastructure, logs } = application.capabilities; + return [ + ...(logs.show + ? [ + { + label: logsTitle, + sortKey: 200, + entries: getLogsNavigationEntries({ + isLogsExplorerAccessible, + config: this.config, + routes: logRoutes, + }), + }, + ] + : []), + ...(infrastructure.show + ? [ + { + label: metricsTitle, + sortKey: 300, + entries: [ + { + label: inventoryTitle, + app: 'metrics', + path: '/inventory', + }, + ...(this.config.featureFlags.metricsExplorerEnabled + ? [ + { + label: metricsExplorerTitle, + app: 'metrics', + path: '/explorer', + }, + ] + : []), + ...(isInfrastructureHostsViewEnabled + ? [ + { + label: hostsTitle, + app: 'metrics', + path: '/hosts', + }, + ] + : []), + ], + }, + ] + : []), + ]; + }) ) ); @@ -333,9 +335,10 @@ export class Plugin implements InfraClientPluginClass { }, }); - startDep$AndHostViewFlag$.subscribe( - ([_startServices, isInfrastructureHostsViewEnabled]: [ - [CoreStart, InfraClientStartDeps, InfraClientStartExports], + startDep$AndAccessibleFlag$.subscribe( + ([_startServices, isInfrastructureHostsViewEnabled, _isLogsExplorerAccessible]: [ + ApplicationStart, + boolean, boolean ]) => { this.appUpdater$.next(() => ({ @@ -408,11 +411,11 @@ export class Plugin implements InfraClientPluginClass { } const getLogsNavigationEntries = ({ - capabilities, + isLogsExplorerAccessible, config, routes, }: { - capabilities: CoreStart['application']['capabilities']; + isLogsExplorerAccessible: boolean; config: InfraPublicConfig; routes: LogsAppRoutes; }) => { @@ -420,7 +423,7 @@ const getLogsNavigationEntries = ({ if (!config.featureFlags.logsUIEnabled) return entries; - if (capabilities.discover?.show && capabilities.fleet?.read) { + if (isLogsExplorerAccessible) { entries.push({ label: 'Explorer', app: 'observability-logs-explorer', @@ -440,6 +443,18 @@ const getLogsNavigationEntries = ({ return entries; }; +const getLogsExplorerAccessible$ = (application: CoreStart['application']) => { + const { applications$ } = application; + return applications$.pipe( + map( + (apps) => + (apps.get(OBSERVABILITY_LOGS_EXPLORER_APP_ID)?.status ?? AppStatus.inaccessible) === + AppStatus.accessible + ), + distinctUntilChanged() + ); +}; + const createNavEntryFromRoute = ({ path, title }: LogsRoute): NavigationEntry => ({ app: 'logs', label: title,