diff --git a/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts index 87318e6c95fe..06866afc65ca 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts @@ -152,6 +152,7 @@ export const applicationUsageSchema = { fleet: commonSchema, integrations: commonSchema, ingestManager: commonSchema, + inventory: commonSchema, lens: commonSchema, maps: commonSchema, ml: commonSchema, diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 37507c50e832..82e766601878 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -4194,6 +4194,137 @@ } } }, + "inventory": { + "properties": { + "appId": { + "type": "keyword", + "_meta": { + "description": "The application being tracked" + } + }, + "viewId": { + "type": "keyword", + "_meta": { + "description": "Always `main`" + } + }, + "clicks_total": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application since we started counting them" + } + }, + "clicks_7_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application over the last 7 days" + } + }, + "clicks_30_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application over the last 30 days" + } + }, + "clicks_90_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application over the last 90 days" + } + }, + "minutes_on_screen_total": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen since we started counting them." + } + }, + "minutes_on_screen_7_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen over the last 7 days" + } + }, + "minutes_on_screen_30_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen over the last 30 days" + } + }, + "minutes_on_screen_90_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen over the last 90 days" + } + }, + "views": { + "type": "array", + "items": { + "properties": { + "appId": { + "type": "keyword", + "_meta": { + "description": "The application being tracked" + } + }, + "viewId": { + "type": "keyword", + "_meta": { + "description": "The application view being tracked" + } + }, + "clicks_total": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application sub view since we started counting them" + } + }, + "clicks_7_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the active application sub view over the last 7 days" + } + }, + "clicks_30_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the active application sub view over the last 30 days" + } + }, + "clicks_90_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the active application sub view over the last 90 days" + } + }, + "minutes_on_screen_total": { + "type": "float", + "_meta": { + "description": "Minutes the application sub view is active and on-screen since we started counting them." + } + }, + "minutes_on_screen_7_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen active application sub view over the last 7 days" + } + }, + "minutes_on_screen_30_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen active application sub view over the last 30 days" + } + }, + "minutes_on_screen_90_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen active application sub view over the last 90 days" + } + } + } + } + } + } + }, "lens": { "properties": { "appId": { diff --git a/x-pack/plugins/observability_solution/apm/ftr_e2e/cypress/e2e/deep_links.cy.ts b/x-pack/plugins/observability_solution/apm/ftr_e2e/cypress/e2e/deep_links.cy.ts index e7c75652c7d0..32ff06d1b3c6 100644 --- a/x-pack/plugins/observability_solution/apm/ftr_e2e/cypress/e2e/deep_links.cy.ts +++ b/x-pack/plugins/observability_solution/apm/ftr_e2e/cypress/e2e/deep_links.cy.ts @@ -16,7 +16,7 @@ describe('APM deep links', () => { .type('APM', { force: true, delay: 100 }) .focus(); cy.contains('APM'); - cy.contains('APM / Services'); + cy.contains('APM / Service Inventory'); cy.contains('APM / Service groups'); cy.contains('APM / Traces'); cy.contains('APM / Service Map'); @@ -33,7 +33,7 @@ describe('APM deep links', () => { .should('be.visible') .type('APM', { force: true, delay: 100 }); // navigates to services page - cy.contains('APM / Services').click({ force: true }); + cy.contains('APM / Service Inventory').click({ force: true }); cy.url().should('include', '/apm/services'); cy.getByTestSubj('nav-search-input') diff --git a/x-pack/plugins/observability_solution/apm/public/plugin.ts b/x-pack/plugins/observability_solution/apm/public/plugin.ts index c429fdaeb9ec..f58b9d6d4096 100644 --- a/x-pack/plugins/observability_solution/apm/public/plugin.ts +++ b/x-pack/plugins/observability_solution/apm/public/plugin.ts @@ -156,7 +156,7 @@ const applicationsTitle = i18n.translate('xpack.apm.navigation.rootTitle', { }); const servicesTitle = i18n.translate('xpack.apm.navigation.servicesTitle', { - defaultMessage: 'Services', + defaultMessage: 'Service Inventory', }); const serviceGroupsTitle = i18n.translate('xpack.apm.navigation.serviceGroupsTitle', { diff --git a/x-pack/plugins/observability_solution/inventory/kibana.jsonc b/x-pack/plugins/observability_solution/inventory/kibana.jsonc index 524b59d37cc3..f60cf36183b2 100644 --- a/x-pack/plugins/observability_solution/inventory/kibana.jsonc +++ b/x-pack/plugins/observability_solution/inventory/kibana.jsonc @@ -12,13 +12,13 @@ "entityManager", "inference", "dataViews", + "share", + "features", "unifiedSearch", "data", "share" ], - "requiredBundles": [ - "kibanaReact" - ], + "requiredBundles": ["kibanaReact"], "optionalPlugins": [], "extraPublicDirs": [] } diff --git a/x-pack/plugins/observability_solution/inventory/public/components/inventory_page_template/index.tsx b/x-pack/plugins/observability_solution/inventory/public/components/inventory_page_template/index.tsx index c2734d664355..29a6ac31348b 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/inventory_page_template/index.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/inventory_page_template/index.tsx @@ -4,10 +4,10 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { EuiEmptyPrompt, EuiLoadingLogo } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiEmptyPrompt, EuiLoadingLogo } from '@elastic/eui'; +import { TechnicalPreviewBadge } from '@kbn/observability-shared-plugin/public'; import { useKibana } from '../../hooks/use_kibana'; import { SearchBar } from '../search_bar'; import { getEntityManagerEnablement } from './no_data_config'; @@ -16,11 +16,18 @@ import { Welcome } from '../entity_enablement/welcome_modal'; import { useInventoryAbortableAsync } from '../../hooks/use_inventory_abortable_async'; import { EmptyState } from '../empty_states/empty_state'; -const pageTitle = { - pageTitle: i18n.translate('xpack.inventory.inventoryPageHeaderLabel', { - defaultMessage: 'Inventory', - }), -}; +const pageTitle = ( + + + {i18n.translate('xpack.inventory.inventoryPageHeaderLabel', { + defaultMessage: 'Inventory', + })} + + + + + +); export function InventoryPageTemplate({ children }: { children: React.ReactNode }) { const { @@ -52,7 +59,11 @@ export function InventoryPageTemplate({ children }: { children: React.ReactNode if (isEnablementLoading || hasDataLoading) { return ( - + } /> ); @@ -60,12 +71,14 @@ export function InventoryPageTemplate({ children }: { children: React.ReactNode return ( {value.hasData ? ( diff --git a/x-pack/plugins/observability_solution/inventory/public/plugin.ts b/x-pack/plugins/observability_solution/inventory/public/plugin.ts index 6f05d4594d15..c02a57b45f69 100644 --- a/x-pack/plugins/observability_solution/inventory/public/plugin.ts +++ b/x-pack/plugins/observability_solution/inventory/public/plugin.ts @@ -12,6 +12,7 @@ import { DEFAULT_APP_CATEGORIES, Plugin, PluginInitializerContext, + AppStatus, } from '@kbn/core/public'; import { INVENTORY_APP_ID } from '@kbn/deeplinks-observability/constants'; import { i18n } from '@kbn/i18n'; @@ -49,33 +50,39 @@ export class InventoryPlugin pluginsSetup: InventorySetupDependencies ): InventoryPublicSetup { const inventoryAPIClient = createCallInventoryAPI(coreSetup); - this.telemetry.setup({ analytics: coreSetup.analytics }); - - pluginsSetup.observabilityShared.navigation.registerSections( - from(coreSetup.getStartServices()).pipe( - map(([coreStart, pluginsStart]) => { - return [ - { - label: '', - sortKey: 101, - entries: [ - { - label: i18n.translate('xpack.inventory.inventoryLinkTitle', { - defaultMessage: 'Inventory', - }), - app: INVENTORY_APP_ID, - path: '/', - matchPath(currentPath: string) { - return ['/', ''].some((testPath) => currentPath.startsWith(testPath)); - }, - }, - ], - }, - ]; - }) - ) + const isEntityCentricExperienceSettingEnabled = coreSetup.uiSettings.get( + 'observability:entityCentricExperience', + true ); + if (isEntityCentricExperienceSettingEnabled) { + pluginsSetup.observabilityShared.navigation.registerSections( + from(coreSetup.getStartServices()).pipe( + map(([coreStart, pluginsStart]) => { + return [ + { + label: '', + sortKey: 300, + entries: [ + { + label: i18n.translate('xpack.inventory.inventoryLinkTitle', { + defaultMessage: 'Inventory', + }), + app: INVENTORY_APP_ID, + path: '/', + matchPath(currentPath: string) { + return ['/', ''].some((testPath) => currentPath.startsWith(testPath)); + }, + isTechnicalPreview: true, + }, + ], + }, + ]; + }) + ) + ); + } + this.telemetry.setup({ analytics: coreSetup.analytics }); const telemetry = this.telemetry.start(); coreSetup.application.register({ @@ -86,17 +93,11 @@ export class InventoryPlugin euiIconType: 'logoObservability', appRoute: '/app/observability/inventory', category: DEFAULT_APP_CATEGORIES.observability, - visibleIn: ['sideNav'], - order: 8001, - deepLinks: [ - { - id: 'inventory', - title: i18n.translate('xpack.inventory.inventoryDeepLinkTitle', { - defaultMessage: 'Inventory', - }), - path: '/', - }, - ], + visibleIn: ['sideNav', 'globalSearch'], + order: 8200, + status: isEntityCentricExperienceSettingEnabled + ? AppStatus.accessible + : AppStatus.inaccessible, mount: async (appMountParameters: AppMountParameters) => { // Load application bundle and Get start services const [{ renderApp }, [coreStart, pluginsStart]] = await Promise.all([ diff --git a/x-pack/plugins/observability_solution/inventory/server/config.ts b/x-pack/plugins/observability_solution/inventory/server/config.ts index 2d6d7604b40e..36f19bd6579a 100644 --- a/x-pack/plugins/observability_solution/inventory/server/config.ts +++ b/x-pack/plugins/observability_solution/inventory/server/config.ts @@ -8,7 +8,7 @@ import { schema, type TypeOf } from '@kbn/config-schema'; export const config = schema.object({ - enabled: schema.boolean({ defaultValue: false }), + enabled: schema.boolean({ defaultValue: true }), }); export type InventoryConfig = TypeOf; diff --git a/x-pack/plugins/observability_solution/inventory/server/feature.ts b/x-pack/plugins/observability_solution/inventory/server/feature.ts new file mode 100644 index 000000000000..2b3f2e92a44d --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/server/feature.ts @@ -0,0 +1,64 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server'; +import { KibanaFeatureScope } from '@kbn/features-plugin/common'; + +const INVENTORY_SERVER_FEATURE_ID = 'inventory'; + +export const INVENTORY_SERVER_FEATURE = { + id: INVENTORY_SERVER_FEATURE_ID, + name: i18n.translate('xpack.inventory.featureRegistry.inventoryFeatureName', { + defaultMessage: 'Inventory', + }), + order: 1000, + category: DEFAULT_APP_CATEGORIES.observability, + scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security], + app: [INVENTORY_SERVER_FEATURE_ID], + catalogue: [INVENTORY_SERVER_FEATURE_ID], + alerting: [], + // see x-pack/plugins/features/common/feature_kibana_privileges.ts + privileges: { + all: { + app: [INVENTORY_SERVER_FEATURE_ID], + api: [INVENTORY_SERVER_FEATURE_ID], + catalogue: [INVENTORY_SERVER_FEATURE_ID], + savedObject: { + all: [], + read: [], + }, + alerting: { + alert: { + all: [], + }, + rule: { + all: [], + }, + }, + ui: ['show', 'save'], + }, + read: { + app: [INVENTORY_SERVER_FEATURE_ID], + api: [INVENTORY_SERVER_FEATURE_ID], + catalogue: [INVENTORY_SERVER_FEATURE_ID], + savedObject: { + all: [], + read: [], + }, + alerting: { + alert: { + read: [], + }, + rule: { + read: [], + }, + }, + ui: ['show'], + }, + }, +}; diff --git a/x-pack/plugins/observability_solution/inventory/server/plugin.ts b/x-pack/plugins/observability_solution/inventory/server/plugin.ts index 1ac928b72cdb..642dd12769fb 100644 --- a/x-pack/plugins/observability_solution/inventory/server/plugin.ts +++ b/x-pack/plugins/observability_solution/inventory/server/plugin.ts @@ -17,6 +17,7 @@ import type { InventorySetupDependencies, InventoryStartDependencies, } from './types'; +import { INVENTORY_SERVER_FEATURE } from './feature'; export class InventoryPlugin implements @@ -55,6 +56,9 @@ export class InventoryPlugin }) as unknown as InventoryRouteHandlerResources['plugins'], }, }); + + pluginsSetup.features.registerKibanaFeature(INVENTORY_SERVER_FEATURE); + return {}; } diff --git a/x-pack/plugins/observability_solution/inventory/server/types.ts b/x-pack/plugins/observability_solution/inventory/server/types.ts index a58bd62fe57a..05f75561674c 100644 --- a/x-pack/plugins/observability_solution/inventory/server/types.ts +++ b/x-pack/plugins/observability_solution/inventory/server/types.ts @@ -13,6 +13,7 @@ import type { DataViewsServerPluginSetup, DataViewsServerPluginStart, } from '@kbn/data-views-plugin/server'; +import { FeaturesPluginSetup } from '@kbn/features-plugin/server'; /* eslint-disable @typescript-eslint/no-empty-interface*/ export interface ConfigSchema {} @@ -21,6 +22,7 @@ export interface InventorySetupDependencies { entityManager: EntityManagerServerPluginSetup; inference: InferenceServerSetup; dataViews: DataViewsServerPluginSetup; + features: FeaturesPluginSetup; } export interface InventoryStartDependencies { diff --git a/x-pack/plugins/observability_solution/inventory/tsconfig.json b/x-pack/plugins/observability_solution/inventory/tsconfig.json index c4b6b55d41f4..163e87108bf7 100644 --- a/x-pack/plugins/observability_solution/inventory/tsconfig.json +++ b/x-pack/plugins/observability_solution/inventory/tsconfig.json @@ -16,7 +16,6 @@ "kbn_references": [ "@kbn/core", "@kbn/logging", - "@kbn/config-schema", "@kbn/observability-shared-plugin", "@kbn/server-route-repository", "@kbn/shared-ux-link-redirect-app", @@ -40,7 +39,9 @@ "@kbn/data-plugin", "@kbn/core-analytics-browser", "@kbn/core-http-browser", + "@kbn/shared-svg", + "@kbn/features-plugin", "@kbn/es-query", - "@kbn/shared-svg" + "@kbn/config-schema" ] } diff --git a/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts b/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts index 309d2f226430..353f1258fc7b 100644 --- a/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts +++ b/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts @@ -67,9 +67,6 @@ export function createNavTree(pluginsStart: ObservabilityPublicPluginsStart) { { link: 'slo', }, - { - link: 'inventory', - }, { id: 'aiMl', title: i18n.translate('xpack.observability.obltNav.ml.aiAndMlGroupTitle', { @@ -132,6 +129,10 @@ export function createNavTree(pluginsStart: ObservabilityPublicPluginsStart) { }, ], }, + { + link: 'inventory', + spaceBefore: 'm', + }, { id: 'apm', title: i18n.translate('xpack.observability.obltNav.applications', { diff --git a/x-pack/plugins/serverless_observability/public/navigation_tree.ts b/x-pack/plugins/serverless_observability/public/navigation_tree.ts index 135c687655e2..e3c61ec0b29e 100644 --- a/x-pack/plugins/serverless_observability/public/navigation_tree.ts +++ b/x-pack/plugins/serverless_observability/public/navigation_tree.ts @@ -121,6 +121,7 @@ export const navigationTree: NavigationTreeDefinition = { }, ], }, + { link: 'inventory', spaceBefore: 'm' }, { id: 'apm', title: i18n.translate('xpack.serverlessObservability.nav.applications', { diff --git a/x-pack/test/api_integration/apis/features/features/features.ts b/x-pack/test/api_integration/apis/features/features/features.ts index d6602c0ce8cb..895bfcb851bd 100644 --- a/x-pack/test/api_integration/apis/features/features/features.ts +++ b/x-pack/test/api_integration/apis/features/features/features.ts @@ -120,6 +120,7 @@ export default function ({ getService }: FtrProviderContext) { 'canvas', 'generalCases', 'infrastructure', + 'inventory', 'logs', 'maintenanceWindow', 'maps', @@ -168,6 +169,7 @@ export default function ({ getService }: FtrProviderContext) { 'canvas', 'generalCases', 'infrastructure', + 'inventory', 'logs', 'maintenanceWindow', 'maps', diff --git a/x-pack/test/api_integration/apis/security/privileges.ts b/x-pack/test/api_integration/apis/security/privileges.ts index a809d0837abc..8db8c08b1672 100644 --- a/x-pack/test/api_integration/apis/security/privileges.ts +++ b/x-pack/test/api_integration/apis/security/privileges.ts @@ -140,6 +140,7 @@ export default function ({ getService }: FtrProviderContext) { maintenanceWindow: ['all', 'read', 'minimal_all', 'minimal_read'], guidedOnboardingFeature: ['all', 'read', 'minimal_all', 'minimal_read'], aiAssistantManagementSelection: ['all', 'read', 'minimal_all', 'minimal_read'], + inventory: ['all', 'read', 'minimal_all', 'minimal_read'], }, reserved: ['fleet-setup', 'ml_user', 'ml_admin', 'ml_apm_user', 'monitoring'], }; diff --git a/x-pack/test/api_integration/apis/security/privileges_basic.ts b/x-pack/test/api_integration/apis/security/privileges_basic.ts index 59829c673e40..a82ecc2aa4fd 100644 --- a/x-pack/test/api_integration/apis/security/privileges_basic.ts +++ b/x-pack/test/api_integration/apis/security/privileges_basic.ts @@ -58,6 +58,7 @@ export default function ({ getService }: FtrProviderContext) { maintenanceWindow: ['all', 'read', 'minimal_all', 'minimal_read'], guidedOnboardingFeature: ['all', 'read', 'minimal_all', 'minimal_read'], aiAssistantManagementSelection: ['all', 'read', 'minimal_all', 'minimal_read'], + inventory: ['all', 'read', 'minimal_all', 'minimal_read'], }, global: ['all', 'read'], space: ['all', 'read'], @@ -226,6 +227,7 @@ export default function ({ getService }: FtrProviderContext) { maintenanceWindow: ['all', 'read', 'minimal_all', 'minimal_read'], guidedOnboardingFeature: ['all', 'read', 'minimal_all', 'minimal_read'], aiAssistantManagementSelection: ['all', 'read', 'minimal_all', 'minimal_read'], + inventory: ['all', 'read', 'minimal_all', 'minimal_read'], }, reserved: ['fleet-setup', 'ml_user', 'ml_admin', 'ml_apm_user', 'monitoring'], }; diff --git a/x-pack/test/spaces_api_integration/spaces_only/telemetry/telemetry.ts b/x-pack/test/spaces_api_integration/spaces_only/telemetry/telemetry.ts index d39c58bd2b6e..98b3c8835459 100644 --- a/x-pack/test/spaces_api_integration/spaces_only/telemetry/telemetry.ts +++ b/x-pack/test/spaces_api_integration/spaces_only/telemetry/telemetry.ts @@ -75,6 +75,7 @@ export default function ({ getService }: FtrProviderContext) { uptime: 0, slo: 0, infrastructure: 0, + inventory: 0, logs: 0, monitoring: 0, apm: 0,