kibana/x-pack/plugins/observability_solution/infra/public/plugin.ts
Julia Rechkunova db1c118fa1
[8.x] [Discover] Rename Saved Search to Discover Session (#202217) (#204818)
# Backport

This will backport the following commits from `main` to `8.x`:
- [[Discover] Rename Saved Search to Discover Session
(#202217)](https://github.com/elastic/kibana/pull/202217)

<!--- Backport version: 8.9.8 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Julia
Rechkunova","email":"julia.rechkunova@elastic.co"},"sourceCommit":{"committedDate":"2024-12-18T12:45:32Z","message":"[Discover]
Rename Saved Search to Discover Session (#202217)\n\n- Closes
https://github.com/elastic/kibana/issues/174144\r\n\r\n##
Summary\r\n\r\nThis PR renames Saved Search into Discover Session in
UI.\r\n\r\n- [x] Discover\r\n- [x] Saved Objects page and modal\r\n- [x]
Docs\r\n- [x] Other occurrences \r\n\r\n<img width=\"810\"
alt=\"Screenshot 2024-12-16 at 15 20
10\"\r\nsrc=\"https://github.com/user-attachments/assets/e39083da-f496-4ed5-bbdc-8e184897fc41\"\r\n/>\r\n<img
width=\"1220\" alt=\"Screenshot 2024-12-11 at 14 40
15\"\r\nsrc=\"https://github.com/user-attachments/assets/a6dc3e29-e1a5-4304-8148-0108231cc9de\"\r\n/>\r\n<img
width=\"1476\" alt=\"Screenshot 2024-12-16 at 14 57
39\"\r\nsrc=\"https://github.com/user-attachments/assets/4b34c70e-e21a-4d82-85f2-f5a3cb7a3826\"\r\n/>\r\n\r\n\r\n###
Checklist\r\n\r\n- [x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[x]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [x] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [x] The PR
description includes the appropriate Release Notes section,\r\nand the
correct `release_note:*` label is applied per
the\r\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\r\nCo-authored-by:
wajihaparvez <wajiha.parvez@elastic.co>\r\nCo-authored-by: Davis McPhee
<davismcphee@hotmail.com>\r\nCo-authored-by: Julia Bardi
<90178898+juliaElastic@users.noreply.github.com>","sha":"40c90550f12f99f23e6b7d545c7427e30d648dab","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:enhancement","Team:Fleet","v9.0.0","Team:DataDiscovery","backport:prev-minor","ci:project-deploy-observability"],"number":202217,"url":"https://github.com/elastic/kibana/pull/202217","mergeCommit":{"message":"[Discover]
Rename Saved Search to Discover Session (#202217)\n\n- Closes
https://github.com/elastic/kibana/issues/174144\r\n\r\n##
Summary\r\n\r\nThis PR renames Saved Search into Discover Session in
UI.\r\n\r\n- [x] Discover\r\n- [x] Saved Objects page and modal\r\n- [x]
Docs\r\n- [x] Other occurrences \r\n\r\n<img width=\"810\"
alt=\"Screenshot 2024-12-16 at 15 20
10\"\r\nsrc=\"https://github.com/user-attachments/assets/e39083da-f496-4ed5-bbdc-8e184897fc41\"\r\n/>\r\n<img
width=\"1220\" alt=\"Screenshot 2024-12-11 at 14 40
15\"\r\nsrc=\"https://github.com/user-attachments/assets/a6dc3e29-e1a5-4304-8148-0108231cc9de\"\r\n/>\r\n<img
width=\"1476\" alt=\"Screenshot 2024-12-16 at 14 57
39\"\r\nsrc=\"https://github.com/user-attachments/assets/4b34c70e-e21a-4d82-85f2-f5a3cb7a3826\"\r\n/>\r\n\r\n\r\n###
Checklist\r\n\r\n- [x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[x]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [x] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [x] The PR
description includes the appropriate Release Notes section,\r\nand the
correct `release_note:*` label is applied per
the\r\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\r\nCo-authored-by:
wajihaparvez <wajiha.parvez@elastic.co>\r\nCo-authored-by: Davis McPhee
<davismcphee@hotmail.com>\r\nCo-authored-by: Julia Bardi
<90178898+juliaElastic@users.noreply.github.com>","sha":"40c90550f12f99f23e6b7d545c7427e30d648dab"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","labelRegex":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/202217","number":202217,"mergeCommit":{"message":"[Discover]
Rename Saved Search to Discover Session (#202217)\n\n- Closes
https://github.com/elastic/kibana/issues/174144\r\n\r\n##
Summary\r\n\r\nThis PR renames Saved Search into Discover Session in
UI.\r\n\r\n- [x] Discover\r\n- [x] Saved Objects page and modal\r\n- [x]
Docs\r\n- [x] Other occurrences \r\n\r\n<img width=\"810\"
alt=\"Screenshot 2024-12-16 at 15 20
10\"\r\nsrc=\"https://github.com/user-attachments/assets/e39083da-f496-4ed5-bbdc-8e184897fc41\"\r\n/>\r\n<img
width=\"1220\" alt=\"Screenshot 2024-12-11 at 14 40
15\"\r\nsrc=\"https://github.com/user-attachments/assets/a6dc3e29-e1a5-4304-8148-0108231cc9de\"\r\n/>\r\n<img
width=\"1476\" alt=\"Screenshot 2024-12-16 at 14 57
39\"\r\nsrc=\"https://github.com/user-attachments/assets/4b34c70e-e21a-4d82-85f2-f5a3cb7a3826\"\r\n/>\r\n\r\n\r\n###
Checklist\r\n\r\n- [x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[x]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [x] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [x] The PR
description includes the appropriate Release Notes section,\r\nand the
correct `release_note:*` label is applied per
the\r\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\r\nCo-authored-by:
wajihaparvez <wajiha.parvez@elastic.co>\r\nCo-authored-by: Davis McPhee
<davismcphee@hotmail.com>\r\nCo-authored-by: Julia Bardi
<90178898+juliaElastic@users.noreply.github.com>","sha":"40c90550f12f99f23e6b7d545c7427e30d648dab"}}]}]
BACKPORT-->
2024-12-19 21:38:57 +11:00

469 lines
16 KiB
TypeScript

/*
* 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 AppMountParameters,
type AppUpdater,
type CoreStart,
type AppDeepLink,
DEFAULT_APP_CATEGORIES,
PluginInitializerContext,
AppDeepLinkLocations,
AppStatus,
ApplicationStart,
} from '@kbn/core/public';
import { i18n } from '@kbn/i18n';
import { enableInfrastructureHostsView } from '@kbn/observability-plugin/public';
import {
METRICS_EXPLORER_LOCATOR_ID,
MetricsExplorerLocatorParams,
} from '@kbn/observability-shared-plugin/common';
import {
BehaviorSubject,
combineLatest,
distinctUntilChanged,
from,
of,
switchMap,
map,
firstValueFrom,
} from 'rxjs';
import type { EmbeddableApiContext } from '@kbn/presentation-publishing';
import { apiCanAddNewPanel } from '@kbn/presentation-containers';
import { IncompatibleActionError, ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public';
import { COMMON_EMBEDDABLE_GROUPING } from '@kbn/embeddable-plugin/public';
import {
ASSET_DETAILS_LOCATOR_ID,
INVENTORY_LOCATOR_ID,
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 { 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';
import { createMetricThresholdRuleType } from './alerting/metric_threshold';
import { ADD_LOG_STREAM_ACTION_ID, LOG_STREAM_EMBEDDABLE } from './components/log_stream/constants';
import { createMetricsFetchData, createMetricsHasData } from './metrics_overview_fetchers';
import { registerFeatures } from './register_feature';
import { InventoryViewsService } from './services/inventory_views';
import { MetricsExplorerViewsService } from './services/metrics_explorer_views';
import { TelemetryService } from './services/telemetry';
import type {
InfraClientCoreSetup,
InfraClientCoreStart,
InfraClientPluginClass,
InfraClientSetupDeps,
InfraClientStartDeps,
InfraClientStartExports,
} from './types';
import { getLogsHasDataFetcher, getLogsOverviewDataFetcher } from './utils/logs_overview_fetchers';
import type { LogStreamSerializedState } from './components/log_stream/types';
import {
hostsTitle,
inventoryTitle,
logsTitle,
metricsExplorerTitle,
metricsTitle,
} from './translations';
import { LogsAppRoutes, LogsRoute, getLogsAppRoutes } from './pages/logs/routes';
export class Plugin implements InfraClientPluginClass {
public config: InfraPublicConfig;
private inventoryViews: InventoryViewsService;
private metricsExplorerViews?: MetricsExplorerViewsService;
private telemetry: TelemetryService;
private kibanaVersion: string;
private isServerlessEnv: boolean;
private readonly appUpdater$ = new BehaviorSubject<AppUpdater>(() => ({}));
constructor(context: PluginInitializerContext<InfraPublicConfig>) {
this.config = context.config.get();
this.inventoryViews = new InventoryViewsService();
this.metricsExplorerViews = this.config.featureFlags.metricsExplorerEnabled
? new MetricsExplorerViewsService()
: undefined;
this.telemetry = new TelemetryService();
this.kibanaVersion = context.env.packageInfo.version;
this.isServerlessEnv = context.env.packageInfo.buildFlavor === 'serverless';
}
setup(core: InfraClientCoreSetup, pluginsSetup: InfraClientSetupDeps) {
const isLogsStreamEnabled = core.uiSettings.get(OBSERVABILITY_ENABLE_LOGS_STREAM, false);
if (pluginsSetup.home) {
registerFeatures(pluginsSetup.home);
}
const assetDetailsLocator =
pluginsSetup.share.url.locators.get<AssetDetailsLocatorParams>(ASSET_DETAILS_LOCATOR_ID);
const inventoryLocator =
pluginsSetup.share.url.locators.get<InventoryLocatorParams>(INVENTORY_LOCATOR_ID);
const metricsExplorerLocator =
pluginsSetup.share.url.locators.get<MetricsExplorerLocatorParams>(
METRICS_EXPLORER_LOCATOR_ID
);
pluginsSetup.observability.observabilityRuleTypeRegistry.register(
createInventoryMetricRuleType({ assetDetailsLocator, inventoryLocator })
);
pluginsSetup.observability.observabilityRuleTypeRegistry.register(
createMetricThresholdRuleType({ assetDetailsLocator, metricsExplorerLocator })
);
if (this.config.featureFlags.logsUIEnabled) {
// fetchData `appLink` redirects to logs explorer
pluginsSetup.observability.dashboard.register({
appName: 'infra_logs',
hasData: getLogsHasDataFetcher(core.getStartServices),
fetchData: getLogsOverviewDataFetcher(core.getStartServices),
});
}
pluginsSetup.observability.dashboard.register({
appName: 'infra_metrics',
hasData: createMetricsHasData(core.getStartServices),
fetchData: createMetricsFetchData(core.getStartServices),
});
pluginsSetup.logsShared.logViews.setLogViewsStaticConfig({
messageFields: this.config.sources?.default?.fields?.message,
});
const startDep$AndAccessibleFlag$ = combineLatest([
from(core.getStartServices()),
core.settings.client.get$<boolean>(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$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',
},
]
: []),
],
},
]
: []),
];
})
)
);
pluginsSetup.embeddable.registerReactEmbeddableFactory(LOG_STREAM_EMBEDDABLE, async () => {
const { getLogStreamEmbeddableFactory } = await import(
'./components/log_stream/log_stream_react_embeddable'
);
const [coreStart, pluginDeps, pluginStart] = await core.getStartServices();
return getLogStreamEmbeddableFactory({
coreStart,
pluginDeps,
pluginStart,
});
});
pluginsSetup.observability.observabilityRuleTypeRegistry.register(
createLogThresholdRuleType(core, pluginsSetup.share.url)
);
if (this.config.featureFlags.logsUIEnabled) {
core.application.register({
id: 'logs',
title: i18n.translate('xpack.infra.logs.pluginTitle', {
defaultMessage: 'Logs',
}),
euiIconType: 'logoObservability',
order: 8100,
appRoute: '/app/logs',
deepLinks: Object.values(logRoutes),
category: DEFAULT_APP_CATEGORIES.observability,
mount: async (params: AppMountParameters) => {
// mount callback should not use setup dependencies, get start dependencies instead
const [coreStart, plugins, pluginStart] = await core.getStartServices();
const isLogsExplorerAccessible = await firstValueFrom(
getLogsExplorerAccessible$(coreStart.application)
);
const { renderApp } = await import('./apps/logs_app');
return renderApp(coreStart, plugins, pluginStart, isLogsExplorerAccessible, params);
},
});
}
// !! Need to be kept in sync with the routes in x-pack/plugins/observability_solution/infra/public/pages/metrics/index.tsx
const getInfraDeepLinks = ({
hostsEnabled,
metricsExplorerEnabled,
}: {
hostsEnabled: boolean;
metricsExplorerEnabled: boolean;
}): AppDeepLink[] => {
const visibleIn: AppDeepLinkLocations[] = ['globalSearch'];
return [
{
id: 'inventory',
title: inventoryTitle,
path: '/inventory',
visibleIn,
},
...(hostsEnabled
? [
{
id: 'hosts',
title: i18n.translate('xpack.infra.homePage.metricsHostsTabTitle', {
defaultMessage: 'Hosts',
}),
path: '/hosts',
visibleIn,
},
]
: []),
...(metricsExplorerEnabled
? [
{
id: 'metrics-explorer',
title: i18n.translate('xpack.infra.homePage.metricsExplorerTabTitle', {
defaultMessage: 'Metrics Explorer',
}),
path: '/explorer',
},
]
: []),
{
id: 'settings',
title: i18n.translate('xpack.infra.homePage.settingsTabTitle', {
defaultMessage: 'Settings',
}),
path: '/settings',
},
{
id: 'assetDetails',
title: '', // Internal deep link, not shown in the UI. Title is dynamically set in the app.
path: '/detail',
visibleIn: [],
},
];
};
core.application.register({
id: 'metrics',
title: i18n.translate('xpack.infra.metrics.pluginTitle', {
defaultMessage: 'Infrastructure',
}),
euiIconType: 'logoObservability',
order: 8200,
appRoute: '/app/metrics',
category: DEFAULT_APP_CATEGORIES.observability,
updater$: this.appUpdater$,
deepLinks: getInfraDeepLinks({
hostsEnabled: core.settings.client.get<boolean>(enableInfrastructureHostsView),
metricsExplorerEnabled: this.config.featureFlags.metricsExplorerEnabled,
}),
mount: async (params: AppMountParameters) => {
// mount callback should not use setup dependencies, get start dependencies instead
const [coreStart, plugins, pluginStart] = await core.getStartServices();
const { renderApp } = await import('./apps/metrics_app');
const isCloudEnv = !!pluginsSetup.cloud?.isCloudEnabled;
const isServerlessEnv = pluginsSetup.cloud?.isServerlessEnabled || this.isServerlessEnv;
return renderApp(
coreStart,
{ ...plugins, licenseManagement: pluginsSetup.licenseManagement },
pluginStart,
this.config,
params,
{
kibanaVersion: this.kibanaVersion,
isCloudEnv,
isServerlessEnv,
}
);
},
});
startDep$AndAccessibleFlag$.subscribe(
([_startServices, isInfrastructureHostsViewEnabled, _isLogsExplorerAccessible]: [
ApplicationStart,
boolean,
boolean
]) => {
this.appUpdater$.next(() => ({
deepLinks: getInfraDeepLinks({
hostsEnabled: isInfrastructureHostsViewEnabled,
metricsExplorerEnabled: this.config.featureFlags.metricsExplorerEnabled,
}),
}));
}
);
// Setup telemetry events
this.telemetry.setup({ analytics: core.analytics });
return {};
}
start(core: InfraClientCoreStart, plugins: InfraClientStartDeps) {
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();
if (isLogsStreamEnabled) {
plugins.uiActions.registerAction<EmbeddableApiContext>({
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 Discover session instead of using Log stream.',
}),
getIconType: () => 'logsApp',
isCompatible: async ({ embeddable }) => {
return apiCanAddNewPanel(embeddable);
},
execute: async ({ embeddable }) => {
if (!apiCanAddNewPanel(embeddable)) throw new IncompatibleActionError();
embeddable.addNewPanel<LogStreamSerializedState>(
{
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);
}
const startContract: InfraClientStartExports = {
inventoryViews,
metricsExplorerViews,
telemetry,
};
return startContract;
}
stop() {}
}
const getLogsNavigationEntries = ({
isLogsExplorerAccessible,
config,
routes,
}: {
isLogsExplorerAccessible: boolean;
config: InfraPublicConfig;
routes: LogsAppRoutes;
}) => {
const entries: NavigationEntry[] = [];
if (!config.featureFlags.logsUIEnabled) return entries;
if (isLogsExplorerAccessible) {
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 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,
path,
});